From 9412c5861c365610d7c6c0e4ecd3572ac3fe5860 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 21 Jul 2025 22:17:25 -0400 Subject: [PATCH 1/6] chore: log effect functions in log_effect_tree (#16468) --- packages/svelte/src/internal/client/dev/debug.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/svelte/src/internal/client/dev/debug.js b/packages/svelte/src/internal/client/dev/debug.js index c47080ed2f1e..2714a3af1f69 100644 --- a/packages/svelte/src/internal/client/dev/debug.js +++ b/packages/svelte/src/internal/client/dev/debug.js @@ -63,6 +63,13 @@ export function log_effect_tree(effect, depth = 0) { // eslint-disable-next-line no-console console.log(callsite); + } else { + // eslint-disable-next-line no-console + console.groupCollapsed(`%cfn`, `font-weight: normal`); + // eslint-disable-next-line no-console + console.log(effect.fn); + // eslint-disable-next-line no-console + console.groupEnd(); } if (effect.deps !== null) { From 1deb31082a383fb5f4f9ae86f1ff12657823bcd7 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 22 Jul 2025 12:56:32 -0400 Subject: [PATCH 2/6] fix: abort and reschedule `$effect.pre` when necessary (#16335) * unskip failing test * fix * tidy up * skip_no_async * add comment --- .../runtime-runes/samples/effect-order-7/_config.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/svelte/tests/runtime-runes/samples/effect-order-7/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-order-7/_config.js index 29c33c7b1886..f0a9c2e867bd 100644 --- a/packages/svelte/tests/runtime-runes/samples/effect-order-7/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/effect-order-7/_config.js @@ -2,14 +2,18 @@ import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ - skip: true, + // For this to work in non-async mode, we would need to abort + // inside `#traverse_effect_tree`, which would be very + // complicated and annoying. Since this hasn't been + // a real issue (AFAICT), we ignore it + skip_no_async: true, - async test({ assert, target, logs }) { + async test({ target }) { const [open, close] = target.querySelectorAll('button'); flushSync(() => open.click()); - flushSync(() => close.click()); - assert.deepEqual(logs, [true]); + // if the effect queue isn't aborted after the state change, this will throw + flushSync(() => close.click()); } }); From 68372460e999e48ac09b1c2bd4935ceaea8c33e9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 22 Jul 2025 13:29:15 -0400 Subject: [PATCH 3/6] chore: small tidy up (#16476) --- .../src/internal/client/reactivity/batch.js | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index ec082bb595ff..c45221189404 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -193,6 +193,8 @@ export class Batch { // if we didn't start any new async work, and no async work // is outstanding from a previous flush, commit if (this.#async_effects.length === 0 && this.#pending === 0) { + this.#commit(); + var render_effects = this.#render_effects; var effects = this.#effects; @@ -200,8 +202,6 @@ export class Batch { this.#effects = []; this.#block_effects = []; - this.#commit(); - flush_queued_effects(render_effects); flush_queued_effects(effects); @@ -539,43 +539,43 @@ function flush_queued_effects(effects) { var length = effects.length; if (length === 0) return; - for (var i = 0; i < length; i++) { - var effect = effects[i]; - - if ((effect.f & (DESTROYED | INERT)) === 0) { - if (is_dirty(effect)) { - var wv = write_version; - - update_effect(effect); - - // Effects with no dependencies or teardown do not get added to the effect tree. - // Deferred effects (e.g. `$effect(...)`) _are_ added to the tree because we - // don't know if we need to keep them until they are executed. Doing the check - // here (rather than in `update_effect`) allows us to skip the work for - // immediate effects. - if (effect.deps === null && effect.first === null && effect.nodes_start === null) { - // if there's no teardown or abort controller we completely unlink - // the effect from the graph - if (effect.teardown === null && effect.ac === null) { - // remove this effect from the graph - unlink_effect(effect); - } else { - // keep the effect in the graph, but free up some memory - effect.fn = null; - } - } + var i = 0; - // if state is written in a user effect, abort and re-schedule, lest we run - // effects that should be removed as a result of the state change - if (write_version > wv && (effect.f & USER_EFFECT) !== 0) { - break; + while (i < length) { + var effect = effects[i++]; + + if ((effect.f & (DESTROYED | INERT)) === 0 && is_dirty(effect)) { + var wv = write_version; + + update_effect(effect); + + // Effects with no dependencies or teardown do not get added to the effect tree. + // Deferred effects (e.g. `$effect(...)`) _are_ added to the tree because we + // don't know if we need to keep them until they are executed. Doing the check + // here (rather than in `update_effect`) allows us to skip the work for + // immediate effects. + if (effect.deps === null && effect.first === null && effect.nodes_start === null) { + // if there's no teardown or abort controller we completely unlink + // the effect from the graph + if (effect.teardown === null && effect.ac === null) { + // remove this effect from the graph + unlink_effect(effect); + } else { + // keep the effect in the graph, but free up some memory + effect.fn = null; } } + + // if state is written in a user effect, abort and re-schedule, lest we run + // effects that should be removed as a result of the state change + if (write_version > wv && (effect.f & USER_EFFECT) !== 0) { + break; + } } } - for (; i < length; i += 1) { - schedule_effect(effects[i]); + while (i < length) { + schedule_effect(effects[i++]); } } From 8e2f4b51c50a2e10bc482fac6d900be921dd8b74 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 23 Jul 2025 07:55:51 -0400 Subject: [PATCH 4/6] fix: unset batch before flushing queued effects (#16482) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - add state changes resulting from an $effect to a separate new batch - schedule rerunning effects based on the sources that are dirty, not just rerunning them all blindly (excempting async effects which will have run by that time already) * test * better fix * tests * this fixes the last test somehow * fix #16477 * typo * copy over changeset from #16477 * copy over changeset from #16464 * changeset * dedupe * move flushing_sync check inside Batch.ensure * unused * flushing_sync -> is_flushing_sync * remove flush_effects method * dedupe declaration * tweak * tweak * update comment — it _does_ feel slightly wrong, but no wronger than the rest of this cursed function --------- Co-authored-by: Simon Holthausen --- .changeset/grumpy-boats-beg.md | 5 + .changeset/shiny-walls-fix.md | 5 + .changeset/thick-mice-kick.md | 5 + .../src/internal/client/reactivity/batch.js | 190 ++++++++++-------- .../src/internal/client/reactivity/sources.js | 11 +- .../Component.svelte | 7 + .../1000-reading-derived-effects/_config.js | 5 + .../1000-reading-derived-effects/main.svelte | 8 + .../_config.js | 26 +++ .../main.svelte | 27 +++ .../async-effect-triggers-await/_config.js | 32 +++ .../async-effect-triggers-await/main.svelte | 24 +++ .../binding-update-while-focused-2/_config.js | 24 +++ .../main.svelte | 21 ++ 14 files changed, 298 insertions(+), 92 deletions(-) create mode 100644 .changeset/grumpy-boats-beg.md create mode 100644 .changeset/shiny-walls-fix.md create mode 100644 .changeset/thick-mice-kick.md create mode 100644 packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/Component.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-binding-update-while-focused/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-binding-update-while-focused/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-triggers-await/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-effect-triggers-await/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/binding-update-while-focused-2/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/binding-update-while-focused-2/main.svelte diff --git a/.changeset/grumpy-boats-beg.md b/.changeset/grumpy-boats-beg.md new file mode 100644 index 000000000000..f677743defa5 --- /dev/null +++ b/.changeset/grumpy-boats-beg.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: keep input in sync when binding updated via effect diff --git a/.changeset/shiny-walls-fix.md b/.changeset/shiny-walls-fix.md new file mode 100644 index 000000000000..91ed548728a3 --- /dev/null +++ b/.changeset/shiny-walls-fix.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent infinite async loop diff --git a/.changeset/thick-mice-kick.md b/.changeset/thick-mice-kick.md new file mode 100644 index 000000000000..eec55b77eee4 --- /dev/null +++ b/.changeset/thick-mice-kick.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: exclude derived writes from effect abort and rescheduling diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index c45221189404..ce413fa1e186 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -21,14 +21,13 @@ import { is_updating_effect, set_is_updating_effect, set_signal_status, - update_effect, - write_version + update_effect } from '../runtime.js'; import * as e from '../errors.js'; import { flush_tasks } from '../dom/task.js'; import { DEV } from 'esm-env'; import { invoke_error_boundary } from '../error-handling.js'; -import { old_values } from './sources.js'; +import { mark_reactions, old_values } from './sources.js'; import { unlink_effect } from './effects.js'; import { unset_context } from './async.js'; @@ -70,13 +69,15 @@ let last_scheduled_effect = null; let is_flushing = false; +let is_flushing_sync = false; + export class Batch { /** * The current values of any sources that are updated in this batch * They keys of this map are identical to `this.#previous` * @type {Map} */ - #current = new Map(); + current = new Map(); /** * The values of any sources that are updated in this batch _before_ those updates took place. @@ -156,7 +157,7 @@ export class Batch { * * @param {Effect[]} root_effects */ - #process(root_effects) { + process(root_effects) { queued_root_effects = []; /** @type {Map | null} */ @@ -169,7 +170,7 @@ export class Batch { current_values = new Map(); batch_deriveds = new Map(); - for (const [source, current] of this.#current) { + for (const [source, current] of this.current) { current_values.set(source, { v: source.v, wv: source.wv }); source.v = current; } @@ -202,9 +203,22 @@ export class Batch { this.#effects = []; this.#block_effects = []; + // If sources are written to, then work needs to happen in a separate batch, else prior sources would be mixed with + // newly updated sources, which could lead to infinite loops when effects run over and over again. + current_batch = null; + flush_queued_effects(render_effects); flush_queued_effects(effects); + // Reinstate the current batch if there was no new one created, as `process()` runs in a loop in `flush_effects()`. + // That method expects `current_batch` to be set, and could run the loop again if effects result in new effects + // being scheduled but without writes happening in which case no new batch is created. + if (current_batch === null) { + current_batch = this; + } else { + batches.delete(this); + } + this.#deferred?.resolve(); } else { // otherwise mark effects clean so they get scheduled on the next run @@ -300,7 +314,7 @@ export class Batch { this.#previous.set(source, value); } - this.#current.set(source, source.v); + this.current.set(source, source.v); } activate() { @@ -327,13 +341,13 @@ export class Batch { flush() { if (queued_root_effects.length > 0) { - this.flush_effects(); + flush_effects(); } else { this.#commit(); } if (current_batch !== this) { - // this can happen if a `flushSync` occurred during `this.flush_effects()`, + // this can happen if a `flushSync` occurred during `flush_effects()`, // which is permitted in legacy mode despite being a terrible idea return; } @@ -345,52 +359,6 @@ export class Batch { this.deactivate(); } - flush_effects() { - var was_updating_effect = is_updating_effect; - is_flushing = true; - - try { - var flush_count = 0; - set_is_updating_effect(true); - - while (queued_root_effects.length > 0) { - if (flush_count++ > 1000) { - if (DEV) { - var updates = new Map(); - - for (const source of this.#current.keys()) { - for (const [stack, update] of source.updated ?? []) { - var entry = updates.get(stack); - - if (!entry) { - entry = { error: update.error, count: 0 }; - updates.set(stack, entry); - } - - entry.count += update.count; - } - } - - for (const update of updates.values()) { - // eslint-disable-next-line no-console - console.error(update.error); - } - } - - infinite_loop_guard(); - } - - this.#process(queued_root_effects); - old_values.clear(); - } - } finally { - is_flushing = false; - set_is_updating_effect(was_updating_effect); - - last_scheduled_effect = null; - } - } - /** * Append and remove branches to/from the DOM */ @@ -412,19 +380,8 @@ export class Batch { this.#pending -= 1; if (this.#pending === 0) { - for (const e of this.#render_effects) { - set_signal_status(e, DIRTY); - schedule_effect(e); - } - - for (const e of this.#effects) { - set_signal_status(e, DIRTY); - schedule_effect(e); - } - - for (const e of this.#block_effects) { - set_signal_status(e, DIRTY); - schedule_effect(e); + for (const source of this.current.keys()) { + mark_reactions(source, DIRTY, false); } this.#render_effects = []; @@ -445,12 +402,12 @@ export class Batch { return (this.#deferred ??= deferred()).promise; } - static ensure(autoflush = true) { + static ensure() { if (current_batch === null) { const batch = (current_batch = new Batch()); batches.add(current_batch); - if (autoflush) { + if (!is_flushing_sync) { Batch.enqueue(() => { if (current_batch !== batch) { // a flushSync happened in the meantime @@ -487,32 +444,85 @@ export function flushSync(fn) { e.flush_sync_in_effect(); } - var result; + var was_flushing_sync = is_flushing_sync; + is_flushing_sync = true; - const batch = Batch.ensure(false); + try { + var result; - if (fn) { - batch.flush_effects(); + if (fn) { + flush_effects(); + result = fn(); + } - result = fn(); - } + while (true) { + flush_tasks(); - while (true) { - flush_tasks(); + if (queued_root_effects.length === 0) { + current_batch?.flush(); - if (queued_root_effects.length === 0) { - if (batch === current_batch) { - batch.flush(); + // we need to check again, in case we just updated an `$effect.pending()` + if (queued_root_effects.length === 0) { + // this would be reset in `flush_effects()` but since we are early returning here, + // we need to reset it here as well in case the first time there's 0 queued root effects + last_scheduled_effect = null; + + return /** @type {T} */ (result); + } } - // this would be reset in `batch.flush_effects()` but since we are early returning here, - // we need to reset it here as well in case the first time there's 0 queued root effects - last_scheduled_effect = null; + flush_effects(); + } + } finally { + is_flushing_sync = was_flushing_sync; + } +} + +function flush_effects() { + var was_updating_effect = is_updating_effect; + is_flushing = true; + + try { + var flush_count = 0; + set_is_updating_effect(true); + + while (queued_root_effects.length > 0) { + var batch = Batch.ensure(); + + if (flush_count++ > 1000) { + if (DEV) { + var updates = new Map(); + + for (const source of batch.current.keys()) { + for (const [stack, update] of source.updated ?? []) { + var entry = updates.get(stack); + + if (!entry) { + entry = { error: update.error, count: 0 }; + updates.set(stack, entry); + } + + entry.count += update.count; + } + } + + for (const update of updates.values()) { + // eslint-disable-next-line no-console + console.error(update.error); + } + } + + infinite_loop_guard(); + } - return /** @type {T} */ (result); + batch.process(queued_root_effects); + old_values.clear(); } + } finally { + is_flushing = false; + set_is_updating_effect(was_updating_effect); - batch.flush_effects(); + last_scheduled_effect = null; } } @@ -545,7 +555,7 @@ function flush_queued_effects(effects) { var effect = effects[i++]; if ((effect.f & (DESTROYED | INERT)) === 0 && is_dirty(effect)) { - var wv = write_version; + var n = current_batch ? current_batch.current.size : 0; update_effect(effect); @@ -568,7 +578,11 @@ function flush_queued_effects(effects) { // if state is written in a user effect, abort and re-schedule, lest we run // effects that should be removed as a result of the state change - if (write_version > wv && (effect.f & USER_EFFECT) !== 0) { + if ( + current_batch !== null && + current_batch.current.size > n && + (effect.f & USER_EFFECT) !== 0 + ) { break; } } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index f6b14f3360de..3b28c8fdceeb 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -179,7 +179,7 @@ export function internal_set(source, value) { source.v = value; - const batch = Batch.ensure(); + var batch = Batch.ensure(); batch.capture(source, old_value); if (DEV) { @@ -301,9 +301,10 @@ export function increment(source) { /** * @param {Value} signal * @param {number} status should be DIRTY or MAYBE_DIRTY + * @param {boolean} schedule_async * @returns {void} */ -function mark_reactions(signal, status) { +export function mark_reactions(signal, status, schedule_async = true) { var reactions = signal.reactions; if (reactions === null) return; @@ -323,14 +324,16 @@ function mark_reactions(signal, status) { continue; } + var should_schedule = (flags & DIRTY) === 0 && (schedule_async || (flags & ASYNC) === 0); + // don't set a DIRTY reaction to MAYBE_DIRTY - if ((flags & DIRTY) === 0) { + if (should_schedule) { set_signal_status(reaction, status); } if ((flags & DERIVED) !== 0) { mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY); - } else if ((flags & DIRTY) === 0) { + } else if (should_schedule) { schedule_effect(/** @type {Effect} */ (reaction)); } } diff --git a/packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/Component.svelte b/packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/Component.svelte new file mode 100644 index 000000000000..7a54323cb97f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/Component.svelte @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/_config.js b/packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/_config.js new file mode 100644 index 000000000000..2e4a27cf0912 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + async test() {} +}); diff --git a/packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/main.svelte b/packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/main.svelte new file mode 100644 index 000000000000..bd326edfb92a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/1000-reading-derived-effects/main.svelte @@ -0,0 +1,8 @@ + + +{#each arr} + +{/each} diff --git a/packages/svelte/tests/runtime-runes/samples/async-binding-update-while-focused/_config.js b/packages/svelte/tests/runtime-runes/samples/async-binding-update-while-focused/_config.js new file mode 100644 index 000000000000..782ae945f9c3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-binding-update-while-focused/_config.js @@ -0,0 +1,26 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + await tick(); + + const [input] = target.querySelectorAll('input'); + + input.focus(); + input.value = '3'; + input.dispatchEvent(new InputEvent('input', { bubbles: true })); + await tick(); + + assert.equal(input.value, '3'); + assert.htmlEqual(target.innerHTML, `

3

`); + + input.focus(); + input.value = '1'; + input.dispatchEvent(new InputEvent('input', { bubbles: true })); + await tick(); + + assert.equal(input.value, '2'); + assert.htmlEqual(target.innerHTML, `

2

`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-binding-update-while-focused/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-binding-update-while-focused/main.svelte new file mode 100644 index 000000000000..763ce6ebf073 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-binding-update-while-focused/main.svelte @@ -0,0 +1,27 @@ + + + +

{await value}

+ + + {#snippet pending()} +

loading...

+ {/snippet} +
diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-triggers-await/_config.js b/packages/svelte/tests/runtime-runes/samples/async-effect-triggers-await/_config.js new file mode 100644 index 000000000000..c551cc6b8c39 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-triggers-await/_config.js @@ -0,0 +1,32 @@ +import { tick } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + await tick(); + + const [increment] = target.querySelectorAll('button'); + + increment.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + +

1

+

1

+ ` + ); + + increment.click(); + await tick(); + assert.htmlEqual( + target.innerHTML, + ` + +

2

+

2

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-effect-triggers-await/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-effect-triggers-await/main.svelte new file mode 100644 index 000000000000..153fe03f0d89 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-effect-triggers-await/main.svelte @@ -0,0 +1,24 @@ + + + + +

{JSON.stringify((await data), null, 2)}

+ {#if true} + +

{unrelated}

+ {/if} + + {#snippet pending()} +

loading...

+ {/snippet} +
diff --git a/packages/svelte/tests/runtime-runes/samples/binding-update-while-focused-2/_config.js b/packages/svelte/tests/runtime-runes/samples/binding-update-while-focused-2/_config.js new file mode 100644 index 000000000000..7a56c79d7176 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/binding-update-while-focused-2/_config.js @@ -0,0 +1,24 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, target }) { + const [input] = target.querySelectorAll('input'); + + input.focus(); + input.value = '3'; + input.dispatchEvent(new InputEvent('input', { bubbles: true })); + flushSync(); + + assert.equal(input.value, '3'); + assert.htmlEqual(target.innerHTML, `

3

`); + + input.focus(); + input.value = '1'; + input.dispatchEvent(new InputEvent('input', { bubbles: true })); + flushSync(); + + assert.equal(input.value, '2'); + assert.htmlEqual(target.innerHTML, `

2

`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/binding-update-while-focused-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/binding-update-while-focused-2/main.svelte new file mode 100644 index 000000000000..b0597c223b99 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/binding-update-while-focused-2/main.svelte @@ -0,0 +1,21 @@ + + +

{value}

+ From c26365bdda1a13e98a2e75f17da655e08ff4f8bc Mon Sep 17 00:00:00 2001 From: Hyunbin Seo <47051820+hyunbinseo@users.noreply.github.com> Date: Wed, 23 Jul 2025 21:02:28 +0900 Subject: [PATCH 5/6] chore: rename form `accept-charset` attribute (#16478) * chore: rename form accept-charset attribute * chore: utf-8 to lowercase Co-authored-by: Rich Harris * Update .changeset/healthy-carpets-deny.md --------- Co-authored-by: Rich Harris --- .changeset/healthy-carpets-deny.md | 5 +++++ packages/svelte/elements.d.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/healthy-carpets-deny.md diff --git a/.changeset/healthy-carpets-deny.md b/.changeset/healthy-carpets-deny.md new file mode 100644 index 000000000000..8f8db7fa9cd3 --- /dev/null +++ b/.changeset/healthy-carpets-deny.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: rename form accept-charset attribute diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index 1492f777925d..604241592aff 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -996,7 +996,7 @@ export interface HTMLFieldsetAttributes extends HTMLAttributes { - acceptcharset?: string | undefined | null; + 'accept-charset'?: 'utf-8' | (string & {}) | undefined | null; action?: string | undefined | null; autocomplete?: AutoFillBase | undefined | null; enctype?: From f8820956d2591288de53927c5e173242a13f125a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 11:23:52 -0400 Subject: [PATCH 6/6] Version Packages (#16484) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/grumpy-boats-beg.md | 5 ----- .changeset/healthy-carpets-deny.md | 5 ----- .changeset/shiny-walls-fix.md | 5 ----- .changeset/thick-mice-kick.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 .changeset/grumpy-boats-beg.md delete mode 100644 .changeset/healthy-carpets-deny.md delete mode 100644 .changeset/shiny-walls-fix.md delete mode 100644 .changeset/thick-mice-kick.md diff --git a/.changeset/grumpy-boats-beg.md b/.changeset/grumpy-boats-beg.md deleted file mode 100644 index f677743defa5..000000000000 --- a/.changeset/grumpy-boats-beg.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: keep input in sync when binding updated via effect diff --git a/.changeset/healthy-carpets-deny.md b/.changeset/healthy-carpets-deny.md deleted file mode 100644 index 8f8db7fa9cd3..000000000000 --- a/.changeset/healthy-carpets-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: rename form accept-charset attribute diff --git a/.changeset/shiny-walls-fix.md b/.changeset/shiny-walls-fix.md deleted file mode 100644 index 91ed548728a3..000000000000 --- a/.changeset/shiny-walls-fix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent infinite async loop diff --git a/.changeset/thick-mice-kick.md b/.changeset/thick-mice-kick.md deleted file mode 100644 index eec55b77eee4..000000000000 --- a/.changeset/thick-mice-kick.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: exclude derived writes from effect abort and rescheduling diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 5a5e532a081e..8cd385046041 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.36.14 + +### Patch Changes + +- fix: keep input in sync when binding updated via effect ([#16482](https://github.com/sveltejs/svelte/pull/16482)) + +- fix: rename form accept-charset attribute ([#16478](https://github.com/sveltejs/svelte/pull/16478)) + +- fix: prevent infinite async loop ([#16482](https://github.com/sveltejs/svelte/pull/16482)) + +- fix: exclude derived writes from effect abort and rescheduling ([#16482](https://github.com/sveltejs/svelte/pull/16482)) + ## 5.36.13 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 4bf9a5df22e4..7fe1d161f288 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.36.13", + "version": "5.36.14", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 7d47fbc5f14c..cd9d8b459c32 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.36.13'; +export const VERSION = '5.36.14'; export const PUBLIC_VERSION = '5'; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy