Await invalidations after mutations - #3354
Draft
david-crespo wants to merge 1 commit into
Draft
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
david-crespo
force-pushed
the
prototype-mutation-invalidations
branch
from
August 27, 2026 00:03
308dbf8 to
c75ba35
Compare
david-crespo
added a commit
that referenced
this pull request
Aug 27, 2026
Spun off from #3354, where I wanted `Promise.withResolvers` in a test but tsc wouldn't let me. Then I found out our browserslist list setting is not being used by anything — autoprefixer went away with Tailwind v4. ### Changes - `target` in tsconfig: es2023 → es2024, so tsc accepts ES2024 builtins like `Promise.withResolvers` - Set `build.target: 'es2024'` in the Vite config to match. Vite's default target is [`'baseline-widely-available'`](https://vite.dev/config/build-options.html#build-target), a browser list regenerated for each Vite major (currently Chrome 111 / Firefox 114 / Safari 16.4) - Add a safety test asserting the two targets stay equal - Remove the browserslist key and the autoprefixer dep ### Argument `target` in tsconfig can control two things: what syntax tsc emits and the default `lib` declarations for typechecking. We use `noEmit` because we compile with Vite, so we only care about `lib`: it determines which builtin APIs the typechecker allows (`Promise.withResolvers`, `Object.groupBy`, etc.). Vite has its own target concept, and it [only affects syntax](https://oxc.rs/docs/guide/usage/transformer/lowering#warnings): it rewrites newer syntax for older browsers, but if you call a too-new API, it doesn't polyfill or warn about it. 🤖 confirmed this by building `Promise.withResolvers()` with target `safari16.4`: clean build, the call ships as-is, TypeError at runtime. That means tsc's `lib` is the only thing keeping unsupported APIs out of the code, so the tsconfig target has to line up with the browsers we actually support. This PR sets `build.target: 'es2024'`, which Vite maps to the oldest browsers with full ES2024 support (Chrome 119, Safari 17.4, Firefox 145, per https://caniuse.com/sr-es15), and adds a safety test asserting the two targets are the same. The potential downside is that we are moving our target to browsers that might be too new. The worst one is Firefox, where full ES2024 support means 145 (Nov 2025). But that version requirement comes from a single feature that we're never going to call: [per MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/waitAsync#browser_compatibility), `Atomics.waitAsync` appeared in Firefox 145, while every other ES2024 feature was in by Firefox 128 (July 2024), and the ones we'd actually use (`groupBy`, `withResolvers`, `isWellFormed`) by 121 (Dec 2023). ### Alternatives * Do nothing (but still remove useless browserslist), i.e., leave `es2023` for TS and `baseline-widely-available` for Vite. Fine, but `es2024` has fun APIs we could use. * `es2024` for TS but leave Vite on the default. Also most likely fine in practice, but feels bad.
david-crespo
force-pushed
the
prototype-mutation-invalidations
branch
from
August 27, 2026 21:57
c75ba35 to
1bd1f85
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Would close #3083. Experimenting with this while investigating a CI e2e flake on the silo IP pools tab. Leaving as a draft because I need to make sure I like it and I don't know about these tests.
Basically there are things in the UI that should wait for the invalidation after a mutation to be done, but currently we don't await
invalidateEndpointcalls anywhere. It's hard for users to have a real problem with this because they don't click fast enough to beat the async invalidation, but it affects tests, and it also means that there is some visual pop-in after a side-modal closes but before the invalidation comes back.🤖 description of invalidation race in e2e test
After unlinking
ip-pool-1, the confirm modal closes before the invalidated queries finish refetching.The test then opens
ip-pool-2’s action menu. One of those late refetches updates the data used to build the row actions, replacing the action column and unmounting the open menu. Playwright either:The unlink itself succeeds; the failure is the UI becoming unstable during the unawaited refetch. Awaiting invalidation keeps the mutation—and therefore the modal—pending until those refetches settle.
We could manually await invalidations in all onSuccess callbacks (and make all success callbacks async) but the approach in this PR is neater and harder to mess up. When we do it manually, there's nothing forcing us to put the invalidations first. By defining them in this structured way, the
useApiMutationhelper ensures the invalidations always run first and it runs them in parallel instead of sequentially.New invalidation API
UX considerations
Even when there isn't a bug actively caused by not awaiting, it still makes the UI jump around unnecessarily. One thing worth considering is whether we should find a way to do a kind of temporary glowy flash on new rows when they appear in the table. That would be kind of nice, especially now that the modal closing and the pop-in are simultaneous.
Before
It's subtle, but you can see that after creating a floating IP, the side modal closes and then the new floating IP pops into the list.
2026-08-27-await-invalidation-before.mp4
After
After successful creation, the side modal closes and the new floating IP appears at the same time.
2026-08-27-await-invalidation-after.mp4