#17061 didn't properly handle the case where the title is sync but reactive and async work outside is pending. Handle this by creating a proper effect for the document title, and make sure to wait on it and flush it once ready.
Fixes#17114
There's a possibility of a race condition where `batch.deactivate()` is called while `current_batch !== batch`, and so it would deactivate another batch, which can lead to zombie batches that are never flushed, subsequently leading to wrong `batch_values`. This fixes that by checking if the current batch is the own batch.
Fixes#17109
* fix: wait on dependencies of async bindings
Correctly takes into account blockers for bindings. Also ensures it works for functional bindings.
During that I also noticed a bug in our `nodes_end` assignment logic, which the added test also regresses against.
Fixes#17092Fixes#17090
* ensure async static props/attributes are awaited
* wait on blockers in binding validation
* await dependencies of style directives
* tidy
Fixes#17024Fixes#17049 (comment) (and therefore everything that was still buggy in that issue I think)
* chore: remove unowned check when calling `e.effect_in_unowned_derived`
* WIP
* all non-unit tests passing
* tidy
* WIP
* WIP
* WIP
* note to self
* fix
* fix
* hmm maybe not
* try this
* simplify
* remove skip_reaction
* docs
* add changeset, in case this results in changed behaviour
* Update packages/svelte/src/internal/client/reactivity/effects.js
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
* fix#17024
* fix comment
* revert
* fix
* dry
* changeset
* fix WAS_MARKED logic
* failing test (that uncovered other unrelated bug) + fix
* fix: delete from batch_values on updates (#17115)
* fix: delete from batch_values on updates
This fixes a bug where a derived would still show its old value even after it was indirectly updated again within the same batch. This can for example happen by reading a derived on an effect, then writing to a source in that same effect that makes the derived update, and then read the derived value in a sibling effect - it still shows the old value without the fix.
The fix is to _delete_ the value from batch_values, as it's now the newest value across all batches. In order to not prevent breakage on other batches we have to leave the status of deriveds as-is, i.e. within is_dirty and update_derived we cannot update its status. That might be a bit more inefficient as you now have to traverse the graph for each `get` of that derived (it's a bit like they are all disconnected) but we can always optimize that later if need be.
Another advantage of this fix is that we can get rid of duplicate logic we had to add about unmarking and reconnecting deriveds, because that logic was only needed for the case where deriveds are read after they are updated, which now no longer hits that if-branch
* keep derived cache, but clear it in mark_reactions (#17116)
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
Not all batches will flush right after being activated, some will be activated and then `get` is called on a signal. In that case the value was wrong because we did not apply the changes of that batch. By doing `this.apply()` during `activate()` we ensure we do, which fixes (among other things, likely) a forking bug where old values where sneaking in.
Fixes#17079
* fix: change title only after any pending work has completed
We have to use an effect - not a render effect - for updating the title, and always. That way we change the title only after any pending work has completed.
Fixes#17060
* fix
The previous check was flawed because EFFECT_RAN would be set by the time it is checked, since a promise in a parent component will cause a delay of the inner component being instantiated. Instead we have a new field on the component context checking if the component was already popped (if se we are indeed too late). Don't love it to have a field just for this but I don't see another way to reliably check it.
Fixes#16629
* chore: run boundary async effects in the context of the current batch
* WIP
* reinstate kludge
* fix test
* WIP
* WIP
* WIP
* remove kludge
* restore batch_values after commit
* make private
* tidy up
* fix tests
* update test
* reset #dirty_effects and #maybe_dirty_effects
* add test
* WIP
* add test, fix block resolution
* bring async-effect-after-await test from defer-effects-in-pending-boundary branch
* avoid reawakening committed batches
* changeset
* cheat
* better API
* regenerate
* slightly better approach
* lint
* revert this whatever it is
* add test
* Update feature description for fork API
* error if missing experimental flag
* rename inspect effects to eager effects, run them in prod
* regenerate
* Apply suggestions from code review
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
* tidy up
* add some minimal prose. probably don't need to go super deep here as it's not really meant for non-framework authors
* bit more detail
* add a fork_timing error, regenerate
* unused
* add note
* add fork_discarded error
* require users to discard forks
* add docs
* regenerate
* tweak docs
* fix leak
* fix
* preload on focusin as well
* missed a spot
* reduce nesting
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
* fix: coordinate mount of snippets with await expressions
* try this
* deduplicate
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
The "is this a transparent effect we can ignore" logic for determining whether or not to play a local transition didn't account for pruned block effects. This fix marks the child branch as transparent if the block effect was, and during traversal then checks if the branch is the child of a block - if not that means it's the child of a pruned block effect.
fixes#16826
at present we only call batch.increment() when something async happens if we're not inside a pending boundary, but that's incorrect — it means that a batch is committed before everything resolves. When work inside a pending boundary does resolve, the batch becomes a zombie.
At the same time, we don't handle effects inside pending boundaries correctly. They should be deferred until the boundary (and all its parents) are ready.
This PR attempts to fix that — during traversal, when we exit a pending boundary, any effects that were collected get deferred until the next flush. We also distinguish between batch.#pending (any ongoing async work) and batch.#blocking_pending (any async work that should prevent effects outside pending boundaries from being flushed).
* fix: make `$inspect` logs come from the callsite
* default to showing stack trace
* reuse id
* DRY out, improve server-side inspect
* update docs
* ugh
* fix the dang tests
* ugh windows is there no punch bowl you won't poop in?
* argh
* how about this
* alright finally
Fixes#16850, fixes#16775, fixes#16795, fixes#16982#16631 introduced a bug that results in the effects within guards being evaluated before the guards themselves. This fix makes sure to iterate the block effects in the correct order (top down)
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com>
* WIP implement `$effect.pending(...)`
* feat: `$state.eager(value)` (#16926)
* runtime-first approach
* revert these
* type safety, lint
* fix: better input cursor restoration for `bind:value` (#16925)
If cursor was at end and new input is longer, move cursor to new end
No test because not possible to reproduce using our test setup.
Follow-up to #14649, helps with #16577
* Version Packages (#16920)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* docs: await no longer need pending (#16900)
* docs: link to custom renderer issue in Svelte Native discussion (#16896)
* fix code block (#16937)
Updated code block syntax from Svelte to JavaScript for clarity.
* fix: unset context on stale promises (#16935)
* fix: unset context on stale promises
When a stale promise is rejected in `async_derived`, and the promise eventually resolves, `d.resolve` will be noop and `d.promise.then(handler, ...)` will never run. That in turns means any restored context (via `(await save(..))()`) will never be unset. We have to handle this case and unset the context to prevent errors such as false-positive state mutation errors
* fix: unset context on stale promises (slightly different approach) (#16936)
* slightly different approach to #16935
* move unset_context call
* get rid of logs
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
* fix: svg `radialGradient` `fr` attribute missing in types (#16943)
* fix(svg radialGradient): fr attribute missing in types
* chore: add changeset
* Version Packages (#16940)
* Version Packages
* Update packages/svelte/CHANGELOG.md
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
* chore: simplify `batch.apply()` (#16945)
* chore: simplify `batch.apply()`
* belt and braces
* note to self
* unused
* fix: don't rerun async effects unnecessarily (#16944)
Since #16866, when an async effect runs multiple times, we rebase older batches and rerun those effects. This can have unintended consequences: In a case where an async effect only depends on a single source, and that single source was updated in a later batch, we know that we don't need to / should not rerun the older batch.
This PR makes it so: We collect all the sources of older batches that are not part of the current batch that just committed, and then only mark those async effects as dirty which depend on one of those other sources. Fixes the bug I noticed while working on #16935
* fix: ensure map iteration order is correct (#16947)
quick follow-up to #16944
Resetting a map entry does not change its position in the map when iterating. We need to make sure that reset makes that batch jump "to the front" for the "reject all stale batches" logic below. Edge case for which I can't come up with a test case but it _is_ a possibility.
* feat: add `createContext` utility for type-safe context (#16948)
* feat: add `createContext` utility for type-safe context
* regenerate
* Version Packages (#16946)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: Remove annoying sync-async warning (#16949)
* fix
* use `$state.eager(value)` instead of `$effect.pending(value)`
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Hyunbin Seo <47051820+hyunbinseo@users.noreply.github.com>
Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Co-authored-by: Hannes Rüger <hannes@hannesrueger.de>
Co-authored-by: Elliott Johnson <sejohnson@torchcloudconsulting.com>
* decouple from boundaries
* use queue_micro_task
* add test
* fix
* changeset
* revert
* tidy up
* update docs
* Update packages/svelte/src/internal/client/reactivity/batch.js
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
* minor tweak
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Hyunbin Seo <47051820+hyunbinseo@users.noreply.github.com>
Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>
Co-authored-by: Hannes Rüger <hannes@hannesrueger.de>
Co-authored-by: Elliott Johnson <sejohnson@torchcloudconsulting.com>
Since #16866, when an async effect runs multiple times, we rebase older batches and rerun those effects. This can have unintended consequences: In a case where an async effect only depends on a single source, and that single source was updated in a later batch, we know that we don't need to / should not rerun the older batch.
This PR makes it so: We collect all the sources of older batches that are not part of the current batch that just committed, and then only mark those async effects as dirty which depend on one of those other sources. Fixes the bug I noticed while working on #16935
* fix: unset context on stale promises
When a stale promise is rejected in `async_derived`, and the promise eventually resolves, `d.resolve` will be noop and `d.promise.then(handler, ...)` will never run. That in turns means any restored context (via `(await save(..))()`) will never be unset. We have to handle this case and unset the context to prevent errors such as false-positive state mutation errors
* fix: unset context on stale promises (slightly different approach) (#16936)
* slightly different approach to #16935
* move unset_context call
* get rid of logs
---------
Co-authored-by: Rich Harris <rich.harris@vercel.com>
Test for #16912
Also some explanation what the bug was:
1. async batch kicks off
2. outer async work succeeds, still something pending, so doesn't do anything for now
3. something unrelated writes to a signal (in the remote functions case it's the query writing to loading, raw etc), which creates a new batch
4. new batch executes. since there are multiple batches it takes the previous value which means if block is still alive. commits that, since no async work from the perspective of this branch
5. inner async work succeeds. now the batch has zero pending async work so it can flush. But the if block is no longer dirty since it was done by the other batch already -> never undos the other work
#16912 fixes it by still traversing the tree which means the if block deletion is scheduled to commit later, which it then does
We have an each block optimization that omits the comment when the each block is the sole child of an element. This optimization clashes with async which wants to skip ahead to the sibling closing comment.
For now we therefore remove that optimization when the each block is async. In the long run we should instead optimize _all_ cases where _any_ block is the sole child of an element, in both async and sync mode, consistently.
fixes#16905fixes#16907