From 1445341ba83b238708ea2f5ecd88910c829c34c9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Dec 2025 13:14:12 -0500 Subject: [PATCH] fix: handle cross-realm Promises in `hydratable` --- .changeset/olive-mangos-march.md | 5 +++++ packages/svelte/src/internal/server/hydratable.js | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 .changeset/olive-mangos-march.md diff --git a/.changeset/olive-mangos-march.md b/.changeset/olive-mangos-march.md new file mode 100644 index 000000000000..28253eb49aa7 --- /dev/null +++ b/.changeset/olive-mangos-march.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: handle cross-realm Promises in `hydratable` diff --git a/packages/svelte/src/internal/server/hydratable.js b/packages/svelte/src/internal/server/hydratable.js index 59fa97da4c6d..5711885ed41c 100644 --- a/packages/svelte/src/internal/server/hydratable.js +++ b/packages/svelte/src/internal/server/hydratable.js @@ -56,7 +56,7 @@ function encode(key, value, unresolved) { let uid = 1; entry.serialized = devalue.uneval(entry.value, (value, uneval) => { - if (value instanceof Promise) { + if (is_promise(value)) { const p = value .then((v) => `r(${uneval(v)})`) .catch((devalue_error) => @@ -90,6 +90,16 @@ function encode(key, value, unresolved) { return entry; } +/** + * @param {any} value + * @returns {value is Promise} + */ +function is_promise(value) { + // we use this check rather than `instanceof Promise` + // because it works cross-realm + return Object.prototype.toString.call(value) === '[object Promise]'; +} + /** * @param {string} key * @param {HydratableLookupEntry} a