diff --git a/.changeset/forty-peaches-unite.md b/.changeset/forty-peaches-unite.md new file mode 100644 index 0000000000..b24993deb2 --- /dev/null +++ b/.changeset/forty-peaches-unite.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: ensure unstate() only deeply applies to plain objects and arrays diff --git a/packages/svelte/src/internal/client/proxy/proxy.js b/packages/svelte/src/internal/client/proxy/proxy.js index ae993267d6..d0bb285e10 100644 --- a/packages/svelte/src/internal/client/proxy/proxy.js +++ b/packages/svelte/src/internal/client/proxy/proxy.js @@ -11,11 +11,15 @@ import { batch_inspect } from '../runtime.js'; import { + array_prototype, define_property, get_descriptor, get_descriptors, + get_prototype_of, is_array, - object_keys + is_frozen, + object_keys, + object_prototype } from '../utils.js'; /** @typedef {{ s: Map>; v: import('../types.js').SourceSignal; a: boolean, i: boolean, p: StateObject }} Metadata */ @@ -23,12 +27,6 @@ import { export const STATE_SYMBOL = Symbol('$state'); export const READONLY_SYMBOL = Symbol('readonly'); - -const object_prototype = Object.prototype; -const array_prototype = Array.prototype; -const get_prototype_of = Object.getPrototypeOf; -const is_frozen = Object.isFrozen; - /** * @template {StateObject} T * @param {T} value diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 126692b1a1..97804e11ce 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1,7 +1,16 @@ import { DEV } from 'esm-env'; import { subscribe_to_store } from '../../store/utils.js'; import { EMPTY_FUNC, run_all } from '../common.js'; -import { get_descriptor, get_descriptors, is_array, is_frozen, object_freeze } from './utils.js'; +import { + array_prototype, + get_descriptor, + get_descriptors, + get_prototype_of, + is_array, + is_frozen, + object_freeze, + object_prototype +} from './utils.js'; import { PROPS_IS_LAZY_INITIAL, PROPS_IS_IMMUTABLE, @@ -1975,19 +1984,23 @@ function deep_unstate(value, visited = new Map()) { visited.set(value, unstated); return unstated; } - - let contains_unstated = false; - /** @type {any} */ - const nested_unstated = Array.isArray(value) ? [] : {}; - for (let key in value) { - const result = deep_unstate(value[key], visited); - nested_unstated[key] = result; - if (result !== value[key]) { - contains_unstated = true; + const prototype = get_prototype_of(value); + // Only deeply unstate plain objects and arrays + if (prototype === object_prototype || prototype === array_prototype) { + let contains_unstated = false; + /** @type {any} */ + const nested_unstated = Array.isArray(value) ? [] : {}; + for (let key in value) { + const result = deep_unstate(value[key], visited); + nested_unstated[key] = result; + if (result !== value[key]) { + contains_unstated = true; + } } + visited.set(value, contains_unstated ? nested_unstated : value); + } else { + visited.set(value, value); } - - visited.set(value, contains_unstated ? nested_unstated : value); } return visited.get(value) ?? value; diff --git a/packages/svelte/src/internal/client/utils.js b/packages/svelte/src/internal/client/utils.js index 7c1b01515e..1da6f2fb39 100644 --- a/packages/svelte/src/internal/client/utils.js +++ b/packages/svelte/src/internal/client/utils.js @@ -10,6 +10,9 @@ export var object_freeze = Object.freeze; export var define_property = Object.defineProperty; export var get_descriptor = Object.getOwnPropertyDescriptor; export var get_descriptors = Object.getOwnPropertyDescriptors; +export var object_prototype = Object.prototype; +export var array_prototype = Array.prototype; +export var get_prototype_of = Object.getPrototypeOf; /** * @param {any} thing