nicktrn b082e44389 fix(webapp): write-path and appearance-control fixes for the theme work (#4756)
Fixes found while reviewing #4547, stacked on that branch so they can be
reviewed on their own and merged into it. One commit per fix.

## Write-path correctness

**Refuse account writes while impersonating.** The five
`dashboardPreferences` writers already no-op for an impersonating admin,
but the three profile writers added next to them did not, and
`requireUserId` returns the impersonated user's id. Both gates now
refuse up front and say so, rather than the preference writers silently
no-opping while the page reports success.

**Preserve unknown keys on a full-blob write.**
`mutateDashboardPreferences` parses the JSON column, hands the result to
a mutator and persists the whole object back. zod strips keys it does
not declare, so a deploy that predates a preference field drops it on
the next write through that path — and
`updateCurrentProjectEnvironmentId` sits on the navigation hot path.
`preserveUnknownKeys` re-attaches them at the write. Note this cannot
help deploys already running, so it makes this the last release able to
strip rather than retroactively protecting the fields added in #4547.

**Scope hidden-sidebar writes to what was shown.** The customize dialog
builds its hidden map from the sections it can see and the write
replaced `hiddenItems` wholesale. The profile page has no org in scope,
so it resolves sections from the most-recently-updated project's org:
confirming there dropped hidden ids belonging to sections that org's
flags exclude. The payload now carries the ids the dialog rendered and
the write only replaces those. Submissions without the list stay
authoritative.

**Consider both addresses when checking email ownership.** The check
only looked at the address the user already had; it now considers the
current and submitted address together, so an org managing either one
governs the change. Validation moved ahead of the check, and
`emailDomainOf` splits on the last `@`.

## Interaction

**Revert unsaved themes, debounce contrast saves.** The theme and
system-theme selects stamp `data-theme` before the write lands. When it
fails, the loader returns the value it always had — so
`useSystemThemeSync`'s effect deps are unchanged and React's vdom diff
sees no change either, and nothing rewrites the attribute. The page kept
rendering a theme that was never stored while the select showed the
stored one. The stored pair is now re-applied explicitly, as the side
menu's switcher already did. The contrast slider is debounced because
Radix commits on every arrow keypress, so a keyboard user crossing the
range fired one write per step.

**Tick More options for themes outside the short list.** The appearance
submenu offers System, Light and Dark; Black and White live on the
profile page. With one of those stored, every row read as unselected.

## Subtraction

**Drop the profile update rate limiter.** It covered one of four paths
that write the same column — `resources.preferences.sidemenu` and
`.favorites` take unlimited authenticated writes and go through the
locked read-modify-write, which is more expensive than the single narrow
`jsonb_set` this capped. It was also what made the contrast slider
unusable by keyboard. If preference writes want limiting, it belongs in
one place covering all of them.

**Resolve email ownership when the dialog opens.** It fans out one SSO
status lookup per organization the user belongs to and ran in the
profile loader on every page view, purely to pick which body the dialog
renders. The action re-derives it before writing either way, so the
check that guards the write now has one call site instead of two.

## Testing

`typecheck --filter webapp` and `lint` clean. New unit tests for
`preserveUnknownKeys`, `mergeHiddenItems` and `emailDomainOf`;
`themePreference`, `mergeHiddenItems` and `ssoManagedIdentity` suites
pass locally (26 tests). The rest of the webapp suite needs
testcontainers and is left to CI.

No changeset or `.server-changes` entry: everything here fixes code on
the parent branch that has not shipped. The one exception worth a
maintainer's call is `mergeHiddenItems`, which also touches the side
menu's own customize path.
2026-08-21 19:27:52 +01:00
2026-08-20 12:47:22 +01:00
2026-08-21 09:28:27 +00:00
2026-08-20 12:47:22 +01:00
2025-08-27 16:52:58 +01:00
2026-08-18 14:59:46 +01:00
2025-12-23 18:37:19 +00:00
2026-07-08 17:23:18 +01:00

Trigger.dev logo

Build and deploy fullymanaged AI agents and workflows

Website | Docs | Issues | Example projects | Feature requests | Public roadmap | Self-hosting

Open Source License npm SDK downloads

Twitter Follow Discord Ask DeepWiki GitHub stars

About Trigger.dev

Trigger.dev is the open-source platform for building AI workflows in TypeScript. Long-running tasks with retries, queues, observability, and elastic scaling.

The platform designed for building AI agents

Build AI agents using all the frameworks, services and LLMs you're used to, deploy them to Trigger.dev and get durable, long-running tasks with retries, queues, observability, and elastic scaling out of the box.

  • Long-running without timeouts: Execute your tasks with absolutely no timeouts, unlike AWS Lambda, Vercel, and other serverless platforms.

  • Durability, retries & queues: Build rock solid agents and AI applications using our durable tasks, retries, queues and idempotency.

  • True runtime freedom: Customize your deployed tasks with system packages run browsers, Python scripts, FFmpeg and more.

  • Human-in-the-loop: Programmatically pause your tasks until a human can approve, reject or give feedback.

  • Realtime apps & streaming: Move your background jobs to the foreground by subscribing to runs or streaming AI responses to your app.

  • Observability & monitoring: Each run has full tracing and logs. Configure error alerts to catch bugs fast.

Key features:

  • JavaScript and TypeScript SDK - Build background tasks using familiar programming models
  • Long-running tasks - Handle resource-heavy tasks without timeouts
  • Durable cron schedules - Create and attach recurring schedules of up to a year
  • Trigger.dev Realtime - Trigger, subscribe to, and get real-time updates for runs, with LLM streaming support
  • Build extensions - Hook directly into the build system and customize the build process. Run Python scripts, FFmpeg, browsers, and more.
  • React hooks - Interact with the Trigger.dev API on your frontend using our React hooks package
  • Batch triggering - Use batchTrigger() to initiate multiple runs of a task with custom payloads and options
  • Structured inputs / outputs - Define precise data schemas for your tasks with runtime payload validation
  • Waits - Add waits to your tasks to pause execution for a specified duration
  • Preview branches - Create isolated environments for testing and development. Integrates with Vercel and git workflows
  • Waitpoints - Add human-in-the-loop judgment at critical decision points without disrupting workflow
  • Concurrency & queues - Set concurrency rules to manage how multiple tasks execute
  • Multiple environments - Support for DEV, PREVIEW, STAGING, and PROD environments
  • No infrastructure to manage - Auto-scaling infrastructure that eliminates timeouts and server management
  • Automatic retries - If your task encounters an uncaught error, we automatically attempt to run it again
  • Checkpointing - Tasks are inherently durable, thanks to our checkpointing feature
  • Versioning - Atomic versioning allows you to deploy new versions without affecting running tasks
  • Machines - Configure the number of vCPUs and GBs of RAM you want the task to use
  • Observability & monitoring - Monitor every aspect of your tasks' performance with comprehensive logging and visualization tools
  • Logging & tracing - Comprehensive logging and tracing for all your tasks
  • Tags - Attach up to ten tags to each run, allowing you to filter via the dashboard, realtime, and the SDK
  • Run metadata - Attach metadata to runs which updates as the run progresses and is available to use in your frontend for live updates
  • Bulk actions - Perform actions on multiple runs simultaneously, including replaying and cancelling
  • Real-time alerts - Choose your preferred notification method for run failures and deployments

Write tasks in your codebase

Create tasks where they belong: in your codebase. Version control, localhost, test and review like you're already used to.

import { task } from "@trigger.dev/sdk";

//1. You need to export each task
export const helloWorld = task({
  //2. Use a unique id for each task
  id: "hello-world",
  //3. The run function is the main function of the task
  run: async (payload: { message: string }) => {
    //4. You can write code that runs for a long time here, there are no timeouts
    console.log(payload.message);
  },
});

Deployment

Use our SDK to write tasks in your codebase. There's no infrastructure to manage, your tasks automatically scale and connect to our cloud. Or you can always self-host.

Environments

We support Development, Staging, Preview, and Production environments, allowing you to test your tasks before deploying them to production.

Full visibility of every job run

View every task in every run so you can tell exactly what happened. We provide a full trace view of every task run so you can see what happened at every step.

Trace view image

Getting started

The quickest way to get started is to create an account and project in our web app, and follow the instructions in the onboarding. Build and deploy your first task in minutes.

Self-hosting

If you prefer to self-host Trigger.dev, you can follow our self-hosting guides:

Support and community

We have a large active community in our official Discord server for support, including a dedicated channel for self-hosting.

Development

To setup and develop locally or contribute to the open source project, follow our development guide.

Meet the Amazing People Behind This Project:

S
Description
Trigger.dev 支持构建和部署完全托管的 AI Agent 与工作流。|GitHub 镜像 16.1k · 🍴 1.4k
https://github.com/triggerdotdev/trigger.dev Readme Apache-2.0 198 MiB
Languages
TypeScript 99%
JavaScript 0.4%
Shell 0.2%
CSS 0.1%