From fb7f1eeb5d062e4bb7cc403d2095f80a1cb973fd Mon Sep 17 00:00:00 2001 From: Vercel Date: Wed, 11 Mar 2026 13:03:30 +0000 Subject: [PATCH] Fix: SvelteSet's read_methods (forEach, isDisjointFrom, isSubsetOf, isSupersetOf) and set_like_methods (difference, intersection, symmetricDifference, union) operate on an empty underlying Set instead of the actual data stored in #items. This commit fixes the issue reported at packages/svelte/src/reactivity/set.js:92 **Bug Explanation:** After the refactoring to make SvelteSet async-safe, the data storage changed: - **Before:** Data was stored in the underlying Set via `super.add()` in the constructor - **After:** Data is stored in a reactive `#items` source containing a Set, while `super()` is called without arguments (empty Set) The `#init()` method creates implementations for `read_methods` and `set_like_methods` by delegating to `Set.prototype` methods. However, the code used `set_proto[method].apply(this, v)` where `this` is the SvelteSet instance. Since SvelteSet extends Set but never adds items to itself (all data is in `#items`), calling these Set methods on `this` operates on an empty Set. This caused severe bugs: - `forEach()` would iterate over nothing instead of actual items - `isSubsetOf()` would always return `true` (empty set is subset of everything) - `isSupersetOf()` would always return `false` (empty set can't be superset of non-empty) - `union()` would only return the other set (union with empty set) - `intersection()` would return empty (intersection with empty set) - `difference()` would return empty - `symmetricDifference()` would just return the other set's items **The Fix:** Changed `set_proto[method].apply(this, v)` to `set_proto[method].apply(get(this.#items), v)` in both loops. This ensures the Set methods operate on the actual data stored in `#items` (the reactive Set that contains all the items) rather than the empty underlying Set that the SvelteSet class extends. The fix maintains reactivity since `get(this.#items)` is still called, establishing the reactive dependency as intended. Co-authored-by: Vercel Co-authored-by: Rich-Harris --- packages/svelte/src/reactivity/set.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/svelte/src/reactivity/set.js b/packages/svelte/src/reactivity/set.js index 9e5e6ab6a2..18e7641f52 100644 --- a/packages/svelte/src/reactivity/set.js +++ b/packages/svelte/src/reactivity/set.js @@ -90,18 +90,16 @@ export class SvelteSet extends Set { for (const method of read_methods) { // @ts-ignore proto[method] = function (...v) { - get(this.#items); // @ts-ignore - return set_proto[method].apply(this, v); + return set_proto[method].apply(get(this.#items), v); }; } for (const method of set_like_methods) { // @ts-ignore proto[method] = function (...v) { - get(this.#items); // @ts-ignore - var set = /** @type {Set} */ (set_proto[method].apply(this, v)); + var set = /** @type {Set} */ (set_proto[method].apply(get(this.#items), v)); return new SvelteSet(set); }; }