🌐 AI搜索 & 代理 主页
Skip to content

Commit fb119fe

Browse files
Merge branch 'main' into fix/select-multiple-dynamic
2 parents 8e20eef + 26d3d68 commit fb119fe

File tree

6 files changed

+101
-89
lines changed

6 files changed

+101
-89
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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"prettier-plugin-svelte": "^3.4.0",
4242
"svelte": "workspace:^",
4343
"typescript": "^5.5.4",
44-
"typescript-eslint": "^8.48.0",
44+
"typescript-eslint": "^8.48.1",
4545
"v8-natives": "^1.2.5",
4646
"vitest": "^2.1.9"
4747
}

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>

pnpm-lock.yaml

Lines changed: 72 additions & 72 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)