Skip to content

Commit 44058da

Browse files
fix: Don't crash on hydratable serialization failures (#17315)
* fix: Don't crash on `hydratable` serialization failures * better * Update two-lizards-poke.md Co-authored-by: Ben McCann <[email protected]> --------- Co-authored-by: Ben McCann <[email protected]>
1 parent ad91827 commit 44058da

File tree

4 files changed

+28
-16
lines changed

4 files changed

+28
-16
lines changed

.changeset/two-lizards-poke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: don't crash on `hydratable` serialization failure

packages/svelte/src/internal/server/hydratable.js

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,32 +57,28 @@ function encode(key, value, unresolved) {
5757

5858
entry.serialized = devalue.uneval(entry.value, (value, uneval) => {
5959
if (is_promise(value)) {
60+
// we serialize promises as `"${i}"`, because it's impossible for that string
61+
// to occur 'naturally' (since the quote marks would have to be escaped)
62+
// this placeholder is returned synchronously from `uneval`, which includes it in the
63+
// serialized string. Later (at least one microtask from now), when `p.then` runs, it'll
64+
// be replaced.
65+
const placeholder = `"${uid++}"`;
6066
const p = value
61-
.then((v) => `r(${uneval(v)})`)
67+
.then((v) => {
68+
entry.serialized = entry.serialized.replace(placeholder, `r(${uneval(v)})`);
69+
})
6270
.catch((devalue_error) =>
6371
e.hydratable_serialization_failed(
6472
key,
6573
serialization_stack(entry.stack, devalue_error?.stack)
6674
)
6775
);
6876

69-
// prevent unhandled rejections from crashing the server
70-
p.catch(() => {});
71-
72-
// track which promises are still resolving when render is complete
7377
unresolved?.set(p, key);
74-
p.finally(() => unresolved?.delete(p));
75-
76-
// we serialize promises as `"${i}"`, because it's impossible for that string
77-
// to occur 'naturally' (since the quote marks would have to be escaped)
78-
const placeholder = `"${uid++}"`;
79-
80-
(entry.promises ??= []).push(
81-
p.then((s) => {
82-
entry.serialized = entry.serialized.replace(placeholder, s);
83-
})
84-
);
78+
// prevent unhandled rejections from crashing the server, track which promises are still resolving when render is complete
79+
p.catch(() => {}).finally(() => unresolved?.delete(p));
8580

81+
(entry.promises ??= []).push(p);
8682
return placeholder;
8783
}
8884
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
mode: ['async'],
5+
error: 'hydratable_serialization_failed'
6+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<script lang="ts">
2+
import { hydratable } from 'svelte';
3+
4+
hydratable('key', () => new Promise(() => { throw new Error('nope') }));
5+
</script>

0 commit comments

Comments
 (0)