From 7a77d366829c62569132a58f88da752c3f679f16 Mon Sep 17 00:00:00 2001 From: Mihai Popescu Date: Wed, 10 Dec 2025 12:05:16 +0000 Subject: [PATCH 01/13] fix(webapp) : fixed Cmd+Left Arrow was intercepted by the TreeView component (#2772) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed the issue where Cmd+Left Arrow was being intercepted by the TreeView component on task runs screen. Solution: Added a check in the getTreeProps keyboard handler to detect when metaKey (Cmd on macOS) is pressed with Left Arrow. When detected, the handler returns early without preventing the default browser behavior, allowing Chrome's native back navigation to work. Closes # ## ✅ Checklist - [ x] I have followed every step in the [contributing guide](https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md) - [ x] The PR title follows the convention. - [ x] I ran and tested the code works --- ## Testing Went to the task details page and confirmed that Cmd + Left Arrow will navigate back --- ## Changelog Modified TreeView.tsx to check for e.metaKey before handling Left Arrow key events When Cmd+Left is pressed, the event is no longer prevented, allowing browser default behavior --- Co-authored-by: Mihai Popescu --- apps/webapp/app/components/primitives/TreeView/TreeView.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/webapp/app/components/primitives/TreeView/TreeView.tsx b/apps/webapp/app/components/primitives/TreeView/TreeView.tsx index 7a2e368bec..bb9ca4c462 100644 --- a/apps/webapp/app/components/primitives/TreeView/TreeView.tsx +++ b/apps/webapp/app/components/primitives/TreeView/TreeView.tsx @@ -423,6 +423,10 @@ export function useTree({ } case "Left": case "ArrowLeft": { + if (e.metaKey) { + return; + } + e.preventDefault(); const selected = selectedIdFromState(state.nodes); From a00ed929a7613657085563ec16c54197801c4cc8 Mon Sep 17 00:00:00 2001 From: nicktrn <55853254+nicktrn@users.noreply.github.com> Date: Wed, 10 Dec 2025 14:49:44 +0000 Subject: [PATCH 02/13] feat(cli): enable zstd compression for deployment images (#2773) This will speed up ice cold starts (*) for two reasons: - better compression ratio - faster decompression This is a minor release because zstd compression will now be enabled by default for all deployments. (*) ice cold starts happen when deploy images are not cached on the worker node yet. These cold start durations are highly dependent on image size and as it turns out, also the type of compression used. --- .changeset/great-pillows-look.md | 5 ++ packages/cli-v3/src/commands/deploy.ts | 46 ++++++++++- packages/cli-v3/src/deploy/buildImage.ts | 99 +++++++++++++++++++++++- 3 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 .changeset/great-pillows-look.md diff --git a/.changeset/great-pillows-look.md b/.changeset/great-pillows-look.md new file mode 100644 index 0000000000..72a0589b26 --- /dev/null +++ b/.changeset/great-pillows-look.md @@ -0,0 +1,5 @@ +--- +"trigger.dev": minor +--- + +feat(cli): enable zstd compression for deployment images diff --git a/packages/cli-v3/src/commands/deploy.ts b/packages/cli-v3/src/commands/deploy.ts index 818051d68d..f4de03281c 100644 --- a/packages/cli-v3/src/commands/deploy.ts +++ b/packages/cli-v3/src/commands/deploy.ts @@ -71,7 +71,7 @@ const DeployCommandOptions = CommonCommandOptions.extend({ saveLogs: z.boolean().default(false), skipUpdateCheck: z.boolean().default(false), skipPromotion: z.boolean().default(false), - noCache: z.boolean().default(false), + cache: z.boolean().default(true), envFile: z.string().optional(), // Local build options forceLocalBuild: z.boolean().optional(), @@ -83,6 +83,10 @@ const DeployCommandOptions = CommonCommandOptions.extend({ nativeBuildServer: z.boolean().default(false), detach: z.boolean().default(false), plain: z.boolean().default(false), + compression: z.enum(["zstd", "gzip"]).default("zstd"), + cacheCompression: z.enum(["zstd", "gzip"]).default("zstd"), + compressionLevel: z.number().optional(), + forceCompression: z.boolean().default(true), }); type DeployCommandOptions = z.infer; @@ -157,6 +161,40 @@ export function configureDeployCommand(program: Command) { "If provided, will save logs even for successful builds" ).hideHelp() ) + .addOption( + new CommandOption( + "--compression ", + "Compression algorithm for image layers: zstd or gzip (default: zstd)" + ) + .choices(["zstd", "gzip"]) + .hideHelp() + ) + .addOption( + new CommandOption( + "--cache-compression ", + "Compression algorithm for build cache: zstd or gzip (default: zstd)" + ) + .choices(["zstd", "gzip"]) + .hideHelp() + ) + .addOption( + new CommandOption( + "--compression-level ", + "The compression level to use when building the image." + ).hideHelp() + ) + .addOption( + new CommandOption( + "--force-compression", + "Force recompression of all layers. Enabled by default when using zstd." + ).hideHelp() + ) + .addOption( + new CommandOption( + "--no-force-compression", + "Disable forced recompression of layers." + ).hideHelp() + ) // Local build options .addOption( new CommandOption("--force-local-build", "Deprecated alias for --local-build").implies({ @@ -480,7 +518,7 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) { const buildResult = await buildImage({ isLocalBuild, useRegistryCache: options.useRegistryCache, - noCache: options.noCache, + noCache: !options.cache, deploymentId: deployment.id, deploymentVersion: deployment.version, imageTag: deployment.imageTag, @@ -499,6 +537,10 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) { authAccessToken: authorization.auth.accessToken, compilationPath: destination.path, buildEnvVars: buildManifest.build.env, + compression: options.compression, + cacheCompression: options.cacheCompression, + compressionLevel: options.compressionLevel, + forceCompression: options.forceCompression, onLog: (logMessage) => { if (options.plain || isCI) { console.log(logMessage); diff --git a/packages/cli-v3/src/deploy/buildImage.ts b/packages/cli-v3/src/deploy/buildImage.ts index e325b58d8f..d6957bf973 100644 --- a/packages/cli-v3/src/deploy/buildImage.ts +++ b/packages/cli-v3/src/deploy/buildImage.ts @@ -20,6 +20,10 @@ export interface BuildImageOptions { imagePlatform: string; noCache?: boolean; load?: boolean; + compression?: "zstd" | "gzip"; + cacheCompression?: "zstd" | "gzip"; + compressionLevel?: number; + forceCompression?: boolean; // Local build options push?: boolean; @@ -79,6 +83,10 @@ export async function buildImage(options: BuildImageOptions): Promise; + compression?: "zstd" | "gzip"; + compressionLevel?: number; + forceCompression?: boolean; onLog?: (log: string) => void; } @@ -180,6 +198,14 @@ async function remoteBuildImage(options: DepotBuildImageOptions): Promise value) .flatMap(([key, value]) => ["--build-arg", `${key}=${value}`]); + const outputOptions = getOutputOptions({ + imageTag: undefined, // This is already handled via the --save flag + push: true, // We always push the image to the registry + compression: options.compression, + compressionLevel: options.compressionLevel, + forceCompression: options.forceCompression, + }); + const args = [ "build", "-f", @@ -214,6 +240,8 @@ async function remoteBuildImage(options: DepotBuildImageOptions): Promise void; } async function localBuildImage(options: SelfHostedBuildImageOptions): Promise { - const { builder, imageTag, deploymentId, apiClient, useRegistryCache } = options; + const { + builder, + imageTag, + deploymentId, + apiClient, + useRegistryCache, + compression, + cacheCompression, + compressionLevel, + forceCompression, + } = options; // Ensure multi-platform build is supported on the local machine let builderExists = false; @@ -489,6 +531,14 @@ async function localBuildImage(options: SelfHostedBuildImageOptions): Promise Date: Wed, 10 Dec 2025 15:52:32 +0100 Subject: [PATCH 03/13] docs(kubernetes): use v4 helm chart instead of beta (#2671) --- docs/self-hosting/kubernetes.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/self-hosting/kubernetes.mdx b/docs/self-hosting/kubernetes.mdx index f3a827701d..4506d6da9b 100644 --- a/docs/self-hosting/kubernetes.mdx +++ b/docs/self-hosting/kubernetes.mdx @@ -61,7 +61,7 @@ webapp: ```bash helm upgrade -n trigger --install trigger \ oci://ghcr.io/triggerdotdev/charts/trigger \ - --version "~4.0.0-beta" \ + --version "~4.0.0" \ --create-namespace ``` @@ -107,11 +107,11 @@ The following commands will display the default values: ```bash # Specific version helm show values oci://ghcr.io/triggerdotdev/charts/trigger \ - --version "4.0.0-beta.5" + --version "4.0.5" -# Latest v4 beta +# Latest v4 helm show values oci://ghcr.io/triggerdotdev/charts/trigger \ - --version "~4.0.0-beta" + --version "~4.0.0" ``` ### Custom values @@ -171,7 +171,7 @@ Deploy with your custom values: ```bash helm upgrade -n trigger --install trigger \ oci://ghcr.io/triggerdotdev/charts/trigger \ - --version "~4.0.0-beta" \ + --version "~4.0.0" \ --create-namespace \ -f values-custom.yaml ``` @@ -489,14 +489,14 @@ You can lock versions in two ways: # Pin to a specific version for production helm upgrade -n trigger --install trigger \ oci://ghcr.io/triggerdotdev/charts/trigger \ - --version "4.0.0-beta.5" + --version "4.0.5" # The app version will be different from the chart version # This is the version of the Trigger.dev webapp and supervisor # ..and should always match your Trigger.dev CLI version helm show chart \ oci://ghcr.io/triggerdotdev/charts/trigger \ - --version "4.0.0-beta.5" | grep appVersion + --version "4.0.5" | grep appVersion ``` **Specific image tags:** From df9ecb38662ef8e53e21e233946b36998db04054 Mon Sep 17 00:00:00 2001 From: Oskar Otwinowski Date: Wed, 10 Dec 2025 17:03:16 +0000 Subject: [PATCH 04/13] feat(webapp): add GitHub onboarding flow to empty Tasks and Deployments pages (#2775) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## ✅ Checklist - [x] I have followed every step in the [contributing guide](https://github.com/triggerdotdev/trigger.dev/blob/main/CONTRIBUTING.md) - [x] The PR title follows the convention. - [x] I ran and tested the code works --- ## Testing Set up the local github application, and tested its connection with trigger.dev Checked: - Change is backwards compatible - Actions/ux are uniform across the pages (empty Tasks, Deployments, project settings) - Connecting GH, Connecting Repo, disconnecting Repo, modifying settings --- ## Changelog - Create new resource route for GitHub settings management with loader and actions - Add GitHubSettingsPresenter to fetch connected repos and installations - Implement GitHubSettingsPanel component for reusable GitHub configuration UI - Refactor project settings page to use shared GitHubSettingsPanel component - Integrate GitHub connection flow into empty state onboarding for Tasks and Deployments - Add support for GitHub repo connection, disconnection, and branch tracking settings - Include redirect URL support for seamless navigation after GitHub actions - Remove duplicate GitHub connection code from project settings route --- ## Screenshots https://github.com/user-attachments/assets/8fc24699-640b-4f9e-afd8-b26edc945218 🐐 --------- Co-authored-by: James Ritchie --- .../app/components/BlankStatePanels.tsx | 280 +++--- .../app/components/primitives/ClientTabs.tsx | 224 +++-- .../webapp/app/components/primitives/Tabs.tsx | 130 ++- .../v3/GitHubSettingsPresenter.server.ts | 137 +++ .../route.tsx | 4 +- .../route.tsx | 678 +------------- ...cts.$projectParam.env.$envParam.github.tsx | 877 ++++++++++++++++++ .../storybook.tabs.$tabNumber/route.tsx | 2 +- .../app/routes/storybook.tabs/route.tsx | 190 +++- 9 files changed, 1638 insertions(+), 884 deletions(-) create mode 100644 apps/webapp/app/presenters/v3/GitHubSettingsPresenter.server.ts create mode 100644 apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx diff --git a/apps/webapp/app/components/BlankStatePanels.tsx b/apps/webapp/app/components/BlankStatePanels.tsx index 380a6d990c..d0e798f168 100644 --- a/apps/webapp/app/components/BlankStatePanels.tsx +++ b/apps/webapp/app/components/BlankStatePanels.tsx @@ -52,6 +52,13 @@ import { } from "./SetupCommands"; import { StepContentContainer } from "./StepContentContainer"; import { V4Badge } from "./V4Badge"; +import { + ClientTabs, + ClientTabsContent, + ClientTabsList, + ClientTabsTrigger, +} from "./primitives/ClientTabs"; +import { GitHubSettingsPanel } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github"; export function HasNoTasksDev() { return ( @@ -93,62 +100,7 @@ export function HasNoTasksDev() { } export function HasNoTasksDeployed({ environment }: { environment: MinimumEnvironment }) { - return ( - -
-
-
- - 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. - -
-
- ); + return ; } export function SchedulesNoPossibleTaskPanel() { @@ -266,45 +218,7 @@ export function TestHasNoTasks() { } export function DeploymentsNone() { - const organization = useOrganization(); - const project = useProject(); - const environment = useEnvironment(); - - return ( - - - There are several ways to deploy your tasks. You can use the CLI or a Continuous Integration - service like GitHub Actions. Make sure you{" "} - - set your environment variables - {" "} - first. - -
- - Deploy with the CLI - - - Deploy with GitHub actions - -
-
- ); + return ; } export function DeploymentsNoneDev() { @@ -313,46 +227,52 @@ export function DeploymentsNoneDev() { const environment = useEnvironment(); return ( -
- - + <> +
+
+ + Deploy your tasks +
+
+ + } + content="Deploy docs" + /> + + } + content="Troubleshooting docs" + /> + +
+
+ + + This is the Development environment. When you're ready to deploy your tasks, switch to a different environment. - - There are several ways to deploy your tasks. You can use the CLI or a Continuous - Integration service like GitHub Actions. Make sure you{" "} - - set your environment variables - {" "} - first. - -
- - Deploy with the CLI - - - Deploy with GitHub actions - -
-
- -
+ + + ); } @@ -670,3 +590,99 @@ export function BulkActionsNone() { ); } + +function DeploymentOnboardingSteps() { + const environment = useEnvironment(); + const organization = useOrganization(); + const project = useProject(); + + return ( + +
+
+ + Deploy your tasks to {environmentFullTitle(environment)} +
+
+ + } + content="Deploy docs" + /> + + } + content="Troubleshooting docs" + /> + +
+
+ + + + GitHub + + + Manual + + + GitHub Actions + + + + + + + Deploy automatically with every push. Read the{" "} + full guide. + +
+ +
+
+
+ + + + + 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/primitives/ClientTabs.tsx b/apps/webapp/app/components/primitives/ClientTabs.tsx index 52d10b8cfe..bc3943e82b 100644 --- a/apps/webapp/app/components/primitives/ClientTabs.tsx +++ b/apps/webapp/app/components/primitives/ClientTabs.tsx @@ -1,41 +1,185 @@ "use client"; -import * as React from "react"; +import { motion } from "framer-motion"; import * as TabsPrimitive from "@radix-ui/react-tabs"; +import * as React from "react"; import { cn } from "~/utils/cn"; -import { motion } from "framer-motion"; +import { type Variants } from "./Tabs"; + +type ClientTabsContextValue = { + value?: string; +}; + +const ClientTabsContext = React.createContext(undefined); + +function useClientTabsContext() { + return React.useContext(ClientTabsContext); +} const ClientTabs = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->((props, ref) => ); +>(({ onValueChange, value: valueProp, defaultValue, ...props }, ref) => { + const [value, setValue] = React.useState(valueProp ?? defaultValue); + + React.useEffect(() => { + if (valueProp !== undefined) { + setValue(valueProp); + } + }, [valueProp]); + + const handleValueChange = React.useCallback( + (nextValue: string) => { + if (valueProp === undefined) { + setValue(nextValue); + } + onValueChange?.(nextValue); + }, + [onValueChange, valueProp] + ); + + const controlledProps = + valueProp !== undefined + ? { value: valueProp } + : defaultValue !== undefined + ? { defaultValue } + : {}; + + const contextValue = React.useMemo(() => ({ value }), [value]); + + return ( + + + + ); +}); ClientTabs.displayName = TabsPrimitive.Root.displayName; const ClientTabsList = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); + React.ComponentPropsWithoutRef & { + variant?: Variants; + } +>(({ className, variant = "pipe-divider", ...props }, ref) => { + const variantClassName = (() => { + switch (variant) { + case "segmented": + return "relative flex h-10 w-full items-center rounded bg-charcoal-700/50 p-1"; + case "underline": + return "flex gap-x-6 border-b border-grid-bright"; + default: + return "inline-flex items-center justify-center transition duration-100"; + } + })(); + + return ; +}); ClientTabsList.displayName = TabsPrimitive.List.displayName; const ClientTabsTrigger = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); + React.ComponentPropsWithoutRef & { + variant?: Variants; + layoutId?: string; + } +>(({ className, variant = "pipe-divider", layoutId, children, ...props }, ref) => { + const context = useClientTabsContext(); + const activeValue = context?.value; + const isActive = activeValue === props.value; + + if (variant === "segmented") { + return ( + +
+ + {children} + +
+ {isActive ? ( + layoutId ? ( + + ) : ( +
+ ) + ) : null} + + ); + } + + if (variant === "underline") { + return ( + + + {children} + + {layoutId ? ( + isActive ? ( + + ) : ( +
+ ) + ) : isActive ? ( +
+ ) : ( +
+ )} + + ); + } + + return ( + + {children} + + ); +}); ClientTabsTrigger.displayName = TabsPrimitive.Trigger.displayName; const ClientTabsContent = React.forwardRef< @@ -61,39 +205,7 @@ export type TabsProps = { currentValue: string; className?: string; layoutId: string; + variant?: Variants; }; -export function ClientTabsWithUnderline({ className, tabs, currentValue, layoutId }: TabsProps) { - return ( - - {tabs.map((tab, index) => { - const isActive = currentValue === tab.value; - return ( - - - {tab.label} - - {isActive ? ( - - ) : ( -
- )} - - ); - })} - - ); -} - -export { ClientTabs, ClientTabsList, ClientTabsTrigger, ClientTabsContent }; +export { ClientTabs, ClientTabsContent, ClientTabsList, ClientTabsTrigger }; diff --git a/apps/webapp/app/components/primitives/Tabs.tsx b/apps/webapp/app/components/primitives/Tabs.tsx index e3d3183d94..cbc5cf4275 100644 --- a/apps/webapp/app/components/primitives/Tabs.tsx +++ b/apps/webapp/app/components/primitives/Tabs.tsx @@ -1,10 +1,12 @@ import { NavLink } from "@remix-run/react"; import { motion } from "framer-motion"; -import { ReactNode, useRef } from "react"; -import { ShortcutDefinition, useShortcutKeys } from "~/hooks/useShortcutKeys"; +import { type ReactNode, useRef } from "react"; +import { type ShortcutDefinition, useShortcutKeys } from "~/hooks/useShortcutKeys"; import { cn } from "~/utils/cn"; import { ShortcutKey } from "./ShortcutKey"; +export type Variants = "underline" | "pipe-divider" | "segmented"; + export type TabsProps = { tabs: { label: string; @@ -12,13 +14,14 @@ export type TabsProps = { }[]; className?: string; layoutId: string; + variant?: Variants; }; -export function Tabs({ tabs, className, layoutId }: TabsProps) { +export function Tabs({ tabs, className, layoutId, variant = "underline" }: TabsProps) { return ( - + {tabs.map((tab, index) => ( - + {tab.label} ))} @@ -26,23 +29,107 @@ export function Tabs({ tabs, className, layoutId }: TabsProps) { ); } -export function TabContainer({ children, className }: { children: ReactNode; className?: string }) { - return ( -
- {children} -
- ); +export function TabContainer({ + children, + className, + variant = "underline", +}: { + children: ReactNode; + className?: string; + variant?: Variants; +}) { + if (variant === "segmented") { + return ( +
+ {children} +
+ ); + } + + if (variant === "underline") { + return ( +
{children}
+ ); + } + + return
{children}
; } export function TabLink({ to, children, layoutId, + variant = "underline", }: { to: string; children: ReactNode; layoutId: string; + variant?: Variants; }) { + if (variant === "segmented") { + return ( + + {({ isActive, isPending }) => { + const active = isActive || isPending; + return ( + <> +
+ + {children} + +
+ {active && ( + + )} + + ); + }} +
+ ); + } + + if (variant === "pipe-divider") { + return ( + + {({ isActive, isPending }) => { + const active = isActive || isPending; + return ( + + {children} + + ); + }} + + ); + } + + // underline variant (default) return ( {({ isActive, isPending }) => { @@ -51,13 +138,19 @@ export function TabLink({ {children} {isActive || isPending ? ( - + ) : (
)} @@ -106,17 +199,18 @@ export function TabButton({ <>
{props.children} {shortcut && }
{isActive ? ( - + ) : (
)} diff --git a/apps/webapp/app/presenters/v3/GitHubSettingsPresenter.server.ts b/apps/webapp/app/presenters/v3/GitHubSettingsPresenter.server.ts new file mode 100644 index 0000000000..c3f715deff --- /dev/null +++ b/apps/webapp/app/presenters/v3/GitHubSettingsPresenter.server.ts @@ -0,0 +1,137 @@ +import { type PrismaClient } from "@trigger.dev/database"; +import { err, fromPromise, ok, ResultAsync } from "neverthrow"; +import { env } from "~/env.server"; +import { BranchTrackingConfigSchema } from "~/v3/github"; +import { BasePresenter } from "./basePresenter.server"; + +type GitHubSettingsOptions = { + projectId: string; + organizationId: string; +}; + +export class GitHubSettingsPresenter extends BasePresenter { + public call({ projectId, organizationId }: GitHubSettingsOptions) { + const githubAppEnabled = env.GITHUB_APP_ENABLED === "1"; + + if (!githubAppEnabled) { + return ok({ + enabled: false, + connectedRepository: undefined, + installations: undefined, + isPreviewEnvironmentEnabled: undefined, + }); + } + + const findConnectedGithubRepository = () => + fromPromise( + (this._replica as PrismaClient).connectedGithubRepository.findFirst({ + where: { + projectId, + repository: { + installation: { + deletedAt: null, + suspendedAt: null, + }, + }, + }, + select: { + branchTracking: true, + previewDeploymentsEnabled: true, + createdAt: true, + repository: { + select: { + id: true, + name: true, + fullName: true, + htmlUrl: true, + private: true, + }, + }, + }, + }), + (error) => ({ + type: "other" as const, + cause: error, + }) + ).map((connectedGithubRepository) => { + if (!connectedGithubRepository) { + return undefined; + } + + const branchTrackingOrFailure = BranchTrackingConfigSchema.safeParse( + connectedGithubRepository.branchTracking + ); + const branchTracking = branchTrackingOrFailure.success + ? branchTrackingOrFailure.data + : undefined; + + return { + ...connectedGithubRepository, + branchTracking, + }; + }); + + const listGithubAppInstallations = () => + fromPromise( + (this._replica as PrismaClient).githubAppInstallation.findMany({ + where: { + organizationId, + deletedAt: null, + suspendedAt: null, + }, + select: { + id: true, + accountHandle: true, + targetType: true, + appInstallationId: true, + repositories: { + select: { + id: true, + name: true, + fullName: true, + htmlUrl: true, + private: true, + }, + take: 200, + }, + }, + take: 20, + orderBy: { + createdAt: "desc", + }, + }), + (error) => ({ + type: "other" as const, + cause: error, + }) + ); + + const isPreviewEnvironmentEnabled = () => + fromPromise( + (this._replica as PrismaClient).runtimeEnvironment.findFirst({ + select: { + id: true, + }, + where: { + projectId: projectId, + slug: "preview", + }, + }), + (error) => ({ + type: "other" as const, + cause: error, + }) + ).map((previewEnvironment) => previewEnvironment !== null); + + return ResultAsync.combine([ + isPreviewEnvironmentEnabled(), + findConnectedGithubRepository(), + listGithubAppInstallations(), + ]).map(([isPreviewEnvironmentEnabled, connectedGithubRepository, githubAppInstallations]) => ({ + enabled: true, + connectedRepository: connectedGithubRepository, + installations: githubAppInstallations, + isPreviewEnvironmentEnabled, + })); + } +} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx index 6f161eea98..9b2b78f98b 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx @@ -359,11 +359,11 @@ export default function Page() {
) : environment.type === "DEVELOPMENT" ? ( - + ) : ( - + )} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx index 06b6f6ad8a..66ea64cb36 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings/route.tsx @@ -1,35 +1,18 @@ import { conform, useForm } from "@conform-to/react"; import { parse } from "@conform-to/zod"; -import { - CheckCircleIcon, - ExclamationTriangleIcon, - FolderIcon, - TrashIcon, - LockClosedIcon, - PlusIcon, -} from "@heroicons/react/20/solid"; -import { - Form, - type MetaFunction, - useActionData, - useNavigation, - useNavigate, - useSearchParams, -} from "@remix-run/react"; +import { ExclamationTriangleIcon, FolderIcon, TrashIcon } from "@heroicons/react/20/solid"; +import { Form, type MetaFunction, useActionData, useNavigation } from "@remix-run/react"; import { type ActionFunction, type LoaderFunctionArgs, json } from "@remix-run/server-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { z } from "zod"; import { AdminDebugTooltip } from "~/components/admin/debugTooltip"; import { InlineCode } from "~/components/code/InlineCode"; -import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "~/components/primitives/Dialog"; -import { DialogClose } from "@radix-ui/react-dialog"; -import { OctoKitty } from "~/components/GitHubLoginButton"; import { MainHorizontallyCenteredContainer, PageBody, PageContainer, } from "~/components/layout/AppLayout"; -import { Button, LinkButton } from "~/components/primitives/Buttons"; +import { Button } from "~/components/primitives/Buttons"; import { CheckboxWithLabel } from "~/components/primitives/Checkbox"; import { ClipboardField } from "~/components/primitives/ClipboardField"; import { Fieldset } from "~/components/primitives/Fieldset"; @@ -55,32 +38,12 @@ import { import { ProjectSettingsService } from "~/services/projectSettings.server"; import { logger } from "~/services/logger.server"; import { requireUserId } from "~/services/session.server"; -import { - organizationPath, - v3ProjectPath, - githubAppInstallPath, - EnvironmentParamSchema, - v3ProjectSettingsPath, - docsPath, - v3BillingPath, -} from "~/utils/pathBuilder"; +import { organizationPath, v3ProjectPath, EnvironmentParamSchema, v3BillingPath } from "~/utils/pathBuilder"; import React, { useEffect, useState } from "react"; -import { Select, SelectItem } from "~/components/primitives/Select"; -import { Switch } from "~/components/primitives/Switch"; -import { type BranchTrackingConfig } from "~/v3/github"; -import { - EnvironmentIcon, - environmentFullTitle, - environmentTextClassName, -} from "~/components/environments/EnvironmentLabel"; -import { GitBranchIcon } from "lucide-react"; import { useEnvironment } from "~/hooks/useEnvironment"; -import { DateTime } from "~/components/primitives/DateTime"; -import { TextLink } from "~/components/primitives/TextLink"; -import { cn } from "~/utils/cn"; import { ProjectSettingsPresenter } from "~/services/projectSettingsPresenter.server"; import { type BuildSettings } from "~/v3/buildSettings"; -import { InfoIconTooltip } from "~/components/primitives/Tooltip"; +import { GitHubSettingsPanel } from "../resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github"; export const meta: MetaFunction = () => { return [ @@ -128,29 +91,10 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { return typedjson({ githubAppEnabled: gitHubApp.enabled, - githubAppInstallations: gitHubApp.installations, - connectedGithubRepository: gitHubApp.connectedRepository, - isPreviewEnvironmentEnabled: gitHubApp.isPreviewEnvironmentEnabled, buildSettings, }); }; -const ConnectGitHubRepoFormSchema = z.object({ - action: z.literal("connect-repo"), - installationId: z.string(), - repositoryId: z.string(), -}); - -const UpdateGitSettingsFormSchema = z.object({ - action: z.literal("update-git-settings"), - productionBranch: z.string().trim().optional(), - stagingBranch: z.string().trim().optional(), - previewDeploymentsEnabled: z - .string() - .optional() - .transform((val) => val === "on"), -}); - const UpdateBuildSettingsFormSchema = z.object({ action: z.literal("update-build-settings"), triggerConfigFilePath: z @@ -220,12 +164,7 @@ export function createSchema( } }), }), - ConnectGitHubRepoFormSchema, - UpdateGitSettingsFormSchema, UpdateBuildSettingsFormSchema, - z.object({ - action: z.literal("disconnect-repo"), - }), ]); } @@ -260,7 +199,7 @@ export const action: ActionFunction = async ({ request, params }) => { return json({ errors: { body: membershipResultOrFail.error.type } }, { status: 404 }); } - const { projectId, organizationId } = membershipResultOrFail.value; + const { projectId } = membershipResultOrFail.value; switch (submission.value.action) { case "rename": { @@ -316,101 +255,6 @@ export const action: ActionFunction = async ({ request, params }) => { "Project deleted" ); } - case "disconnect-repo": { - const resultOrFail = await projectSettingsService.disconnectGitHubRepo(projectId); - - if (resultOrFail.isErr()) { - switch (resultOrFail.error.type) { - case "other": - default: { - resultOrFail.error.type satisfies "other"; - - logger.error("Failed to disconnect GitHub repository", { - error: resultOrFail.error, - }); - return redirectBackWithErrorMessage(request, "Failed to disconnect GitHub repository"); - } - } - } - - return redirectBackWithSuccessMessage(request, "GitHub repository disconnected successfully"); - } - case "update-git-settings": { - const { productionBranch, stagingBranch, previewDeploymentsEnabled } = submission.value; - - const resultOrFail = await projectSettingsService.updateGitSettings( - projectId, - productionBranch, - stagingBranch, - previewDeploymentsEnabled - ); - - if (resultOrFail.isErr()) { - switch (resultOrFail.error.type) { - case "github_app_not_enabled": { - return redirectBackWithErrorMessage(request, "GitHub app is not enabled"); - } - case "connected_gh_repository_not_found": { - return redirectBackWithErrorMessage(request, "Connected GitHub repository not found"); - } - case "production_tracking_branch_not_found": { - return redirectBackWithErrorMessage(request, "Production tracking branch not found"); - } - case "staging_tracking_branch_not_found": { - return redirectBackWithErrorMessage(request, "Staging tracking branch not found"); - } - case "other": - default: { - resultOrFail.error.type satisfies "other"; - - logger.error("Failed to update Git settings", { - error: resultOrFail.error, - }); - return redirectBackWithErrorMessage(request, "Failed to update Git settings"); - } - } - } - - return redirectBackWithSuccessMessage(request, "Git settings updated successfully"); - } - case "connect-repo": { - const { repositoryId, installationId } = submission.value; - - const resultOrFail = await projectSettingsService.connectGitHubRepo( - projectId, - organizationId, - repositoryId, - installationId - ); - - if (resultOrFail.isErr()) { - switch (resultOrFail.error.type) { - case "gh_repository_not_found": { - return redirectBackWithErrorMessage(request, "GitHub repository not found"); - } - case "project_already_has_connected_repository": { - return redirectBackWithErrorMessage( - request, - "Project already has a connected repository" - ); - } - case "other": - default: { - resultOrFail.error.type satisfies "other"; - - logger.error("Failed to connect GitHub repository", { - error: resultOrFail.error, - }); - return redirectBackWithErrorMessage(request, "Failed to connect GitHub repository"); - } - } - } - - return json({ - ...submission, - success: true, - }); - } case "update-build-settings": { const { installCommand, preBuildCommand, triggerConfigFilePath, useNativeBuildServer } = submission.value; @@ -446,13 +290,7 @@ export const action: ActionFunction = async ({ request, params }) => { }; export default function Page() { - const { - githubAppInstallations, - connectedGithubRepository, - githubAppEnabled, - buildSettings, - isPreviewEnvironmentEnabled, - } = useTypedLoaderData(); + const { githubAppEnabled, buildSettings } = useTypedLoaderData(); const project = useProject(); const organization = useOrganization(); const environment = useEnvironment(); @@ -578,19 +416,12 @@ export default function Page() {
Git settings
- {connectedGithubRepository ? ( - - ) : ( - - )} +
@@ -650,489 +481,6 @@ export default function Page() { ); } -type GitHubRepository = { - id: string; - name: string; - fullName: string; - private: boolean; - htmlUrl: string; -}; - -type GitHubAppInstallation = { - id: string; - appInstallationId: bigint; - targetType: string; - accountHandle: string; - repositories: GitHubRepository[]; -}; - -function ConnectGitHubRepoModal({ - gitHubAppInstallations, - organizationSlug, - projectSlug, - environmentSlug, -}: { - gitHubAppInstallations: GitHubAppInstallation[]; - organizationSlug: string; - projectSlug: string; - environmentSlug: string; - open?: boolean; -}) { - const [isModalOpen, setIsModalOpen] = useState(false); - const lastSubmission = useActionData() as any; - const navigate = useNavigate(); - - const [selectedInstallation, setSelectedInstallation] = useState< - GitHubAppInstallation | undefined - >(gitHubAppInstallations.at(0)); - - const [selectedRepository, setSelectedRepository] = useState( - undefined - ); - - const navigation = useNavigation(); - const isConnectRepositoryLoading = - navigation.formData?.get("action") === "connect-repo" && - (navigation.state === "submitting" || navigation.state === "loading"); - - const [form, { installationId, repositoryId }] = useForm({ - id: "connect-repo", - lastSubmission: lastSubmission, - shouldRevalidate: "onSubmit", - onValidate({ formData }) { - return parse(formData, { - schema: ConnectGitHubRepoFormSchema, - }); - }, - }); - - const [searchParams, setSearchParams] = useSearchParams(); - useEffect(() => { - const params = new URLSearchParams(searchParams); - - if (params.get("openGithubRepoModal") === "1") { - setIsModalOpen(true); - params.delete("openGithubRepoModal"); - setSearchParams(params); - } - }, [searchParams, setSearchParams]); - - useEffect(() => { - if (lastSubmission && "success" in lastSubmission && lastSubmission.success === true) { - setIsModalOpen(false); - } - }, [lastSubmission]); - - return ( - - - - - - Connect GitHub repository -
-
- - Choose a GitHub repository to connect to your project. - -
- - - - {installationId.error} - - - - - - Configure repository access in{" "} - - GitHub - - . - - {repositoryId.error} - - {form.error} - - Connect repository - - } - cancelButton={ - - - - } - /> -
-
-
-
-
- ); -} - -function GitHubConnectionPrompt({ - gitHubAppInstallations, - organizationSlug, - projectSlug, - environmentSlug, -}: { - gitHubAppInstallations: GitHubAppInstallation[]; - organizationSlug: string; - projectSlug: string; - environmentSlug: string; -}) { - return ( -
- - {gitHubAppInstallations.length === 0 && ( - - Install GitHub app - - )} - {gitHubAppInstallations.length !== 0 && ( -
- - - GitHub app is installed - -
- )} - - Connect your GitHub repository to automatically deploy your changes. -
-
- ); -} - -type ConnectedGitHubRepo = { - branchTracking: BranchTrackingConfig | undefined; - previewDeploymentsEnabled: boolean; - createdAt: Date; - repository: GitHubRepository; -}; - -function ConnectedGitHubRepoForm({ - connectedGitHubRepo, - previewEnvironmentEnabled, -}: { - connectedGitHubRepo: ConnectedGitHubRepo; - previewEnvironmentEnabled?: boolean; -}) { - const lastSubmission = useActionData() as any; - const navigation = useNavigation(); - const organization = useOrganization(); - - const [hasGitSettingsChanges, setHasGitSettingsChanges] = useState(false); - const [gitSettingsValues, setGitSettingsValues] = useState({ - productionBranch: connectedGitHubRepo.branchTracking?.prod?.branch || "", - stagingBranch: connectedGitHubRepo.branchTracking?.staging?.branch || "", - previewDeploymentsEnabled: connectedGitHubRepo.previewDeploymentsEnabled, - }); - - useEffect(() => { - const hasChanges = - gitSettingsValues.productionBranch !== - (connectedGitHubRepo.branchTracking?.prod?.branch || "") || - gitSettingsValues.stagingBranch !== - (connectedGitHubRepo.branchTracking?.staging?.branch || "") || - gitSettingsValues.previewDeploymentsEnabled !== connectedGitHubRepo.previewDeploymentsEnabled; - setHasGitSettingsChanges(hasChanges); - }, [gitSettingsValues, connectedGitHubRepo]); - - const [gitSettingsForm, fields] = useForm({ - id: "update-git-settings", - lastSubmission: lastSubmission, - shouldRevalidate: "onSubmit", - onValidate({ formData }) { - return parse(formData, { - schema: UpdateGitSettingsFormSchema, - }); - }, - }); - - const isGitSettingsLoading = - navigation.formData?.get("action") === "update-git-settings" && - (navigation.state === "submitting" || navigation.state === "loading"); - - return ( - <> -
-
- - - {connectedGitHubRepo.repository.fullName} - - {connectedGitHubRepo.repository.private && ( - - )} - - - -
- - - - - - Disconnect GitHub repository -
- - Are you sure you want to disconnect{" "} - {connectedGitHubRepo.repository.fullName}? - This will stop automatic deployments from GitHub. - - - - - - } - cancelButton={ - - - - } - /> -
-
-
-
- -
-
- - - Every push to the selected tracking branch creates a deployment in the corresponding - environment. - -
-
- - - {environmentFullTitle({ type: "PRODUCTION" })} - -
- { - setGitSettingsValues((prev) => ({ - ...prev, - productionBranch: e.target.value, - })); - }} - /> -
- - - {environmentFullTitle({ type: "STAGING" })} - -
- { - setGitSettingsValues((prev) => ({ - ...prev, - stagingBranch: e.target.value, - })); - }} - /> - -
- - - {environmentFullTitle({ type: "PREVIEW" })} - -
-
- { - setGitSettingsValues((prev) => ({ - ...prev, - previewDeploymentsEnabled: checked, - })); - }} - /> - {!previewEnvironmentEnabled && ( - - Upgrade your plan to - enable preview branches - - } - /> - )} -
-
- {fields.productionBranch?.error} - {fields.stagingBranch?.error} - {fields.previewDeploymentsEnabled?.error} - {gitSettingsForm.error} -
- - - Save - - } - /> -
-
- - ); -} - function BuildSettingsForm({ buildSettings }: { buildSettings: BuildSettings }) { const lastSubmission = useActionData() as any; const navigation = useNavigation(); diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx new file mode 100644 index 0000000000..bb7406ed44 --- /dev/null +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx @@ -0,0 +1,877 @@ +import { conform, useForm } from "@conform-to/react"; +import { parse } from "@conform-to/zod"; +import { CheckCircleIcon, LockClosedIcon, PlusIcon } from "@heroicons/react/20/solid"; +import { Form, useActionData, useNavigation, useNavigate, useSearchParams, useLocation } from "@remix-run/react"; +import { type ActionFunctionArgs, type LoaderFunctionArgs, json } from "@remix-run/server-runtime"; +import { typedjson, useTypedFetcher } from "remix-typedjson"; +import { z } from "zod"; +import { OctoKitty } from "~/components/GitHubLoginButton"; +import { Dialog, DialogContent, DialogHeader, DialogTrigger } from "~/components/primitives/Dialog"; +import { DialogClose } from "@radix-ui/react-dialog"; +import { Button, LinkButton } from "~/components/primitives/Buttons"; +import { Fieldset } from "~/components/primitives/Fieldset"; +import { FormButtons } from "~/components/primitives/FormButtons"; +import { FormError } from "~/components/primitives/FormError"; +import { Hint } from "~/components/primitives/Hint"; +import { Input } from "~/components/primitives/Input"; +import { InputGroup } from "~/components/primitives/InputGroup"; +import { Label } from "~/components/primitives/Label"; +import { Paragraph } from "~/components/primitives/Paragraph"; +import { Select, SelectItem } from "~/components/primitives/Select"; +import { SpinnerWhite } from "~/components/primitives/Spinner"; +import { Switch } from "~/components/primitives/Switch"; +import { TextLink } from "~/components/primitives/TextLink"; +import { DateTime } from "~/components/primitives/DateTime"; +import { InfoIconTooltip } from "~/components/primitives/Tooltip"; +import { + EnvironmentIcon, + environmentFullTitle, + environmentTextClassName, +} from "~/components/environments/EnvironmentLabel"; +import { GitBranchIcon } from "lucide-react"; +import { + redirectBackWithErrorMessage, + redirectBackWithSuccessMessage, + redirectWithErrorMessage, + redirectWithSuccessMessage, +} from "~/models/message.server"; +import { findProjectBySlug } from "~/models/project.server"; +import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server"; +import { ProjectSettingsService } from "~/services/projectSettings.server"; +import { logger } from "~/services/logger.server"; +import { requireUserId } from "~/services/session.server"; +import { + githubAppInstallPath, + EnvironmentParamSchema, + v3ProjectSettingsPath, +} from "~/utils/pathBuilder"; +import { cn } from "~/utils/cn"; +import { type BranchTrackingConfig } from "~/v3/github"; +import { GitHubSettingsPresenter } from "~/presenters/v3/GitHubSettingsPresenter.server"; +import { useEffect, useState } from "react"; + +// ============================================================================ +// Types +// ============================================================================ + +export type GitHubRepository = { + id: string; + name: string; + fullName: string; + private: boolean; + htmlUrl: string; +}; + +export type GitHubAppInstallation = { + id: string; + appInstallationId: bigint; + targetType: string; + accountHandle: string; + repositories: GitHubRepository[]; +}; + +export type ConnectedGitHubRepo = { + branchTracking: BranchTrackingConfig | undefined; + previewDeploymentsEnabled: boolean; + createdAt: Date; + repository: GitHubRepository; +}; + +// ============================================================================ +// Schemas +// ============================================================================ + +export const ConnectGitHubRepoFormSchema = z.object({ + action: z.literal("connect-repo"), + installationId: z.string(), + repositoryId: z.string(), + redirectUrl: z.string().optional(), +}); + +export const DisconnectGitHubRepoFormSchema = z.object({ + action: z.literal("disconnect-repo"), + redirectUrl: z.string().optional(), +}); + +export const UpdateGitSettingsFormSchema = z.object({ + action: z.literal("update-git-settings"), + productionBranch: z.string().trim().optional(), + stagingBranch: z.string().trim().optional(), + previewDeploymentsEnabled: z + .string() + .optional() + .transform((val) => val === "on"), + redirectUrl: z.string().optional(), +}); + +const GitHubActionSchema = z.discriminatedUnion("action", [ + ConnectGitHubRepoFormSchema, + DisconnectGitHubRepoFormSchema, + UpdateGitSettingsFormSchema, +]); + +// ============================================================================ +// Loader +// ============================================================================ + +export async function loader({ request, params }: LoaderFunctionArgs) { + const userId = await requireUserId(request); + const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params); + + const project = await findProjectBySlug(organizationSlug, projectParam, userId); + if (!project) { + throw new Response("Not Found", { status: 404 }); + } + + const environment = await findEnvironmentBySlug(project.id, envParam, userId); + if (!environment) { + throw new Response("Not Found", { status: 404 }); + } + + const presenter = new GitHubSettingsPresenter(); + const resultOrFail = await presenter.call({ + projectId: project.id, + organizationId: project.organizationId, + }); + + if (resultOrFail.isErr()) { + throw new Response("Failed to load GitHub settings", { status: 500 }); + } + + return typedjson(resultOrFail.value); +} + +// ============================================================================ +// Action +// ============================================================================ + +function redirectWithMessage( + request: Request, + redirectUrl: string | undefined, + message: string, + type: "success" | "error" +) { + if (type === "success") { + return redirectUrl + ? redirectWithSuccessMessage(redirectUrl, request, message) + : redirectBackWithSuccessMessage(request, message); + } + return redirectUrl + ? redirectWithErrorMessage(redirectUrl, request, message) + : redirectBackWithErrorMessage(request, message); +} + +export async function action({ request, params }: ActionFunctionArgs) { + const userId = await requireUserId(request); + const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params); + + const project = await findProjectBySlug(organizationSlug, projectParam, userId); + if (!project) { + throw new Response("Not Found", { status: 404 }); + } + + const environment = await findEnvironmentBySlug(project.id, envParam, userId); + if (!environment) { + throw new Response("Not Found", { status: 404 }); + } + + const formData = await request.formData(); + const submission = parse(formData, { schema: GitHubActionSchema }); + + if (!submission.value || submission.intent !== "submit") { + return json(submission); + } + + const projectSettingsService = new ProjectSettingsService(); + const membershipResultOrFail = await projectSettingsService.verifyProjectMembership( + organizationSlug, + projectParam, + userId + ); + + if (membershipResultOrFail.isErr()) { + return json({ errors: { body: membershipResultOrFail.error.type } }, { status: 404 }); + } + + const { projectId, organizationId } = membershipResultOrFail.value; + const { action: actionType } = submission.value; + + // Handle connect-repo action + if (actionType === "connect-repo") { + const { repositoryId, installationId, redirectUrl } = submission.value; + + const resultOrFail = await projectSettingsService.connectGitHubRepo( + projectId, + organizationId, + repositoryId, + installationId + ); + + if (resultOrFail.isOk()) { + return redirectWithMessage( + request, + redirectUrl, + "GitHub repository connected successfully", + "success" + ); + } + + const errorType = resultOrFail.error.type; + + if (errorType === "gh_repository_not_found") { + return redirectWithMessage(request, redirectUrl, "GitHub repository not found", "error"); + } + + if (errorType === "project_already_has_connected_repository") { + return redirectWithMessage( + request, + redirectUrl, + "Project already has a connected repository", + "error" + ); + } + + logger.error("Failed to connect GitHub repository", { error: resultOrFail.error }); + return redirectWithMessage( + request, + redirectUrl, + "Failed to connect GitHub repository", + "error" + ); + } + + // Handle disconnect-repo action + if (actionType === "disconnect-repo") { + const { redirectUrl } = submission.value; + + const resultOrFail = await projectSettingsService.disconnectGitHubRepo(projectId); + + if (resultOrFail.isOk()) { + return redirectWithMessage( + request, + redirectUrl, + "GitHub repository disconnected successfully", + "success" + ); + } + + logger.error("Failed to disconnect GitHub repository", { error: resultOrFail.error }); + return redirectWithMessage( + request, + redirectUrl, + "Failed to disconnect GitHub repository", + "error" + ); + } + + // Handle update-git-settings action + if (actionType === "update-git-settings") { + const { productionBranch, stagingBranch, previewDeploymentsEnabled, redirectUrl } = + submission.value; + + const resultOrFail = await projectSettingsService.updateGitSettings( + projectId, + productionBranch, + stagingBranch, + previewDeploymentsEnabled + ); + + if (resultOrFail.isOk()) { + return redirectWithMessage( + request, + redirectUrl, + "Git settings updated successfully", + "success" + ); + } + + const errorType = resultOrFail.error.type; + + const errorMessages: Record = { + github_app_not_enabled: "GitHub app is not enabled", + connected_gh_repository_not_found: "Connected GitHub repository not found", + production_tracking_branch_not_found: "Production tracking branch not found", + staging_tracking_branch_not_found: "Staging tracking branch not found", + }; + + const message = errorMessages[errorType]; + if (message) { + return redirectWithMessage(request, redirectUrl, message, "error"); + } + + logger.error("Failed to update Git settings", { error: resultOrFail.error }); + return redirectWithMessage(request, redirectUrl, "Failed to update Git settings", "error"); + } + + // Exhaustive check - this should never be reached + submission.value satisfies never; + return redirectBackWithErrorMessage(request, "Failed to process request"); +} + +// ============================================================================ +// Helper: Build resource URL for fetching GitHub data +// ============================================================================ + +export function gitHubResourcePath( + organizationSlug: string, + projectSlug: string, + environmentSlug: string +) { + return `/resources/orgs/${organizationSlug}/projects/${projectSlug}/env/${environmentSlug}/github`; +} + +// ============================================================================ +// Components +// ============================================================================ + +export function ConnectGitHubRepoModal({ + gitHubAppInstallations, + organizationSlug, + projectSlug, + environmentSlug, + redirectUrl, +}: { + gitHubAppInstallations: GitHubAppInstallation[]; + organizationSlug: string; + projectSlug: string; + environmentSlug: string; + redirectUrl?: string; +}) { + const [isModalOpen, setIsModalOpen] = useState(false); + const lastSubmission = useActionData() as any; + const navigate = useNavigate(); + + const [selectedInstallation, setSelectedInstallation] = useState< + GitHubAppInstallation | undefined + >(gitHubAppInstallations.at(0)); + + const [selectedRepository, setSelectedRepository] = useState( + undefined + ); + + const navigation = useNavigation(); + const isConnectRepositoryLoading = + navigation.formData?.get("action") === "connect-repo" && + (navigation.state === "submitting" || navigation.state === "loading"); + + const [form, { installationId, repositoryId }] = useForm({ + id: "connect-repo", + lastSubmission: lastSubmission, + shouldRevalidate: "onSubmit", + onValidate({ formData }) { + return parse(formData, { + schema: ConnectGitHubRepoFormSchema, + }); + }, + }); + + const [searchParams, setSearchParams] = useSearchParams(); + useEffect(() => { + const params = new URLSearchParams(searchParams); + + if (params.get("openGithubRepoModal") === "1") { + setIsModalOpen(true); + params.delete("openGithubRepoModal"); + setSearchParams(params); + } + }, [searchParams, setSearchParams]); + + useEffect(() => { + if (lastSubmission && "success" in lastSubmission && lastSubmission.success === true) { + setIsModalOpen(false); + } + }, [lastSubmission]); + + const actionUrl = gitHubResourcePath(organizationSlug, projectSlug, environmentSlug); + + return ( + + + + + + Connect GitHub repository +
+
+ {redirectUrl && } + + Choose a GitHub repository to connect to your project. + +
+ + + + {installationId.error} + + + + + + Configure repository access in{" "} + + GitHub + + . + + {repositoryId.error} + + {form.error} + + Connect repository + + } + cancelButton={ + + + + } + /> +
+
+
+
+
+ ); +} + +export function GitHubConnectionPrompt({ + gitHubAppInstallations, + organizationSlug, + projectSlug, + environmentSlug, + redirectUrl, +}: { + gitHubAppInstallations: GitHubAppInstallation[]; + organizationSlug: string; + projectSlug: string; + environmentSlug: string; + redirectUrl?: string; +}) { + + const githubInstallationRedirect = redirectUrl || v3ProjectSettingsPath({ slug: organizationSlug }, { slug: projectSlug }, { slug: environmentSlug }); + return ( +
+ + {gitHubAppInstallations.length === 0 && ( + + Install GitHub app + + )} + {gitHubAppInstallations.length !== 0 && ( +
+ + + GitHub app is installed + +
+ )} +
+
+ ); +} + +export function ConnectedGitHubRepoForm({ + connectedGitHubRepo, + previewEnvironmentEnabled, + organizationSlug, + projectSlug, + environmentSlug, + billingPath, + redirectUrl, +}: { + connectedGitHubRepo: ConnectedGitHubRepo; + previewEnvironmentEnabled?: boolean; + organizationSlug: string; + projectSlug: string; + environmentSlug: string; + billingPath: string; + redirectUrl?: string; +}) { + const lastSubmission = useActionData() as any; + const navigation = useNavigation(); + + const [hasGitSettingsChanges, setHasGitSettingsChanges] = useState(false); + const [gitSettingsValues, setGitSettingsValues] = useState({ + productionBranch: connectedGitHubRepo.branchTracking?.prod?.branch || "", + stagingBranch: connectedGitHubRepo.branchTracking?.staging?.branch || "", + previewDeploymentsEnabled: connectedGitHubRepo.previewDeploymentsEnabled, + }); + + useEffect(() => { + const hasChanges = + gitSettingsValues.productionBranch !== + (connectedGitHubRepo.branchTracking?.prod?.branch || "") || + gitSettingsValues.stagingBranch !== + (connectedGitHubRepo.branchTracking?.staging?.branch || "") || + gitSettingsValues.previewDeploymentsEnabled !== connectedGitHubRepo.previewDeploymentsEnabled; + setHasGitSettingsChanges(hasChanges); + }, [gitSettingsValues, connectedGitHubRepo]); + + const [gitSettingsForm, fields] = useForm({ + id: "update-git-settings", + lastSubmission: lastSubmission, + shouldRevalidate: "onSubmit", + onValidate({ formData }) { + return parse(formData, { + schema: UpdateGitSettingsFormSchema, + }); + }, + }); + + const isGitSettingsLoading = + navigation.formData?.get("action") === "update-git-settings" && + (navigation.state === "submitting" || navigation.state === "loading"); + + const actionUrl = gitHubResourcePath(organizationSlug, projectSlug, environmentSlug); + + return ( + <> +
+
+ + + {connectedGitHubRepo.repository.fullName} + + {connectedGitHubRepo.repository.private && ( + + )} + + + +
+ + + + + + Disconnect GitHub repository +
+ + Are you sure you want to disconnect{" "} + {connectedGitHubRepo.repository.fullName}? + This will stop automatic deployments from GitHub. + + + + {redirectUrl && } + + + } + cancelButton={ + + + + } + /> +
+
+
+
+ +
+ {redirectUrl && } +
+ + + Every push to the selected tracking branch creates a deployment in the corresponding + environment. + +
+
+ + + {environmentFullTitle({ type: "PRODUCTION" })} + +
+ { + setGitSettingsValues((prev) => ({ + ...prev, + productionBranch: e.target.value, + })); + }} + /> +
+ + + {environmentFullTitle({ type: "STAGING" })} + +
+ { + setGitSettingsValues((prev) => ({ + ...prev, + stagingBranch: e.target.value, + })); + }} + /> + +
+ + + {environmentFullTitle({ type: "PREVIEW" })} + +
+
+ { + setGitSettingsValues((prev) => ({ + ...prev, + previewDeploymentsEnabled: checked, + })); + }} + /> + {!previewEnvironmentEnabled && ( + + Upgrade your plan to enable preview + branches + + } + /> + )} +
+
+ {fields.productionBranch?.error} + {fields.stagingBranch?.error} + {fields.previewDeploymentsEnabled?.error} + {gitSettingsForm.error} +
+ + + Save + + } + /> +
+
+ + ); +} + +// ============================================================================ +// Main GitHub Settings Panel Component +// ============================================================================ + +export function GitHubSettingsPanel({ + organizationSlug, + projectSlug, + environmentSlug, + billingPath, +}: { + organizationSlug: string; + projectSlug: string; + environmentSlug: string; + billingPath: string; +}) { + const fetcher = useTypedFetcher(); + const location = useLocation(); + + // Use provided redirectUrl or fall back to current path (without search params) + const effectiveRedirectUrl = location.pathname; + useEffect(() => { + fetcher.load(gitHubResourcePath(organizationSlug, projectSlug, environmentSlug)); + }, [organizationSlug, projectSlug, environmentSlug]); + + const data = fetcher.data; + + // Loading state + if (fetcher.state === "loading" && !data) { + return ( +
+ + Loading GitHub settings... +
+ ); + } + + // GitHub app not enabled + if (!data || !data.enabled) { + return null; + } + + // Connected repository exists - show form + if (data.connectedRepository) { + return ( + + ); + } + + // No connected repository - show connection prompt + return ( +
+ + {!data.connectedRepository && ( + + Connect your GitHub repository to automatically deploy your changes. + + )} +
+ + ); +} diff --git a/apps/webapp/app/routes/storybook.tabs.$tabNumber/route.tsx b/apps/webapp/app/routes/storybook.tabs.$tabNumber/route.tsx index 549108143b..8cf7aaa164 100644 --- a/apps/webapp/app/routes/storybook.tabs.$tabNumber/route.tsx +++ b/apps/webapp/app/routes/storybook.tabs.$tabNumber/route.tsx @@ -3,7 +3,7 @@ import { useParams } from "@remix-run/react"; export default function Story() { const { tabNumber } = useParams(); return ( -
+

{tabNumber}

); diff --git a/apps/webapp/app/routes/storybook.tabs/route.tsx b/apps/webapp/app/routes/storybook.tabs/route.tsx index fc0c8003a4..f3389f2aff 100644 --- a/apps/webapp/app/routes/storybook.tabs/route.tsx +++ b/apps/webapp/app/routes/storybook.tabs/route.tsx @@ -1,18 +1,188 @@ import { Outlet } from "@remix-run/react"; +import { + ClientTabs, + ClientTabsContent, + ClientTabsList, + ClientTabsTrigger, +} from "~/components/primitives/ClientTabs"; +import { Header1 } from "~/components/primitives/Headers"; +import { Paragraph } from "~/components/primitives/Paragraph"; import { Tabs } from "~/components/primitives/Tabs"; export default function Story() { return ( -
- - +
+
+
+
+ {""} (updates the URL) + Variant="underline" +
+ + +
+
+ Variant="pipe-divider" + + +
+
+ Variant="segmented" + + +
+
+
+
+
+ {""} + Variant="underline" +
+ +
+ + + First tab + + + Second tab + + + Third tab + + +
+ +
+

1

+
+
+ +
+

2

+
+
+ +
+

3

+
+
+
+
+ +
+ Variant="pipe-divider" + +
+ + + First tab + + + Second tab + + + Third tab + + +
+ +
+

1

+
+
+ +
+

2

+
+
+ +
+

3

+
+
+
+
+
+ Variant="segmented" + + + + First tab + + + Second tab + + + Third tab + + + +
+

1

+
+
+ +
+

2

+
+
+ +
+

3

+
+
+
+
+
); } From 50077588e3b0985e0f18314cc32fbab7afe33a8b Mon Sep 17 00:00:00 2001 From: Oskar Otwinowski Date: Thu, 11 Dec 2025 17:26:15 +0000 Subject: [PATCH 05/13] feat(webapp): improve adjacent runs navigation and timeline performance (#2776) - Add replace prop to LinkButton to use history replacement for adjacent run navigation - Preserve span and tab params when navigating between adjacent runs - Disable animations for completed spans in timeline to improve performance - Include spanId in runs list navigation for better context preservation - Direct link to task test page when filtering by single task with no runs - Fix minor styling issue with run friendlyId display padding --- .github/workflows/typecheck.yml | 2 + .../app/components/primitives/Buttons.tsx | 4 +- .../route.tsx | 147 ++++++++++-------- .../route.tsx | 17 +- 4 files changed, 105 insertions(+), 65 deletions(-) diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index d7722a46f0..3eb98e5177 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -35,6 +35,8 @@ jobs: - name: 🔎 Type check run: pnpm run typecheck + env: + NODE_OPTIONS: --max-old-space-size=8192 - name: 🔎 Check exports run: pnpm run check-exports diff --git a/apps/webapp/app/components/primitives/Buttons.tsx b/apps/webapp/app/components/primitives/Buttons.tsx index 67ba3c0924..c2845f1040 100644 --- a/apps/webapp/app/components/primitives/Buttons.tsx +++ b/apps/webapp/app/components/primitives/Buttons.tsx @@ -331,7 +331,7 @@ export const Button = forwardRef( type LinkPropsType = Pick< LinkProps, "to" | "target" | "onClick" | "onMouseDown" | "onMouseEnter" | "onMouseLeave" | "download" -> & { disabled?: boolean } & React.ComponentProps; +> & { disabled?: boolean; replace?: boolean } & React.ComponentProps; export const LinkButton = ({ to, onClick, @@ -340,6 +340,7 @@ export const LinkButton = ({ onMouseLeave, download, disabled = false, + replace, ...props }: LinkPropsType) => { const innerRef = useRef(null); @@ -387,6 +388,7 @@ export const LinkButton = ({ ["trace"]>["events"][0]; type RunsListNavigation = { - runs: Array<{ friendlyId: string }>; + runs: Array<{ friendlyId: string; spanId: string }>; pagination: { next?: string; previous?: string }; - prevPageLastRun?: { friendlyId: string; cursor: string }; - nextPageFirstRun?: { friendlyId: string; cursor: string }; + prevPageLastRun?: { friendlyId: string; spanId: string; cursor: string }; + nextPageFirstRun?: { friendlyId: string; spanId: string; cursor: string }; }; async function getRunsListFromTableState({ @@ -204,6 +204,7 @@ async function getRunsListFromTableState({ if (prevPageResult.runs.length > 0) { runsList.prevPageLastRun = { friendlyId: prevPageResult.runs[0].friendlyId, + spanId: prevPageResult.runs[0].spanId, cursor: currentPageResult.pagination.previous, }; } @@ -222,6 +223,7 @@ async function getRunsListFromTableState({ if (nextPageResult.runs.length > 0) { runsList.nextPageFirstRun = { friendlyId: nextPageResult.runs[0].friendlyId, + spanId: nextPageResult.runs[0].spanId, cursor: currentPageResult.pagination.next, }; } @@ -296,7 +298,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { type LoaderData = SerializeFrom; export default function Page() { - const { run, trace, resizable, maximumLiveReloadingSetting, runsList } = useLoaderData(); + const { run, trace, maximumLiveReloadingSetting, runsList } = useLoaderData(); const organization = useOrganization(); const project = useProject(); const environment = useEnvironment(); @@ -308,8 +310,10 @@ export default function Page() { const tableState = decodeURIComponent(value("tableState") ?? ""); const tableStateSearchParams = new URLSearchParams(tableState); const filters = getRunFiltersFromSearchParams(tableStateSearchParams); + const tabParam = value("tab") ?? undefined; + const spanParam = value("span") ?? undefined; - const [previousRunPath, nextRunPath] = useAdjacentRunPaths({organization, project, environment, tableState, run, runsList}); + const [previousRunPath, nextRunPath] = useAdjacentRunPaths({organization, project, environment, tableState, run, runsList, tabParam, useSpan: !!spanParam}); return ( <> @@ -320,7 +324,7 @@ export default function Page() { text: "Runs", }} title={<> - + {tableState && (
@@ -940,7 +944,6 @@ function TimelineView({ scale, rootSpanStatus, rootStartedAt, - parentRef, timelineScrollRef, virtualizer, events, @@ -1127,7 +1130,8 @@ function TimelineView({ "-ml-[0.5px] h-[0.5625rem] w-px rounded-none", eventBackgroundClassName(node.data) )} - layoutId={`${node.id}-${event.name}`} + layoutId={node.data.isPartial ? `${node.id}-${event.name}` : undefined} + animate={!node.data.isPartial ? false : undefined} /> )} @@ -1145,7 +1149,8 @@ function TimelineView({ "-ml-[0.1562rem] size-[0.3125rem] rounded-full border bg-background-bright", eventBorderClassName(node.data) )} - layoutId={`${node.id}-${event.name}`} + layoutId={node.data.isPartial ? `${node.id}-${event.name}` : undefined} + animate={!node.data.isPartial ? false : undefined} /> )} @@ -1164,7 +1169,8 @@ function TimelineView({ > ) : null} @@ -1201,7 +1207,8 @@ function TimelineView({ "-ml-0.5 size-3 rounded-full border-2 border-background-bright", eventBackgroundClassName(node.data) )} - layoutId={node.id} + layoutId={node.data.isPartial ? node.id : undefined} + animate={!node.data.isPartial ? false : undefined} /> )} @@ -1444,7 +1451,8 @@ function SpanWithDuration({ fadeLeft ? "rounded-r-sm bg-gradient-to-r from-black/50 to-transparent" : "rounded-sm" )} style={{ backgroundSize: "20px 100%", backgroundRepeat: "no-repeat" }} - layoutId={node.id} + layoutId={node.data.isPartial ? node.id : undefined} + animate={!node.data.isPartial ? false : undefined} > {node.data.isPartial && (
{formatDurationMilliseconds(props.durationMs, { style: "short", @@ -1543,12 +1553,11 @@ function KeyboardShortcuts({ expandAllBelowDepth, collapseAllBelowDepth, toggleExpandLevel, - setShowDurations, }: { expandAllBelowDepth: (depth: number) => void; collapseAllBelowDepth: (depth: number) => void; toggleExpandLevel: (depth: number) => void; - setShowDurations: (show: (show: boolean) => boolean) => void; + setShowDurations?: (show: (show: boolean) => boolean) => void; }) { return ( <> @@ -1666,63 +1675,77 @@ function useAdjacentRunPaths({ tableState, run, runsList, + tabParam, + useSpan }: { organization: { slug: string }; project: { slug: string }; environment: { slug: string }; tableState: string; - run: { friendlyId: string }; + run: { friendlyId: string, spanId: string }; runsList: RunsListNavigation | null; + tabParam?: string; + useSpan?: boolean; }): [string | null, string | null] { - return useMemo(() => { - if (!runsList || runsList.runs.length === 0) { - return [null, null]; - } - - const currentIndex = runsList.runs.findIndex((r) => r.friendlyId === run.friendlyId); - - if (currentIndex === -1) { - return [null, null]; - } + if (!runsList || runsList.runs.length === 0) { + return [null, null]; + } - // Determine previous run: use prevPageLastRun if at first position, otherwise use previous run in list - let previousRun: { friendlyId: string } | null = null; - const previousRunTableState = new URLSearchParams(tableState); - if (currentIndex > 0) { - previousRun = runsList.runs[currentIndex - 1]; - } else if (runsList.prevPageLastRun) { - previousRun = runsList.prevPageLastRun; - // Update tableState with the new cursor for the previous page - previousRunTableState.set("cursor", runsList.prevPageLastRun.cursor); - previousRunTableState.set("direction", "backward"); - } + const currentIndex = runsList.runs.findIndex((r) => r.friendlyId === run.friendlyId); + + if (currentIndex === -1) { + return [null, null]; + } - // Determine next run: use nextPageFirstRun if at last position, otherwise use next run in list - let nextRun: { friendlyId: string } | null = null; - const nextRunTableState = new URLSearchParams(tableState); - if (currentIndex < runsList.runs.length - 1) { - nextRun = runsList.runs[currentIndex + 1]; - } else if (runsList.nextPageFirstRun) { - nextRun = runsList.nextPageFirstRun; - // Update tableState with the new cursor for the next page - nextRunTableState.set("cursor", runsList.nextPageFirstRun.cursor); - nextRunTableState.set("direction", "forward"); - } + // Determine previous run: use prevPageLastRun if at first position, otherwise use previous run in list + let previousRun: { friendlyId: string; spanId: string } | null = null; + const previousRunTableState = new URLSearchParams(tableState); + if (currentIndex > 0) { + previousRun = runsList.runs[currentIndex - 1]; + } else if (runsList.prevPageLastRun) { + previousRun = runsList.prevPageLastRun; + // Update tableState with the new cursor for the previous page + previousRunTableState.set("cursor", runsList.prevPageLastRun.cursor); + previousRunTableState.set("direction", "backward"); + } - const previousURLSearchParams = new URLSearchParams(); - previousURLSearchParams.set("tableState", previousRunTableState.toString()); - const previousRunPath = previousRun - ? v3RunPath(organization, project, environment, previousRun, previousURLSearchParams) - : null; + // Determine next run: use nextPageFirstRun if at last position, otherwise use next run in list + let nextRun: { friendlyId: string; spanId: string } | null = null; + const nextRunTableState = new URLSearchParams(tableState); + if (currentIndex < runsList.runs.length - 1) { + nextRun = runsList.runs[currentIndex + 1]; + } else if (runsList.nextPageFirstRun) { + nextRun = runsList.nextPageFirstRun; + // Update tableState with the new cursor for the next page + nextRunTableState.set("cursor", runsList.nextPageFirstRun.cursor); + nextRunTableState.set("direction", "forward"); + } - const nextURLSearchParams = new URLSearchParams(); - nextURLSearchParams.set("tableState", nextRunTableState.toString()); - const nextRunPath = nextRun - ? v3RunPath(organization, project, environment, nextRun, nextURLSearchParams) - : null; + const previousURLSearchParams = new URLSearchParams(); + previousURLSearchParams.set("tableState", previousRunTableState.toString()); + if (previousRun && useSpan) { + previousURLSearchParams.set("span", previousRun.spanId); + } + if (tabParam && useSpan) { + previousURLSearchParams.set("tab", tabParam); + } + const previousRunPath = previousRun + ? v3RunPath(organization, project, environment, previousRun, previousURLSearchParams) + : null; + + const nextURLSearchParams = new URLSearchParams(); + nextURLSearchParams.set("tableState", nextRunTableState.toString()); + if (nextRun && useSpan) { + nextURLSearchParams.set("span", nextRun.spanId); + } + if (tabParam && useSpan) { + nextURLSearchParams.set("tab", tabParam); + } + const nextRunPath = nextRun + ? v3RunPath(organization, project, environment, nextRun, nextURLSearchParams) + : null; - return [previousRunPath, nextRunPath]; - }, [organization, project, environment, tableState, run.friendlyId, runsList]); + return [previousRunPath, nextRunPath]; } @@ -1741,6 +1764,7 @@ function PreviousRunButton({ to }: { to: string | null }) { shortcut={{ key: "[" }} tooltip="Previous Run" disabled={!to} + replace />
); @@ -1761,6 +1785,7 @@ function NextRunButton({ to }: { to: string | null }) { shortcut={{ key: "]" }} tooltip="Next Run" disabled={!to} + replace />
); diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index/route.tsx index 8c41f0ceac..8f80124707 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index/route.tsx @@ -55,6 +55,7 @@ import { v3CreateBulkActionPath, v3ProjectPath, v3TestPath, + v3TestTaskPath, } from "~/utils/pathBuilder"; import { ListPagination } from "../../components/ListPagination"; import { CreateBulkActionInspector } from "../resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.bulkaction"; @@ -235,7 +236,13 @@ function RunsList({ list.possibleTasks.length === 0 ? ( ) : ( - + t.slug === list.filters.tasks[0]) + : undefined + } + /> ) ) : (
@@ -339,7 +346,7 @@ function CreateFirstTaskInstructions() { ); } -function RunTaskInstructions() { +function RunTaskInstructions({ task }: { task?: { slug: string } }) { const organization = useOrganization(); const project = useProject(); const environment = useEnvironment(); @@ -352,7 +359,11 @@ function RunTaskInstructions() { Perform a test run with a payload directly from the dashboard. Date: Fri, 12 Dec 2025 09:48:04 +0100 Subject: [PATCH 06/13] feat(cli): deterministic image builds for deployments (#2778) This PR makes our image builds deterministic and reproducible by ensuring that identical source code always produces the same image layers and image digest. This means that deployments where nothing has changed will no longer invalidate the image cache in our worker cluster nodes, thus avoid making the cold starts for runs worse. **Context** New deployments currently increase the cold start times for runs, as they generate a new image which needs to be pulled in the worker cluster where runs are executed. It happens also when the source code for the deployment has not changed due to non-deterministic steps in our build system. This addresses the latter issue by making builds reproducible. **Main changes** - Avoided baking `TRIGGER_DEPLOYMENT_ID` and `TRIGGER_DEPLOYMENT_VERSION` in the image, we now pass these via the supervisor instead. - Used `json-stable-stringify` for consistent key ordering in the files we generate for the build, e.g., `package.json`, `build.json`, `index.json`. - Removed `metafile.json` from the image contents as it is not actually used in the container. This is only relevant for the `analyze` command. - Added `SOURCE_DATE_EPOCH=0` and `rewrite-timestamp=true` to Docker builds to normalize file timestamps. - Removed some `timings` and `outputHashes` from build outputs and manifests. The builds are now reproducible for both native build server and Depot paths. This should also lead to better image layer cache reuse in general. --- .changeset/five-pens-fly.md | 6 +++ apps/supervisor/src/index.ts | 8 ++++ apps/supervisor/src/workloadManager/docker.ts | 2 + .../src/workloadManager/kubernetes.ts | 8 ++++ apps/supervisor/src/workloadManager/types.ts | 2 + .../src/engine/systems/dequeueSystem.ts | 2 + packages/cli-v3/package.json | 3 +- packages/cli-v3/src/build/buildWorker.ts | 27 ++++++------ packages/cli-v3/src/build/bundle.ts | 4 +- packages/cli-v3/src/deploy/buildImage.ts | 30 ++++++------- .../entryPoints/managed-index-controller.ts | 4 +- .../cli-v3/src/entryPoints/managed/env.ts | 4 +- .../cli-v3/src/utilities/buildManifest.ts | 4 +- packages/cli-v3/src/utilities/fileSystem.ts | 3 +- pnpm-lock.yaml | 43 ++++++++++++++----- 15 files changed, 104 insertions(+), 46 deletions(-) create mode 100644 .changeset/five-pens-fly.md diff --git a/.changeset/five-pens-fly.md b/.changeset/five-pens-fly.md new file mode 100644 index 0000000000..2251b67e58 --- /dev/null +++ b/.changeset/five-pens-fly.md @@ -0,0 +1,6 @@ +--- +"trigger.dev": minor +"@trigger.dev/core": minor +--- + +feat(cli): deterministic image builds for deployments diff --git a/apps/supervisor/src/index.ts b/apps/supervisor/src/index.ts index b958897ad4..0e274b3039 100644 --- a/apps/supervisor/src/index.ts +++ b/apps/supervisor/src/index.ts @@ -244,6 +244,12 @@ class ManagedSupervisor { } try { + if (!message.deployment.friendlyId) { + // mostly a type guard, deployments always exists for deployed environments + // a proper fix would be to use a discriminated union schema to differentiate between dequeued runs in dev and in deployed environments. + throw new Error("Deployment is missing"); + } + await this.workloadManager.create({ dequeuedAt: message.dequeuedAt, envId: message.environment.id, @@ -252,6 +258,8 @@ class ManagedSupervisor { machine: message.run.machine, orgId: message.organization.id, projectId: message.project.id, + deploymentFriendlyId: message.deployment.friendlyId, + deploymentVersion: message.backgroundWorker.version, runId: message.run.id, runFriendlyId: message.run.friendlyId, version: message.version, diff --git a/apps/supervisor/src/workloadManager/docker.ts b/apps/supervisor/src/workloadManager/docker.ts index 4ebbe11ca7..d6651d325a 100644 --- a/apps/supervisor/src/workloadManager/docker.ts +++ b/apps/supervisor/src/workloadManager/docker.ts @@ -72,6 +72,8 @@ export class DockerWorkloadManager implements WorkloadManager { `TRIGGER_DEQUEUED_AT_MS=${opts.dequeuedAt.getTime()}`, `TRIGGER_POD_SCHEDULED_AT_MS=${Date.now()}`, `TRIGGER_ENV_ID=${opts.envId}`, + `TRIGGER_DEPLOYMENT_ID=${opts.deploymentFriendlyId}`, + `TRIGGER_DEPLOYMENT_VERSION=${opts.deploymentVersion}`, `TRIGGER_RUN_ID=${opts.runFriendlyId}`, `TRIGGER_SNAPSHOT_ID=${opts.snapshotFriendlyId}`, `TRIGGER_SUPERVISOR_API_PROTOCOL=${this.opts.workloadApiProtocol}`, diff --git a/apps/supervisor/src/workloadManager/kubernetes.ts b/apps/supervisor/src/workloadManager/kubernetes.ts index 2042bf8b6a..96fbd7a274 100644 --- a/apps/supervisor/src/workloadManager/kubernetes.ts +++ b/apps/supervisor/src/workloadManager/kubernetes.ts @@ -123,6 +123,14 @@ export class KubernetesWorkloadManager implements WorkloadManager { name: "TRIGGER_ENV_ID", value: opts.envId, }, + { + name: "TRIGGER_DEPLOYMENT_ID", + value: opts.deploymentFriendlyId, + }, + { + name: "TRIGGER_DEPLOYMENT_VERSION", + value: opts.deploymentVersion, + }, { name: "TRIGGER_SNAPSHOT_ID", value: opts.snapshotFriendlyId, diff --git a/apps/supervisor/src/workloadManager/types.ts b/apps/supervisor/src/workloadManager/types.ts index 64573fb3b9..90b6195779 100644 --- a/apps/supervisor/src/workloadManager/types.ts +++ b/apps/supervisor/src/workloadManager/types.ts @@ -29,6 +29,8 @@ export interface WorkloadManagerCreateOptions { envType: EnvironmentType; orgId: string; projectId: string; + deploymentFriendlyId: string; + deploymentVersion: string; runId: string; runFriendlyId: string; snapshotId: string; diff --git a/internal-packages/run-engine/src/engine/systems/dequeueSystem.ts b/internal-packages/run-engine/src/engine/systems/dequeueSystem.ts index a8316a9880..5a951a645c 100644 --- a/internal-packages/run-engine/src/engine/systems/dequeueSystem.ts +++ b/internal-packages/run-engine/src/engine/systems/dequeueSystem.ts @@ -559,6 +559,8 @@ export class DequeueSystem { friendlyId: result.worker.friendlyId, version: result.worker.version, }, + // TODO: use a discriminated union schema to differentiate between dequeued runs in dev and in deployed environments. + // Would help make the typechecking stricter deployment: { id: result.deployment?.id, friendlyId: result.deployment?.friendlyId, diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index 81f374099d..3223f332c5 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -92,10 +92,10 @@ "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-trace-node": "2.0.1", "@opentelemetry/semantic-conventions": "1.36.0", + "@s2-dev/streamstore": "^0.17.6", "@trigger.dev/build": "workspace:4.2.0", "@trigger.dev/core": "workspace:4.2.0", "@trigger.dev/schema-to-json": "workspace:4.2.0", - "@s2-dev/streamstore": "^0.17.6", "ansi-escapes": "^7.0.0", "braces": "^3.0.3", "c12": "^1.11.1", @@ -117,6 +117,7 @@ "import-in-the-middle": "1.11.0", "import-meta-resolve": "^4.1.0", "ini": "^5.0.0", + "json-stable-stringify": "^1.3.0", "jsonc-parser": "3.2.1", "magicast": "^0.3.4", "minimatch": "^10.0.1", diff --git a/packages/cli-v3/src/build/buildWorker.ts b/packages/cli-v3/src/build/buildWorker.ts index 6e818a0b1e..b23b802f50 100644 --- a/packages/cli-v3/src/build/buildWorker.ts +++ b/packages/cli-v3/src/build/buildWorker.ts @@ -12,7 +12,7 @@ import { join, relative, sep } from "node:path"; import { generateContainerfile } from "../deploy/buildImage.js"; import { writeFile } from "node:fs/promises"; import { buildManifestToJSON } from "../utilities/buildManifest.js"; -import { readPackageJSON, writePackageJSON } from "pkg-types"; +import { readPackageJSON } from "pkg-types"; import { writeJSONFile } from "../utilities/fileSystem.js"; import { isWindows } from "std-env"; import { pathToFileURL } from "node:url"; @@ -192,20 +192,23 @@ async function writeDeployFiles({ ) ?? {}; // Step 3: Write the resolved dependencies to the package.json file - await writePackageJSON(join(outputPath, "package.json"), { - ...packageJson, - name: packageJson.name ?? "trigger-project", - dependencies: { - ...dependencies, + await writeJSONFile( + join(outputPath, "package.json"), + { + ...packageJson, + name: packageJson.name ?? "trigger-project", + dependencies: { + ...dependencies, + }, + trustedDependencies: Object.keys(dependencies).sort(), + devDependencies: {}, + peerDependencies: {}, + scripts: {}, }, - trustedDependencies: Object.keys(dependencies), - devDependencies: {}, - peerDependencies: {}, - scripts: {}, - }); + true + ); await writeJSONFile(join(outputPath, "build.json"), buildManifestToJSON(buildManifest)); - await writeJSONFile(join(outputPath, "metafile.json"), bundleResult.metafile); await writeContainerfile(outputPath, buildManifest); } diff --git a/packages/cli-v3/src/build/bundle.ts b/packages/cli-v3/src/build/bundle.ts index c085937f68..4d1cfd53f8 100644 --- a/packages/cli-v3/src/build/bundle.ts +++ b/packages/cli-v3/src/build/bundle.ts @@ -414,7 +414,9 @@ export async function createBuildManifestFromBundle({ otelImportHook: { include: resolvedConfig.instrumentedPackageNames ?? [], }, - outputHashes: bundle.outputHashes, + // `outputHashes` is only needed for dev builds for the deduplication mechanism during rebuilds. + // For deploys builds, we omit it to ensure deterministic builds + outputHashes: target === "dev" ? bundle.outputHashes : {}, }; if (!workerDir) { diff --git a/packages/cli-v3/src/deploy/buildImage.ts b/packages/cli-v3/src/deploy/buildImage.ts index d6957bf973..2225d7db05 100644 --- a/packages/cli-v3/src/deploy/buildImage.ts +++ b/packages/cli-v3/src/deploy/buildImage.ts @@ -201,6 +201,7 @@ async function remoteBuildImage(options: DepotBuildImageOptions): Promise (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)), deploy: {}, build: {}, }; diff --git a/packages/cli-v3/src/utilities/fileSystem.ts b/packages/cli-v3/src/utilities/fileSystem.ts index 28178881b1..87d74732eb 100644 --- a/packages/cli-v3/src/utilities/fileSystem.ts +++ b/packages/cli-v3/src/utilities/fileSystem.ts @@ -1,4 +1,5 @@ import fsSync from "fs"; +import stringify from "json-stable-stringify"; import fsModule, { writeFile } from "fs/promises"; import fs from "node:fs"; import { homedir, tmpdir } from "node:os"; @@ -159,7 +160,7 @@ export async function safeReadJSONFile(path: string) { } export async function writeJSONFile(path: string, json: any, pretty = false) { - await safeWriteFile(path, JSON.stringify(json, undefined, pretty ? 2 : undefined)); + await safeWriteFile(path, stringify(json, pretty ? { space: 2 } : undefined) ?? ""); } // Will create the directory if it doesn't exist diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 000ad8ab3d..4102c43736 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1444,6 +1444,9 @@ importers: ini: specifier: ^5.0.0 version: 5.0.0 + json-stable-stringify: + specifier: ^1.3.0 + version: 1.3.0 jsonc-parser: specifier: 3.2.1 version: 3.2.1 @@ -11803,13 +11806,14 @@ packages: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.2: - resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - call-bind@1.0.7: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} @@ -14834,6 +14838,10 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json-stable-stringify@1.3.0: + resolution: {integrity: sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==} + engines: {node: '>= 0.4'} + json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} @@ -14860,6 +14868,9 @@ packages: jsonfile@6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonify@0.0.1: + resolution: {integrity: sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==} + jsonpath-plus@10.3.0: resolution: {integrity: sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==} engines: {node: '>=18.0.0'} @@ -31881,7 +31892,7 @@ snapshots: array.prototype.flat@1.3.2: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.0 @@ -31895,7 +31906,7 @@ snapshots: array.prototype.flatmap@1.3.2: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.0 @@ -32328,16 +32339,18 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.2: + call-bind@1.0.7: dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.3.0 + set-function-length: 1.2.2 - call-bind@1.0.7: + call-bind@1.0.8: dependencies: + call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 - es-errors: 1.3.0 - function-bind: 1.1.2 get-intrinsic: 1.3.0 set-function-length: 1.2.2 @@ -35816,6 +35829,14 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json-stable-stringify@1.3.0: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + isarray: 2.0.5 + jsonify: 0.0.1 + object-keys: 1.1.1 + json-stringify-safe@5.0.1: {} json5@1.0.2: @@ -35842,6 +35863,8 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 + jsonify@0.0.1: {} + jsonpath-plus@10.3.0: dependencies: '@jsep-plugin/assignment': 1.3.0(jsep@1.4.0) @@ -40313,7 +40336,7 @@ snapshots: string.prototype.padend@3.1.4: dependencies: - call-bind: 1.0.2 + call-bind: 1.0.7 define-properties: 1.1.4 es-abstract: 1.21.1 From f028f266f82da4c25b1de383e35f1aa8f292bec9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 13:11:56 +0100 Subject: [PATCH 07/13] chore: release v4.3.0 (#2770) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## trigger.dev@4.3.0 ### Minor Changes - feat(cli): deterministic image builds for deployments ([#2778](https://github.com/triggerdotdev/trigger.dev/pull/2778)) - feat(cli): enable zstd compression for deployment images ([#2773](https://github.com/triggerdotdev/trigger.dev/pull/2773)) ### Patch Changes - The new `triggeredVia` field is now populated in deployments via the CLI. ([#2767](https://github.com/triggerdotdev/trigger.dev/pull/2767)) - fix(dev): stop max listeners exceeded warning messages when running more than 10 runs concurrently ([#2771](https://github.com/triggerdotdev/trigger.dev/pull/2771)) - Upgrade @modelcontextprotocol/sdk to 1.24.3 ([#2768](https://github.com/triggerdotdev/trigger.dev/pull/2768)) - Updated dependencies: - `@trigger.dev/core@4.3.0` - `@trigger.dev/build@4.3.0` - `@trigger.dev/schema-to-json@4.3.0` ## @trigger.dev/core@4.3.0 ### Minor Changes - feat(cli): deterministic image builds for deployments ([#2778](https://github.com/triggerdotdev/trigger.dev/pull/2778)) ### Patch Changes - The new `triggeredVia` field is now populated in deployments via the CLI. ([#2767](https://github.com/triggerdotdev/trigger.dev/pull/2767)) ## @trigger.dev/build@4.3.0 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.3.0` ## @trigger.dev/python@4.3.0 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.3.0` - `@trigger.dev/build@4.3.0` - `@trigger.dev/sdk@4.3.0` ## @trigger.dev/react-hooks@4.3.0 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.3.0` ## @trigger.dev/redis-worker@4.3.0 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.3.0` ## @trigger.dev/rsc@4.3.0 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.3.0` ## @trigger.dev/schema-to-json@4.3.0 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.3.0` ## @trigger.dev/sdk@4.3.0 ### Patch Changes - Updated dependencies: - `@trigger.dev/core@4.3.0` --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] --- .changeset/five-pens-fly.md | 6 - .changeset/fluffy-weeks-sleep.md | 6 - .changeset/fuzzy-ghosts-admire.md | 5 - .changeset/great-pillows-look.md | 5 - .changeset/ninety-cougars-remember.md | 5 - packages/build/CHANGELOG.md | 7 + packages/build/package.json | 4 +- packages/cli-v3/CHANGELOG.md | 17 ++ packages/cli-v3/package.json | 8 +- packages/core/CHANGELOG.md | 10 + packages/core/package.json | 2 +- packages/python/CHANGELOG.md | 9 + packages/python/package.json | 12 +- packages/react-hooks/CHANGELOG.md | 7 + packages/react-hooks/package.json | 4 +- packages/redis-worker/CHANGELOG.md | 7 + packages/redis-worker/package.json | 4 +- packages/rsc/CHANGELOG.md | 7 + packages/rsc/package.json | 6 +- packages/schema-to-json/CHANGELOG.md | 7 + packages/schema-to-json/package.json | 2 +- packages/trigger-sdk/CHANGELOG.md | 7 + packages/trigger-sdk/package.json | 4 +- pnpm-lock.yaml | 359 ++++++++++++-------------- 24 files changed, 272 insertions(+), 238 deletions(-) delete mode 100644 .changeset/five-pens-fly.md delete mode 100644 .changeset/fluffy-weeks-sleep.md delete mode 100644 .changeset/fuzzy-ghosts-admire.md delete mode 100644 .changeset/great-pillows-look.md delete mode 100644 .changeset/ninety-cougars-remember.md diff --git a/.changeset/five-pens-fly.md b/.changeset/five-pens-fly.md deleted file mode 100644 index 2251b67e58..0000000000 --- a/.changeset/five-pens-fly.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"trigger.dev": minor -"@trigger.dev/core": minor ---- - -feat(cli): deterministic image builds for deployments diff --git a/.changeset/fluffy-weeks-sleep.md b/.changeset/fluffy-weeks-sleep.md deleted file mode 100644 index 6235b335eb..0000000000 --- a/.changeset/fluffy-weeks-sleep.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"trigger.dev": patch -"@trigger.dev/core": patch ---- - -The new `triggeredVia` field is now populated in deployments via the CLI. diff --git a/.changeset/fuzzy-ghosts-admire.md b/.changeset/fuzzy-ghosts-admire.md deleted file mode 100644 index 56982d015f..0000000000 --- a/.changeset/fuzzy-ghosts-admire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"trigger.dev": patch ---- - -fix(dev): stop max listeners exceeded warning messages when running more than 10 runs concurrently diff --git a/.changeset/great-pillows-look.md b/.changeset/great-pillows-look.md deleted file mode 100644 index 72a0589b26..0000000000 --- a/.changeset/great-pillows-look.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"trigger.dev": minor ---- - -feat(cli): enable zstd compression for deployment images diff --git a/.changeset/ninety-cougars-remember.md b/.changeset/ninety-cougars-remember.md deleted file mode 100644 index 2d4c7e5478..0000000000 --- a/.changeset/ninety-cougars-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"trigger.dev": patch ---- - -Upgrade @modelcontextprotocol/sdk to 1.24.3 diff --git a/packages/build/CHANGELOG.md b/packages/build/CHANGELOG.md index e2bf3dc5ab..5ce6b5f6a1 100644 --- a/packages/build/CHANGELOG.md +++ b/packages/build/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/build +## 4.3.0 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.3.0` + ## 4.2.0 ### Patch Changes diff --git a/packages/build/package.json b/packages/build/package.json index 54b09b3e23..f764f42801 100644 --- a/packages/build/package.json +++ b/packages/build/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/build", - "version": "4.2.0", + "version": "4.3.0", "description": "trigger.dev build extensions", "license": "MIT", "publishConfig": { @@ -78,7 +78,7 @@ }, "dependencies": { "@prisma/config": "^6.10.0", - "@trigger.dev/core": "workspace:4.2.0", + "@trigger.dev/core": "workspace:4.3.0", "mlly": "^1.7.1", "pkg-types": "^1.1.3", "resolve": "^1.22.8", diff --git a/packages/cli-v3/CHANGELOG.md b/packages/cli-v3/CHANGELOG.md index f7ae2e7ea3..0f5f8a0990 100644 --- a/packages/cli-v3/CHANGELOG.md +++ b/packages/cli-v3/CHANGELOG.md @@ -1,5 +1,22 @@ # trigger.dev +## 4.3.0 + +### Minor Changes + +- feat(cli): deterministic image builds for deployments ([#2778](https://github.com/triggerdotdev/trigger.dev/pull/2778)) +- feat(cli): enable zstd compression for deployment images ([#2773](https://github.com/triggerdotdev/trigger.dev/pull/2773)) + +### Patch Changes + +- The new `triggeredVia` field is now populated in deployments via the CLI. ([#2767](https://github.com/triggerdotdev/trigger.dev/pull/2767)) +- fix(dev): stop max listeners exceeded warning messages when running more than 10 runs concurrently ([#2771](https://github.com/triggerdotdev/trigger.dev/pull/2771)) +- Upgrade @modelcontextprotocol/sdk to 1.24.3 ([#2768](https://github.com/triggerdotdev/trigger.dev/pull/2768)) +- Updated dependencies: + - `@trigger.dev/core@4.3.0` + - `@trigger.dev/build@4.3.0` + - `@trigger.dev/schema-to-json@4.3.0` + ## 4.2.0 ### Minor Changes diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index 3223f332c5..1dfc9aaf76 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -1,6 +1,6 @@ { "name": "trigger.dev", - "version": "4.2.0", + "version": "4.3.0", "description": "A Command-Line Interface for Trigger.dev projects", "type": "module", "license": "MIT", @@ -93,9 +93,9 @@ "@opentelemetry/sdk-trace-node": "2.0.1", "@opentelemetry/semantic-conventions": "1.36.0", "@s2-dev/streamstore": "^0.17.6", - "@trigger.dev/build": "workspace:4.2.0", - "@trigger.dev/core": "workspace:4.2.0", - "@trigger.dev/schema-to-json": "workspace:4.2.0", + "@trigger.dev/build": "workspace:4.3.0", + "@trigger.dev/core": "workspace:4.3.0", + "@trigger.dev/schema-to-json": "workspace:4.3.0", "ansi-escapes": "^7.0.0", "braces": "^3.0.3", "c12": "^1.11.1", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 7ee6c18dad..b364ad422f 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,15 @@ # internal-platform +## 4.3.0 + +### Minor Changes + +- feat(cli): deterministic image builds for deployments ([#2778](https://github.com/triggerdotdev/trigger.dev/pull/2778)) + +### Patch Changes + +- The new `triggeredVia` field is now populated in deployments via the CLI. ([#2767](https://github.com/triggerdotdev/trigger.dev/pull/2767)) + ## 4.2.0 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index 2043613945..fefd0224fe 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/core", - "version": "4.2.0", + "version": "4.3.0", "description": "Core code used across the Trigger.dev SDK and platform", "license": "MIT", "publishConfig": { diff --git a/packages/python/CHANGELOG.md b/packages/python/CHANGELOG.md index 80c49a28bb..57a39087da 100644 --- a/packages/python/CHANGELOG.md +++ b/packages/python/CHANGELOG.md @@ -1,5 +1,14 @@ # @trigger.dev/python +## 4.3.0 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.3.0` + - `@trigger.dev/build@4.3.0` + - `@trigger.dev/sdk@4.3.0` + ## 4.2.0 ### Patch Changes diff --git a/packages/python/package.json b/packages/python/package.json index 188f0d033f..f4ace48267 100644 --- a/packages/python/package.json +++ b/packages/python/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/python", - "version": "4.2.0", + "version": "4.3.0", "description": "Python runtime and build extension for Trigger.dev", "license": "MIT", "publishConfig": { @@ -45,7 +45,7 @@ "check-exports": "attw --pack ." }, "dependencies": { - "@trigger.dev/core": "workspace:4.2.0", + "@trigger.dev/core": "workspace:4.3.0", "tinyexec": "^0.3.2" }, "devDependencies": { @@ -56,12 +56,12 @@ "tsx": "4.17.0", "esbuild": "^0.23.0", "@arethetypeswrong/cli": "^0.15.4", - "@trigger.dev/build": "workspace:4.2.0", - "@trigger.dev/sdk": "workspace:4.2.0" + "@trigger.dev/build": "workspace:4.3.0", + "@trigger.dev/sdk": "workspace:4.3.0" }, "peerDependencies": { - "@trigger.dev/sdk": "workspace:^4.2.0", - "@trigger.dev/build": "workspace:^4.2.0" + "@trigger.dev/sdk": "workspace:^4.3.0", + "@trigger.dev/build": "workspace:^4.3.0" }, "engines": { "node": ">=18.20.0" diff --git a/packages/react-hooks/CHANGELOG.md b/packages/react-hooks/CHANGELOG.md index 8fe5c9b7d9..41febd39f0 100644 --- a/packages/react-hooks/CHANGELOG.md +++ b/packages/react-hooks/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/react-hooks +## 4.3.0 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.3.0` + ## 4.2.0 ### Patch Changes diff --git a/packages/react-hooks/package.json b/packages/react-hooks/package.json index b2de5ecbc8..ed38284acf 100644 --- a/packages/react-hooks/package.json +++ b/packages/react-hooks/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/react-hooks", - "version": "4.2.0", + "version": "4.3.0", "description": "trigger.dev react hooks", "license": "MIT", "publishConfig": { @@ -37,7 +37,7 @@ "check-exports": "attw --pack ." }, "dependencies": { - "@trigger.dev/core": "workspace:^4.2.0", + "@trigger.dev/core": "workspace:^4.3.0", "swr": "^2.2.5" }, "devDependencies": { diff --git a/packages/redis-worker/CHANGELOG.md b/packages/redis-worker/CHANGELOG.md index f3400b9399..ac090b2903 100644 --- a/packages/redis-worker/CHANGELOG.md +++ b/packages/redis-worker/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/redis-worker +## 4.3.0 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.3.0` + ## 4.2.0 ### Patch Changes diff --git a/packages/redis-worker/package.json b/packages/redis-worker/package.json index 7e8b94adea..c07239603e 100644 --- a/packages/redis-worker/package.json +++ b/packages/redis-worker/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/redis-worker", - "version": "4.2.0", + "version": "4.3.0", "description": "Redis worker for trigger.dev", "license": "MIT", "publishConfig": { @@ -23,7 +23,7 @@ "test": "vitest --sequence.concurrent=false --no-file-parallelism" }, "dependencies": { - "@trigger.dev/core": "workspace:4.2.0", + "@trigger.dev/core": "workspace:4.3.0", "lodash.omit": "^4.5.0", "nanoid": "^5.0.7", "p-limit": "^6.2.0", diff --git a/packages/rsc/CHANGELOG.md b/packages/rsc/CHANGELOG.md index 4cedc0a6af..93037c8a0d 100644 --- a/packages/rsc/CHANGELOG.md +++ b/packages/rsc/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/rsc +## 4.3.0 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.3.0` + ## 4.2.0 ### Patch Changes diff --git a/packages/rsc/package.json b/packages/rsc/package.json index 3aa6eba5ba..30f3080291 100644 --- a/packages/rsc/package.json +++ b/packages/rsc/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/rsc", - "version": "4.2.0", + "version": "4.3.0", "description": "trigger.dev rsc", "license": "MIT", "publishConfig": { @@ -37,14 +37,14 @@ "check-exports": "attw --pack ." }, "dependencies": { - "@trigger.dev/core": "workspace:^4.2.0", + "@trigger.dev/core": "workspace:^4.3.0", "mlly": "^1.7.1", "react": "19.0.0-rc.1", "react-dom": "19.0.0-rc.1" }, "devDependencies": { "@arethetypeswrong/cli": "^0.15.4", - "@trigger.dev/build": "workspace:^4.2.0", + "@trigger.dev/build": "workspace:^4.3.0", "@types/node": "^20.14.14", "@types/react": "*", "@types/react-dom": "*", diff --git a/packages/schema-to-json/CHANGELOG.md b/packages/schema-to-json/CHANGELOG.md index a6a6372916..e17cd1a9f1 100644 --- a/packages/schema-to-json/CHANGELOG.md +++ b/packages/schema-to-json/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/schema-to-json +## 4.3.0 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.3.0` + ## 4.2.0 ### Patch Changes diff --git a/packages/schema-to-json/package.json b/packages/schema-to-json/package.json index 610797e9b7..66c0feff7c 100644 --- a/packages/schema-to-json/package.json +++ b/packages/schema-to-json/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/schema-to-json", - "version": "4.2.0", + "version": "4.3.0", "description": "Convert various schema validation libraries to JSON Schema", "license": "MIT", "publishConfig": { diff --git a/packages/trigger-sdk/CHANGELOG.md b/packages/trigger-sdk/CHANGELOG.md index 9f4da3eab3..75231f0c5e 100644 --- a/packages/trigger-sdk/CHANGELOG.md +++ b/packages/trigger-sdk/CHANGELOG.md @@ -1,5 +1,12 @@ # @trigger.dev/sdk +## 4.3.0 + +### Patch Changes + +- Updated dependencies: + - `@trigger.dev/core@4.3.0` + ## 4.2.0 ### Patch Changes diff --git a/packages/trigger-sdk/package.json b/packages/trigger-sdk/package.json index fcd8485444..04aa319f55 100644 --- a/packages/trigger-sdk/package.json +++ b/packages/trigger-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@trigger.dev/sdk", - "version": "4.2.0", + "version": "4.3.0", "description": "trigger.dev Node.JS SDK", "license": "MIT", "publishConfig": { @@ -51,7 +51,7 @@ "dependencies": { "@opentelemetry/api": "1.9.0", "@opentelemetry/semantic-conventions": "1.36.0", - "@trigger.dev/core": "workspace:4.2.0", + "@trigger.dev/core": "workspace:4.3.0", "chalk": "^5.2.0", "cronstrue": "^2.21.0", "debug": "^4.3.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4102c43736..1b42585982 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -419,31 +419,31 @@ importers: version: 3.7.1(react@18.2.0) '@remix-run/express': specifier: 2.1.0 - version: 2.1.0(express@4.20.0)(typescript@5.5.4) + version: 2.1.0(express@4.20.0)(typescript@5.9.3) '@remix-run/node': specifier: 2.1.0 - version: 2.1.0(typescript@5.5.4) + version: 2.1.0(typescript@5.9.3) '@remix-run/react': specifier: 2.1.0 - version: 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) + version: 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) '@remix-run/router': specifier: ^1.15.3 version: 1.15.3 '@remix-run/serve': specifier: 2.1.0 - version: 2.1.0(typescript@5.5.4) + version: 2.1.0(typescript@5.9.3) '@remix-run/server-runtime': specifier: 2.1.0 - version: 2.1.0(typescript@5.5.4) + version: 2.1.0(typescript@5.9.3) '@remix-run/v1-meta': specifier: ^0.1.3 - version: 0.1.3(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4)) + version: 0.1.3(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3)) '@s2-dev/streamstore': specifier: ^0.17.2 - version: 0.17.3(typescript@5.5.4) + version: 0.17.3(typescript@5.9.3) '@sentry/remix': specifier: 9.46.0 - version: 9.46.0(patch_hash=146126b032581925294aaed63ab53ce3f5e0356a755f1763d7a9a76b9846943b)(@remix-run/node@2.1.0(typescript@5.5.4))(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(encoding@0.1.13)(react@18.2.0) + version: 9.46.0(patch_hash=146126b032581925294aaed63ab53ce3f5e0356a755f1763d7a9a76b9846943b)(@remix-run/node@2.1.0(typescript@5.9.3))(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(encoding@0.1.13)(react@18.2.0) '@slack/web-api': specifier: 7.9.1 version: 7.9.1 @@ -515,7 +515,7 @@ importers: version: 1.0.18 class-variance-authority: specifier: ^0.5.2 - version: 0.5.2(typescript@5.5.4) + version: 0.5.2(typescript@5.9.3) clsx: specifier: ^1.2.1 version: 1.2.1 @@ -563,7 +563,7 @@ importers: version: 10.12.11(react-dom@18.2.0(react@18.2.0))(react@18.2.0) graphile-worker: specifier: 0.16.6 - version: 0.16.6(patch_hash=798129c99ed02177430fc90a1fdef800ec94e5fd1d491b931297dc52f4c98ab1)(typescript@5.5.4) + version: 0.16.6(patch_hash=798129c99ed02177430fc90a1fdef800ec94e5fd1d491b931297dc52f4c98ab1)(typescript@5.9.3) humanize-duration: specifier: ^3.27.3 version: 3.27.3 @@ -689,22 +689,22 @@ importers: version: 2.0.1 remix-auth: specifier: ^3.6.0 - version: 3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4)) + version: 3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3)) remix-auth-email-link: specifier: 2.0.2 - version: 2.0.2(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))) + version: 2.0.2(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))) remix-auth-github: specifier: ^1.6.0 - version: 1.6.0(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))) + version: 1.6.0(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))) remix-auth-google: specifier: ^2.0.0 - version: 2.0.0(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))) + version: 2.0.0(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))) remix-typedjson: specifier: 0.3.1 - version: 0.3.1(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(react@18.2.0) + version: 0.3.1(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(react@18.2.0) remix-utils: specifier: ^7.7.0 - version: 7.7.0(@remix-run/node@2.1.0(typescript@5.5.4))(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/router@1.15.3)(crypto-js@4.2.0)(intl-parse-accept-language@1.0.0)(react@18.2.0)(zod@3.25.76) + version: 7.7.0(@remix-run/node@2.1.0(typescript@5.9.3))(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/router@1.15.3)(crypto-js@4.2.0)(intl-parse-accept-language@1.0.0)(react@18.2.0)(zod@3.25.76) seedrandom: specifier: ^3.0.5 version: 3.0.5 @@ -783,13 +783,13 @@ importers: version: link:../../internal-packages/testcontainers '@remix-run/dev': specifier: 2.1.0 - version: 2.1.0(@remix-run/serve@2.1.0(typescript@5.5.4))(@types/node@22.13.9)(bufferutil@4.0.9)(encoding@0.1.13)(lightningcss@1.29.2)(terser@5.44.1)(typescript@5.5.4) + version: 2.1.0(@remix-run/serve@2.1.0(typescript@5.9.3))(@types/node@22.13.9)(bufferutil@4.0.9)(encoding@0.1.13)(lightningcss@1.29.2)(terser@5.44.1)(typescript@5.9.3) '@remix-run/eslint-config': specifier: 2.1.0 - version: 2.1.0(eslint@8.31.0)(react@18.2.0)(typescript@5.5.4) + version: 2.1.0(eslint@8.31.0)(react@18.2.0)(typescript@5.9.3) '@remix-run/testing': specifier: ^2.1.0 - version: 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) + version: 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) '@sentry/cli': specifier: 2.50.2 version: 2.50.2(encoding@0.1.13) @@ -888,10 +888,10 @@ importers: version: 8.5.4 '@typescript-eslint/eslint-plugin': specifier: ^5.59.6 - version: 5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint@8.31.0)(typescript@5.5.4) + version: 5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint@8.31.0)(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^5.59.6 - version: 5.59.6(eslint@8.31.0)(typescript@5.5.4) + version: 5.59.6(eslint@8.31.0)(typescript@5.9.3) autoevals: specifier: ^0.0.130 version: 0.0.130(encoding@0.1.13)(ws@8.12.0(bufferutil@4.0.9)) @@ -918,7 +918,7 @@ importers: version: 8.6.0(eslint@8.31.0) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0) + version: 2.29.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0) eslint-plugin-react-hooks: specifier: ^4.6.2 version: 4.6.2(eslint@8.31.0) @@ -936,7 +936,7 @@ importers: version: 16.0.1(postcss@8.5.6) postcss-loader: specifier: ^8.1.1 - version: 8.1.1(postcss@8.5.6)(typescript@5.5.4)(webpack@5.102.1(@swc/core@1.3.26)(esbuild@0.15.18)) + version: 8.1.1(postcss@8.5.6)(typescript@5.9.3)(webpack@5.102.1(@swc/core@1.3.26)(esbuild@0.15.18)) prettier: specifier: ^2.8.8 version: 2.8.8 @@ -969,7 +969,7 @@ importers: version: 4.20.6 vite-tsconfig-paths: specifier: ^4.0.5 - version: 4.0.5(typescript@5.5.4) + version: 4.0.5(typescript@5.9.3) docs: {} @@ -1297,7 +1297,7 @@ importers: specifier: ^6.10.0 version: 6.19.0(magicast@0.3.5) '@trigger.dev/core': - specifier: workspace:4.2.0 + specifier: workspace:4.3.0 version: link:../core mlly: specifier: ^1.7.1 @@ -1373,13 +1373,13 @@ importers: specifier: ^0.17.6 version: 0.17.6 '@trigger.dev/build': - specifier: workspace:4.2.0 + specifier: workspace:4.3.0 version: link:../build '@trigger.dev/core': - specifier: workspace:4.2.0 + specifier: workspace:4.3.0 version: link:../core '@trigger.dev/schema-to-json': - specifier: workspace:4.2.0 + specifier: workspace:4.3.0 version: link:../schema-to-json ansi-escapes: specifier: ^7.0.0 @@ -1639,7 +1639,7 @@ importers: version: 1.36.0 '@s2-dev/streamstore': specifier: 0.17.3 - version: 0.17.3(typescript@5.5.4) + version: 0.17.3(typescript@5.9.3) dequal: specifier: ^2.0.3 version: 2.0.3 @@ -1715,7 +1715,7 @@ importers: version: 4.0.14 ai: specifier: ^3.4.33 - version: 3.4.33(openai@4.97.0(encoding@0.1.13)(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76))(react@19.1.0)(sswr@2.1.0(svelte@5.43.6))(svelte@5.43.6)(vue@3.5.24(typescript@5.5.4))(zod@3.25.76) + version: 3.4.33(openai@4.97.0(encoding@0.1.13)(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76))(react@19.1.0)(sswr@2.1.0(svelte@5.43.6))(svelte@5.43.6)(vue@3.5.24(typescript@5.9.3))(zod@3.25.76) defu: specifier: ^6.1.4 version: 6.1.4 @@ -1727,7 +1727,7 @@ importers: version: 3.0.2 ts-essentials: specifier: 10.0.1 - version: 10.0.1(typescript@5.5.4) + version: 10.0.1(typescript@5.9.3) tshy: specifier: ^3.0.2 version: 3.0.2 @@ -1738,7 +1738,7 @@ importers: packages/python: dependencies: '@trigger.dev/core': - specifier: workspace:4.2.0 + specifier: workspace:4.3.0 version: link:../core tinyexec: specifier: ^0.3.2 @@ -1748,10 +1748,10 @@ importers: specifier: ^0.15.4 version: 0.15.4 '@trigger.dev/build': - specifier: workspace:4.2.0 + specifier: workspace:4.3.0 version: link:../build '@trigger.dev/sdk': - specifier: workspace:4.2.0 + specifier: workspace:4.3.0 version: link:../trigger-sdk '@types/node': specifier: 20.14.14 @@ -1775,7 +1775,7 @@ importers: packages/react-hooks: dependencies: '@trigger.dev/core': - specifier: workspace:^4.2.0 + specifier: workspace:^4.3.0 version: link:../core react: specifier: ^18.0 || ^19.0 || ^19.0.0-rc @@ -1809,7 +1809,7 @@ importers: packages/redis-worker: dependencies: '@trigger.dev/core': - specifier: workspace:4.2.0 + specifier: workspace:4.3.0 version: link:../core cron-parser: specifier: ^4.9.0 @@ -1852,7 +1852,7 @@ importers: packages/rsc: dependencies: '@trigger.dev/core': - specifier: workspace:^4.2.0 + specifier: workspace:^4.3.0 version: link:../core mlly: specifier: ^1.7.1 @@ -1868,7 +1868,7 @@ importers: specifier: ^0.15.4 version: 0.15.4 '@trigger.dev/build': - specifier: workspace:^4.2.0 + specifier: workspace:^4.3.0 version: link:../build '@types/node': specifier: ^20.14.14 @@ -1944,7 +1944,7 @@ importers: specifier: 1.36.0 version: 1.36.0 '@trigger.dev/core': - specifier: workspace:4.2.0 + specifier: workspace:4.3.0 version: link:../core chalk: specifier: ^5.2.0 @@ -20200,13 +20200,13 @@ snapshots: zod: 3.25.76 zod-to-json-schema: 3.24.6(zod@3.25.76) - '@ai-sdk/vue@0.0.59(vue@3.5.24(typescript@5.5.4))(zod@3.25.76)': + '@ai-sdk/vue@0.0.59(vue@3.5.24(typescript@5.9.3))(zod@3.25.76)': dependencies: '@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.24(typescript@5.5.4)) + swrv: 1.0.4(vue@3.5.24(typescript@5.9.3)) optionalDependencies: - vue: 3.5.24(typescript@5.5.4) + vue: 3.5.24(typescript@5.9.3) transitivePeerDependencies: - zod @@ -28698,7 +28698,7 @@ snapshots: transitivePeerDependencies: - encoding - '@remix-run/dev@2.1.0(@remix-run/serve@2.1.0(typescript@5.5.4))(@types/node@22.13.9)(bufferutil@4.0.9)(encoding@0.1.13)(lightningcss@1.29.2)(terser@5.44.1)(typescript@5.5.4)': + '@remix-run/dev@2.1.0(@remix-run/serve@2.1.0(typescript@5.9.3))(@types/node@22.13.9)(bufferutil@4.0.9)(encoding@0.1.13)(lightningcss@1.29.2)(terser@5.44.1)(typescript@5.9.3)': dependencies: '@babel/core': 7.22.17 '@babel/generator': 7.24.7 @@ -28709,7 +28709,7 @@ snapshots: '@babel/traverse': 7.24.7 '@mdx-js/mdx': 2.3.0 '@npmcli/package-json': 4.0.1 - '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) + '@remix-run/server-runtime': 2.1.0(typescript@5.9.3) '@types/mdx': 2.0.5 '@vanilla-extract/integration': 6.2.1(@types/node@22.13.9)(lightningcss@1.29.2)(terser@5.44.1) arg: 5.0.2 @@ -28749,8 +28749,8 @@ snapshots: tsconfig-paths: 4.2.0 ws: 7.5.9(bufferutil@4.0.9) optionalDependencies: - '@remix-run/serve': 2.1.0(typescript@5.5.4) - typescript: 5.5.4 + '@remix-run/serve': 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - '@types/node' - bluebird @@ -28766,43 +28766,43 @@ snapshots: - ts-node - utf-8-validate - '@remix-run/eslint-config@2.1.0(eslint@8.31.0)(react@18.2.0)(typescript@5.5.4)': + '@remix-run/eslint-config@2.1.0(eslint@8.31.0)(react@18.2.0)(typescript@5.9.3)': dependencies: '@babel/core': 7.22.17 '@babel/eslint-parser': 7.21.8(@babel/core@7.22.17)(eslint@8.31.0) '@babel/preset-react': 7.18.6(@babel/core@7.22.17) '@rushstack/eslint-patch': 1.2.0 - '@typescript-eslint/eslint-plugin': 5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint@8.31.0)(typescript@5.5.4) - '@typescript-eslint/parser': 5.59.6(eslint@8.31.0)(typescript@5.5.4) + '@typescript-eslint/eslint-plugin': 5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint@8.31.0)(typescript@5.9.3) + '@typescript-eslint/parser': 5.59.6(eslint@8.31.0)(typescript@5.9.3) eslint: 8.31.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.29.1)(eslint@8.31.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0) - eslint-plugin-jest: 26.9.0(@typescript-eslint/eslint-plugin@5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint@8.31.0)(typescript@5.5.4))(eslint@8.31.0)(typescript@5.5.4) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.29.1)(eslint@8.31.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0) + eslint-plugin-jest: 26.9.0(@typescript-eslint/eslint-plugin@5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint@8.31.0)(typescript@5.9.3))(eslint@8.31.0)(typescript@5.9.3) eslint-plugin-jest-dom: 4.0.3(eslint@8.31.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.31.0) eslint-plugin-node: 11.1.0(eslint@8.31.0) eslint-plugin-react: 7.32.2(eslint@8.31.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.31.0) - eslint-plugin-testing-library: 5.11.0(eslint@8.31.0)(typescript@5.5.4) + eslint-plugin-testing-library: 5.11.0(eslint@8.31.0)(typescript@5.9.3) react: 18.2.0 optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 transitivePeerDependencies: - eslint-import-resolver-webpack - jest - supports-color - '@remix-run/express@2.1.0(express@4.20.0)(typescript@5.5.4)': + '@remix-run/express@2.1.0(express@4.20.0)(typescript@5.9.3)': dependencies: - '@remix-run/node': 2.1.0(typescript@5.5.4) + '@remix-run/node': 2.1.0(typescript@5.9.3) express: 4.20.0 optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 - '@remix-run/node@2.1.0(typescript@5.5.4)': + '@remix-run/node@2.1.0(typescript@5.9.3)': dependencies: - '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) + '@remix-run/server-runtime': 2.1.0(typescript@5.9.3) '@remix-run/web-fetch': 4.4.1 '@remix-run/web-file': 3.1.0 '@remix-run/web-stream': 1.1.0 @@ -28811,26 +28811,26 @@ snapshots: source-map-support: 0.5.21 stream-slice: 0.1.2 optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 - '@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4)': + '@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)': dependencies: '@remix-run/router': 1.10.0 - '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) + '@remix-run/server-runtime': 2.1.0(typescript@5.9.3) react: 18.2.0 react-dom: 18.2.0(react@18.2.0) react-router-dom: 6.17.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 '@remix-run/router@1.10.0': {} '@remix-run/router@1.15.3': {} - '@remix-run/serve@2.1.0(typescript@5.5.4)': + '@remix-run/serve@2.1.0(typescript@5.9.3)': dependencies: - '@remix-run/express': 2.1.0(express@4.20.0)(typescript@5.5.4) - '@remix-run/node': 2.1.0(typescript@5.5.4) + '@remix-run/express': 2.1.0(express@4.20.0)(typescript@5.9.3) + '@remix-run/node': 2.1.0(typescript@5.9.3) chokidar: 3.6.0 compression: 1.7.4 express: 4.20.0 @@ -28841,7 +28841,7 @@ snapshots: - supports-color - typescript - '@remix-run/server-runtime@2.1.0(typescript@5.5.4)': + '@remix-run/server-runtime@2.1.0(typescript@5.9.3)': dependencies: '@remix-run/router': 1.10.0 '@types/cookie': 0.4.1 @@ -28850,24 +28850,24 @@ snapshots: set-cookie-parser: 2.6.0 source-map: 0.7.4 optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 - '@remix-run/testing@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4)': + '@remix-run/testing@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3)': dependencies: - '@remix-run/node': 2.1.0(typescript@5.5.4) - '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) + '@remix-run/node': 2.1.0(typescript@5.9.3) + '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) '@remix-run/router': 1.10.0 react: 18.2.0 react-router-dom: 6.17.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 transitivePeerDependencies: - react-dom - '@remix-run/v1-meta@0.1.3(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))': + '@remix-run/v1-meta@0.1.3(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))': dependencies: - '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) - '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) + '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) + '@remix-run/server-runtime': 2.1.0(typescript@5.9.3) '@remix-run/web-blob@3.1.0': dependencies: @@ -28962,10 +28962,10 @@ snapshots: '@rushstack/eslint-patch@1.2.0': {} - '@s2-dev/streamstore@0.17.3(typescript@5.5.4)': + '@s2-dev/streamstore@0.17.3(typescript@5.9.3)': dependencies: '@protobuf-ts/runtime': 2.11.1 - typescript: 5.5.4 + typescript: 5.9.3 '@s2-dev/streamstore@0.17.6': dependencies: @@ -29119,15 +29119,15 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 18.2.0 - '@sentry/remix@9.46.0(patch_hash=146126b032581925294aaed63ab53ce3f5e0356a755f1763d7a9a76b9846943b)(@remix-run/node@2.1.0(typescript@5.5.4))(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(encoding@0.1.13)(react@18.2.0)': + '@sentry/remix@9.46.0(patch_hash=146126b032581925294aaed63ab53ce3f5e0356a755f1763d7a9a76b9846943b)(@remix-run/node@2.1.0(typescript@5.9.3))(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(encoding@0.1.13)(react@18.2.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 - '@remix-run/node': 2.1.0(typescript@5.5.4) - '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) + '@remix-run/node': 2.1.0(typescript@5.9.3) + '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) '@remix-run/router': 1.15.3 - '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) + '@remix-run/server-runtime': 2.1.0(typescript@5.9.3) '@sentry/cli': 2.50.2(encoding@0.1.13) '@sentry/core': 9.46.0 '@sentry/node': 9.46.0 @@ -31032,34 +31032,34 @@ snapshots: '@types/node': 20.14.14 optional: true - '@typescript-eslint/eslint-plugin@5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint@8.31.0)(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint@8.31.0)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.5.1 - '@typescript-eslint/parser': 5.59.6(eslint@8.31.0)(typescript@5.5.4) + '@typescript-eslint/parser': 5.59.6(eslint@8.31.0)(typescript@5.9.3) '@typescript-eslint/scope-manager': 5.59.6 - '@typescript-eslint/type-utils': 5.59.6(eslint@8.31.0)(typescript@5.5.4) - '@typescript-eslint/utils': 5.59.6(eslint@8.31.0)(typescript@5.5.4) + '@typescript-eslint/type-utils': 5.59.6(eslint@8.31.0)(typescript@5.9.3) + '@typescript-eslint/utils': 5.59.6(eslint@8.31.0)(typescript@5.9.3) debug: 4.3.4 eslint: 8.31.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 natural-compare-lite: 1.4.0 semver: 7.6.3 - tsutils: 3.21.0(typescript@5.5.4) + tsutils: 3.21.0(typescript@5.9.3) optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4)': + '@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3)': dependencies: '@typescript-eslint/scope-manager': 5.59.6 '@typescript-eslint/types': 5.59.6 - '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.9.3) debug: 4.4.0 eslint: 8.31.0 optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -31068,21 +31068,21 @@ snapshots: '@typescript-eslint/types': 5.59.6 '@typescript-eslint/visitor-keys': 5.59.6 - '@typescript-eslint/type-utils@5.59.6(eslint@8.31.0)(typescript@5.5.4)': + '@typescript-eslint/type-utils@5.59.6(eslint@8.31.0)(typescript@5.9.3)': 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) + '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.9.3) + '@typescript-eslint/utils': 5.59.6(eslint@8.31.0)(typescript@5.9.3) debug: 4.4.0 eslint: 8.31.0 - tsutils: 3.21.0(typescript@5.5.4) + tsutils: 3.21.0(typescript@5.9.3) optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 transitivePeerDependencies: - supports-color '@typescript-eslint/types@5.59.6': {} - '@typescript-eslint/typescript-estree@5.59.6(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@5.59.6(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 5.59.6 '@typescript-eslint/visitor-keys': 5.59.6 @@ -31090,20 +31090,20 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 semver: 7.7.2 - tsutils: 3.21.0(typescript@5.5.4) + tsutils: 3.21.0(typescript@5.9.3) optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@5.59.6(eslint@8.31.0)(typescript@5.5.4)': + '@typescript-eslint/utils@5.59.6(eslint@8.31.0)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.31.0) '@types/json-schema': 7.0.13 '@types/semver': 7.5.1 '@typescript-eslint/scope-manager': 5.59.6 '@typescript-eslint/types': 5.59.6 - '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 5.59.6(typescript@5.9.3) eslint: 8.31.0 eslint-scope: 5.1.1 semver: 7.7.2 @@ -31370,11 +31370,11 @@ snapshots: '@vue/shared': 3.5.24 csstype: 3.2.0 - '@vue/server-renderer@3.5.24(vue@3.5.24(typescript@5.5.4))': + '@vue/server-renderer@3.5.24(vue@3.5.24(typescript@5.9.3))': dependencies: '@vue/compiler-ssr': 3.5.24 '@vue/shared': 3.5.24 - vue: 3.5.24(typescript@5.5.4) + vue: 3.5.24(typescript@5.9.3) '@vue/shared@3.5.24': {} @@ -31661,7 +31661,7 @@ snapshots: ahocorasick@1.0.2: {} - ai@3.4.33(openai@4.97.0(encoding@0.1.13)(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76))(react@19.1.0)(sswr@2.1.0(svelte@5.43.6))(svelte@5.43.6)(vue@3.5.24(typescript@5.5.4))(zod@3.25.76): + ai@3.4.33(openai@4.97.0(encoding@0.1.13)(ws@8.18.3(bufferutil@4.0.9))(zod@3.25.76))(react@19.1.0)(sswr@2.1.0(svelte@5.43.6))(svelte@5.43.6)(vue@3.5.24(typescript@5.9.3))(zod@3.25.76): dependencies: '@ai-sdk/provider': 0.0.26 '@ai-sdk/provider-utils': 1.0.22(zod@3.25.76) @@ -31669,7 +31669,7 @@ snapshots: '@ai-sdk/solid': 0.0.54(zod@3.25.76) '@ai-sdk/svelte': 0.0.57(svelte@5.43.6)(zod@3.25.76) '@ai-sdk/ui-utils': 0.0.50(zod@3.25.76) - '@ai-sdk/vue': 0.0.59(vue@3.5.24(typescript@5.5.4))(zod@3.25.76) + '@ai-sdk/vue': 0.0.59(vue@3.5.24(typescript@5.9.3))(zod@3.25.76) '@opentelemetry/api': 1.9.0 eventsource-parser: 1.1.2 json-schema: 0.4.0 @@ -32495,9 +32495,9 @@ snapshots: cjs-module-lexer@1.2.3: {} - class-variance-authority@0.5.2(typescript@5.5.4): + class-variance-authority@0.5.2(typescript@5.9.3): optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 class-variance-authority@0.7.0: dependencies: @@ -32747,15 +32747,6 @@ snapshots: dependencies: layout-base: 2.0.1 - cosmiconfig@8.3.6(typescript@5.5.4): - dependencies: - import-fresh: 3.3.0 - js-yaml: 4.1.1 - parse-json: 5.2.0 - path-type: 4.0.0 - optionalDependencies: - typescript: 5.5.4 - cosmiconfig@8.3.6(typescript@5.9.3): dependencies: import-fresh: 3.3.0 @@ -32765,14 +32756,14 @@ snapshots: optionalDependencies: typescript: 5.9.3 - cosmiconfig@9.0.0(typescript@5.5.4): + cosmiconfig@9.0.0(typescript@5.9.3): dependencies: env-paths: 2.2.1 import-fresh: 3.3.0 js-yaml: 4.1.1 parse-json: 5.2.0 optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 cp-file@10.0.0: dependencies: @@ -33969,13 +33960,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.29.1)(eslint@8.31.0): + eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.29.1)(eslint@8.31.0): dependencies: 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@8.31.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0) + eslint-module-utils: 2.7.4(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0) get-tsconfig: 4.7.2 globby: 13.2.2 is-core-module: 2.14.0 @@ -33987,25 +33978,25 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.7.4(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0): + eslint-module-utils@2.7.4(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 5.59.6(eslint@8.31.0)(typescript@5.5.4) + '@typescript-eslint/parser': 5.59.6(eslint@8.31.0)(typescript@5.9.3) eslint: 8.31.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.29.1)(eslint@8.31.0) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.29.1)(eslint@8.31.0) transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 5.59.6(eslint@8.31.0)(typescript@5.5.4) + '@typescript-eslint/parser': 5.59.6(eslint@8.31.0)(typescript@5.9.3) eslint: 8.31.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.29.1)(eslint@8.31.0) + eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.29.1)(eslint@8.31.0) transitivePeerDependencies: - supports-color @@ -34015,7 +34006,7 @@ snapshots: eslint-utils: 2.1.0 regexpp: 3.2.0 - eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -34025,7 +34016,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.31.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.31.0) hasown: 2.0.2 is-core-module: 2.14.0 is-glob: 4.0.3 @@ -34036,7 +34027,7 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 5.59.6(eslint@8.31.0)(typescript@5.5.4) + '@typescript-eslint/parser': 5.59.6(eslint@8.31.0)(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -34049,12 +34040,12 @@ snapshots: eslint: 8.31.0 requireindex: 1.2.0 - eslint-plugin-jest@26.9.0(@typescript-eslint/eslint-plugin@5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint@8.31.0)(typescript@5.5.4))(eslint@8.31.0)(typescript@5.5.4): + eslint-plugin-jest@26.9.0(@typescript-eslint/eslint-plugin@5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint@8.31.0)(typescript@5.9.3))(eslint@8.31.0)(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 5.59.6(eslint@8.31.0)(typescript@5.5.4) + '@typescript-eslint/utils': 5.59.6(eslint@8.31.0)(typescript@5.9.3) eslint: 8.31.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.5.4))(eslint@8.31.0)(typescript@5.5.4) + '@typescript-eslint/eslint-plugin': 5.59.6(@typescript-eslint/parser@5.59.6(eslint@8.31.0)(typescript@5.9.3))(eslint@8.31.0)(typescript@5.9.3) transitivePeerDependencies: - supports-color - typescript @@ -34112,9 +34103,9 @@ snapshots: semver: 6.3.1 string.prototype.matchall: 4.0.8 - eslint-plugin-testing-library@5.11.0(eslint@8.31.0)(typescript@5.5.4): + eslint-plugin-testing-library@5.11.0(eslint@8.31.0)(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 5.59.6(eslint@8.31.0)(typescript@5.5.4) + '@typescript-eslint/utils': 5.59.6(eslint@8.31.0)(typescript@5.9.3) eslint: 8.31.0 transitivePeerDependencies: - supports-color @@ -35042,22 +35033,6 @@ snapshots: transitivePeerDependencies: - supports-color - graphile-worker@0.16.6(patch_hash=798129c99ed02177430fc90a1fdef800ec94e5fd1d491b931297dc52f4c98ab1)(typescript@5.5.4): - dependencies: - '@graphile/logger': 0.2.0 - '@types/debug': 4.1.12 - '@types/pg': 8.11.6 - cosmiconfig: 8.3.6(typescript@5.5.4) - graphile-config: 0.0.1-beta.8 - json5: 2.2.3 - pg: 8.11.5 - tslib: 2.6.2 - yargs: 17.7.2 - transitivePeerDependencies: - - pg-native - - supports-color - - typescript - graphile-worker@0.16.6(patch_hash=798129c99ed02177430fc90a1fdef800ec94e5fd1d491b931297dc52f4c98ab1)(typescript@5.9.3): dependencies: '@graphile/logger': 0.2.0 @@ -38284,9 +38259,9 @@ snapshots: tsx: 4.17.0 yaml: 2.7.1 - postcss-loader@8.1.1(postcss@8.5.6)(typescript@5.5.4)(webpack@5.102.1(@swc/core@1.3.26)(esbuild@0.15.18)): + postcss-loader@8.1.1(postcss@8.5.6)(typescript@5.9.3)(webpack@5.102.1(@swc/core@1.3.26)(esbuild@0.15.18)): dependencies: - cosmiconfig: 9.0.0(typescript@5.5.4) + cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 1.21.0 postcss: 8.5.6 semver: 7.6.3 @@ -39436,54 +39411,54 @@ snapshots: mdast-util-to-markdown: 2.1.2 unified: 11.0.5 - remix-auth-email-link@2.0.2(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))): + remix-auth-email-link@2.0.2(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))): dependencies: - '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) + '@remix-run/server-runtime': 2.1.0(typescript@5.9.3) crypto-js: 4.1.1 - remix-auth: 3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4)) + remix-auth: 3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3)) - remix-auth-github@1.6.0(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))): + remix-auth-github@1.6.0(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))): dependencies: - '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) - remix-auth: 3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4)) - remix-auth-oauth2: 1.11.0(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))) + '@remix-run/server-runtime': 2.1.0(typescript@5.9.3) + remix-auth: 3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3)) + remix-auth-oauth2: 1.11.0(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))) transitivePeerDependencies: - supports-color - remix-auth-google@2.0.0(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))): + remix-auth-google@2.0.0(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))): dependencies: - '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) - remix-auth: 3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4)) - remix-auth-oauth2: 1.11.0(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))) + '@remix-run/server-runtime': 2.1.0(typescript@5.9.3) + remix-auth: 3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3)) + remix-auth-oauth2: 1.11.0(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))) transitivePeerDependencies: - supports-color - remix-auth-oauth2@1.11.0(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))): + remix-auth-oauth2@1.11.0(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))): dependencies: - '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) + '@remix-run/server-runtime': 2.1.0(typescript@5.9.3) debug: 4.4.1(supports-color@10.0.0) - remix-auth: 3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4)) + remix-auth: 3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3)) transitivePeerDependencies: - supports-color - remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4)): + remix-auth@3.6.0(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3)): dependencies: - '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) - '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) + '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) + '@remix-run/server-runtime': 2.1.0(typescript@5.9.3) uuid: 8.3.2 - remix-typedjson@0.3.1(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/server-runtime@2.1.0(typescript@5.5.4))(react@18.2.0): + remix-typedjson@0.3.1(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/server-runtime@2.1.0(typescript@5.9.3))(react@18.2.0): dependencies: - '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) - '@remix-run/server-runtime': 2.1.0(typescript@5.5.4) + '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) + '@remix-run/server-runtime': 2.1.0(typescript@5.9.3) react: 18.2.0 - remix-utils@7.7.0(@remix-run/node@2.1.0(typescript@5.5.4))(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4))(@remix-run/router@1.15.3)(crypto-js@4.2.0)(intl-parse-accept-language@1.0.0)(react@18.2.0)(zod@3.25.76): + remix-utils@7.7.0(@remix-run/node@2.1.0(typescript@5.9.3))(@remix-run/react@2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3))(@remix-run/router@1.15.3)(crypto-js@4.2.0)(intl-parse-accept-language@1.0.0)(react@18.2.0)(zod@3.25.76): dependencies: type-fest: 4.33.0 optionalDependencies: - '@remix-run/node': 2.1.0(typescript@5.5.4) - '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.5.4) + '@remix-run/node': 2.1.0(typescript@5.9.3) + '@remix-run/react': 2.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.9.3) '@remix-run/router': 1.15.3 crypto-js: 4.2.0 intl-parse-accept-language: 1.0.0 @@ -40559,9 +40534,9 @@ snapshots: swrev@4.0.0: {} - swrv@1.0.4(vue@3.5.24(typescript@5.5.4)): + swrv@1.0.4(vue@3.5.24(typescript@5.9.3)): dependencies: - vue: 3.5.24(typescript@5.5.4) + vue: 3.5.24(typescript@5.9.3) sync-content@2.0.1: dependencies: @@ -40998,10 +40973,6 @@ snapshots: ts-easing@0.2.0: {} - ts-essentials@10.0.1(typescript@5.5.4): - optionalDependencies: - typescript: 5.5.4 - ts-essentials@10.0.1(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 @@ -41032,6 +41003,10 @@ snapshots: optionalDependencies: typescript: 5.5.4 + tsconfck@2.1.2(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + tsconfck@3.1.3(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 @@ -41108,10 +41083,10 @@ snapshots: - tsx - yaml - tsutils@3.21.0(typescript@5.5.4): + tsutils@3.21.0(typescript@5.9.3): dependencies: tslib: 1.14.1 - typescript: 5.5.4 + typescript: 5.9.3 tsx@3.12.2: dependencies: @@ -41273,8 +41248,7 @@ snapshots: typescript@5.5.4: {} - typescript@5.9.3: - optional: true + typescript@5.9.3: {} ufo@1.5.4: {} @@ -41675,6 +41649,15 @@ snapshots: - supports-color - typescript + vite-tsconfig-paths@4.0.5(typescript@5.9.3): + dependencies: + debug: 4.3.7(supports-color@10.0.0) + globrex: 0.1.2 + tsconfck: 2.1.2(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + - typescript + vite@4.4.9(@types/node@22.13.9)(lightningcss@1.29.2)(terser@5.44.1): dependencies: esbuild: 0.18.11 @@ -41751,15 +41734,15 @@ snapshots: vscode-uri@3.0.8: {} - vue@3.5.24(typescript@5.5.4): + vue@3.5.24(typescript@5.9.3): dependencies: '@vue/compiler-dom': 3.5.24 '@vue/compiler-sfc': 3.5.24 '@vue/runtime-dom': 3.5.24 - '@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@5.5.4)) + '@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@5.9.3)) '@vue/shared': 3.5.24 optionalDependencies: - typescript: 5.5.4 + typescript: 5.9.3 w3c-keyname@2.2.6: {} From ce5aef08cd83305989879ced573761da7ab9af12 Mon Sep 17 00:00:00 2001 From: Oskar Otwinowski Date: Fri, 12 Dec 2025 12:16:29 +0000 Subject: [PATCH 08/13] fix(ui): respect rootOnlyDefault, disable adjacent run navigation in inspector views (#2781) ## Changelog - Add disableAdjacentRows prop to TaskRunsTable component to control table state encoding - Pass rootOnlyDefault prop from loader to TaskRunsTable for proper state management - Disable adjacent run navigation in schedule, waitpoint, and other inspector views - Preserve adjacent run navigation on main runs list page with rootOnly filter support --- apps/webapp/app/components/runs/v3/TaskRunsTable.tsx | 11 ++++++++++- .../route.tsx | 1 + .../route.tsx | 1 + .../route.tsx | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx b/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx index c0ae8d2f62..14cdf5a67b 100644 --- a/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx +++ b/apps/webapp/app/components/runs/v3/TaskRunsTable.tsx @@ -56,6 +56,7 @@ import { TaskRunStatusCombo, } from "./TaskRunStatus"; import { useOptimisticLocation } from "~/hooks/useOptimisticLocation"; +import { useSearchParams } from "~/hooks/useSearchParam"; type RunsTableProps = { total: number; @@ -63,9 +64,11 @@ type RunsTableProps = { filters: NextRunListAppliedFilters; showJob?: boolean; runs: NextRunListItem[]; + rootOnlyDefault?: boolean; isLoading?: boolean; allowSelection?: boolean; variant?: TableVariant; + disableAdjacentRows?: boolean; }; export function TaskRunsTable({ @@ -73,6 +76,8 @@ export function TaskRunsTable({ hasFilters, filters, runs, + rootOnlyDefault, + disableAdjacentRows = false, isLoading = false, allowSelection = false, variant = "dimmed", @@ -82,8 +87,12 @@ export function TaskRunsTable({ const checkboxes = useRef<(HTMLInputElement | null)[]>([]); const { has, hasAll, select, deselect, toggle } = useSelectedItems(allowSelection); const { isManagedCloud } = useFeatures(); + const { value } = useSearchParams(); const location = useOptimisticLocation(); - const tableStateParam = encodeURIComponent(location.search ? `${location.search}&rt=1` : "rt=1"); + const rootOnly = value("rootOnly") ? `` : `rootOnly=${rootOnlyDefault}`; + const search = rootOnly ? `${rootOnly}&${location.search}` : location.search; + /** TableState has to be encoded as a separate URI component, so it's merged under one, 'tableState' param */ + const tableStateParam = disableAdjacentRows ? '' : encodeURIComponent(search); const showCompute = isManagedCloud; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index/route.tsx index 8f80124707..9f8cf278be 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs._index/route.tsx @@ -298,6 +298,7 @@ function RunsList({ runs={list.runs} isLoading={isLoading} allowSelection + rootOnlyDefault={rootOnlyDefault} />
)} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx index a11a2a7f9d..f4e663b1b7 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx @@ -318,6 +318,7 @@ export default function Page() { runs={schedule.runs} isLoading={false} variant="bright" + disableAdjacentRows />
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.tokens.$waitpointParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.tokens.$waitpointParam/route.tsx index 263baed23d..48b842d34d 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.tokens.$waitpointParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.tokens.$waitpointParam/route.tsx @@ -126,6 +126,7 @@ export default function Page() { runs={waitpoint.connectedRuns} isLoading={false} variant="bright" + disableAdjacentRows />
From f961f1ad4aac5fe44c65dc879fa170aaa1847c41 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 12:53:57 +0000 Subject: [PATCH 09/13] Initial plan From 8e4db5b4cb35241e9bb05ff9820a4a1afcf91638 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 12:59:14 +0000 Subject: [PATCH 10/13] Fix form submit order: confirmButton now defaults on Enter Co-authored-by: 0ski <1408674+0ski@users.noreply.github.com> --- apps/webapp/app/components/primitives/FormButtons.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/components/primitives/FormButtons.tsx b/apps/webapp/app/components/primitives/FormButtons.tsx index e7d6db75d2..d077227a2d 100644 --- a/apps/webapp/app/components/primitives/FormButtons.tsx +++ b/apps/webapp/app/components/primitives/FormButtons.tsx @@ -12,11 +12,11 @@ export function FormButtons({ return (
- {cancelButton ? cancelButton :
} {confirmButton} + {confirmButton} {cancelButton ? cancelButton :
}
); } From f17de1e59f76368f034c03158aaff2a1900b290d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 14:23:05 +0000 Subject: [PATCH 11/13] Use keypress handler instead of reordering DOM elements Co-authored-by: 0ski <1408674+0ski@users.noreply.github.com> --- .../app/components/primitives/FormButtons.tsx | 4 ++-- .../route.tsx | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/webapp/app/components/primitives/FormButtons.tsx b/apps/webapp/app/components/primitives/FormButtons.tsx index d077227a2d..e7d6db75d2 100644 --- a/apps/webapp/app/components/primitives/FormButtons.tsx +++ b/apps/webapp/app/components/primitives/FormButtons.tsx @@ -12,11 +12,11 @@ export function FormButtons({ return (
- {confirmButton} {cancelButton ? cancelButton :
} + {cancelButton ? cancelButton :
} {confirmButton}
); } diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx index 0c069f31e6..b3c6d3bfe8 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx @@ -953,6 +953,18 @@ function QueueOverrideConcurrencyButton({ navigation.formData?.get("action") === "queue-remove-override" ); + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey && !e.ctrlKey && !e.metaKey && !e.altKey) { + e.preventDefault(); + const overrideButton = e.currentTarget.querySelector( + 'button[name="action"][value="queue-override"]' + ) as HTMLButtonElement; + if (overrideButton && !overrideButton.disabled) { + overrideButton.click(); + } + } + }; + return ( @@ -983,7 +995,12 @@ function QueueOverrideConcurrencyButton({ set {queue.concurrencyLimit !== null ? "in code" : "by the environment"}. )} -
setIsOpen(false)} className="space-y-3"> + setIsOpen(false)} + onKeyDown={handleKeyDown} + className="space-y-3" + >