tangled
alpha
login
or
join now
danabra.mov
/
rscexplorer
37
fork
atom
A tool for people curious about the React Server Components protocol
rscexplorer.dev/
rsc
react
37
fork
atom
overview
issues
pulls
pipelines
rm example
danabra.mov
2 months ago
07180a73
58cee712
+56
-38
3 changed files
expand all
collapse all
unified
split
src
client
samples.ts
tests
actionerror.spec.ts
helpers.ts
-37
src/client/samples.ts
···
576
576
return String(v)
577
577
}`,
578
578
},
579
579
-
actionerror: {
580
580
-
name: "Action Error",
581
581
-
server: `import { Button } from './client'
582
582
-
583
583
-
export default function App() {
584
584
-
return (
585
585
-
<div>
586
586
-
<h1>Action Error</h1>
587
587
-
<Button failAction={failAction} />
588
588
-
</div>
589
589
-
)
590
590
-
}
591
591
-
592
592
-
async function failAction() {
593
593
-
'use server'
594
594
-
throw new Error('Action failed intentionally')
595
595
-
}`,
596
596
-
client: `'use client'
597
597
-
598
598
-
import { useTransition } from 'react'
599
599
-
600
600
-
export function Button({ failAction }) {
601
601
-
const [isPending, startTransition] = useTransition()
602
602
-
603
603
-
const handleClick = () => {
604
604
-
startTransition(async () => {
605
605
-
await failAction()
606
606
-
})
607
607
-
}
608
608
-
609
609
-
return (
610
610
-
<button onClick={handleClick} disabled={isPending}>
611
611
-
{isPending ? 'Running...' : 'Trigger Failing Action'}
612
612
-
</button>
613
613
-
)
614
614
-
}`,
615
615
-
},
616
579
cve: {
617
580
name: "CVE-2025-55182",
618
581
server: `import { Instructions } from './client'
+37
-1
tests/actionerror.spec.ts
···
2
2
import { createHelpers, launchBrowser, type TestHelpers } from "./helpers.ts";
3
3
import type { Browser, Page } from "playwright";
4
4
5
5
+
const ACTION_ERROR_SERVER = `import { Button } from './client'
6
6
+
7
7
+
export default function App() {
8
8
+
return (
9
9
+
<div>
10
10
+
<h1>Action Error</h1>
11
11
+
<Button failAction={failAction} />
12
12
+
</div>
13
13
+
)
14
14
+
}
15
15
+
16
16
+
async function failAction() {
17
17
+
'use server'
18
18
+
throw new Error('Action failed intentionally')
19
19
+
}`;
20
20
+
21
21
+
const ACTION_ERROR_CLIENT = `'use client'
22
22
+
23
23
+
import { useTransition } from 'react'
24
24
+
25
25
+
export function Button({ failAction }) {
26
26
+
const [isPending, startTransition] = useTransition()
27
27
+
28
28
+
const handleClick = () => {
29
29
+
startTransition(async () => {
30
30
+
await failAction()
31
31
+
})
32
32
+
}
33
33
+
34
34
+
return (
35
35
+
<button onClick={handleClick} disabled={isPending}>
36
36
+
{isPending ? 'Running...' : 'Trigger Failing Action'}
37
37
+
</button>
38
38
+
)
39
39
+
}`;
40
40
+
5
41
let browser: Browser;
6
42
let page: Page;
7
43
let h: TestHelpers;
···
21
57
});
22
58
23
59
test("action error - throwing action shows error in entry and clears pending state", async () => {
24
24
-
await h.load("actionerror");
60
60
+
await h.loadCode(ACTION_ERROR_SERVER, ACTION_ERROR_CLIENT);
25
61
26
62
// Render completes
27
63
expect(await h.stepAll()).toMatchInlineSnapshot(`
+19
tests/helpers.ts
···
17
17
18
18
export type TestHelpers = {
19
19
load: (sample: string) => Promise<void>;
20
20
+
loadCode: (server: string, client: string) => Promise<void>;
20
21
step: () => Promise<string | null>;
21
22
stepAll: () => Promise<string | null>;
22
23
stepInfo: () => Promise<string>;
···
40
41
41
42
async function load(sample: string): Promise<void> {
42
43
await page.goto(`http://localhost:5599/?s=${sample}`);
44
44
+
// Wait for iframe to load and get frame reference
45
45
+
const iframe = page.frameLocator("iframe");
46
46
+
frameRef = iframe;
47
47
+
// Wait for content inside iframe
48
48
+
await iframe.getByTestId("flight-entry").first().waitFor({ timeout: 10000 });
49
49
+
await page.waitForTimeout(100);
50
50
+
prevRowTexts = [];
51
51
+
prevStatuses = [];
52
52
+
prevPreview = await getPreviewText();
53
53
+
previewAsserted = true;
54
54
+
}
55
55
+
56
56
+
async function loadCode(server: string, client: string): Promise<void> {
57
57
+
const json = JSON.stringify({ server, client });
58
58
+
// btoa with UTF-8 encoding
59
59
+
const encoded = btoa(unescape(encodeURIComponent(json)));
60
60
+
await page.goto(`http://localhost:5599/?c=${encodeURIComponent(encoded)}`);
43
61
// Wait for iframe to load and get frame reference
44
62
const iframe = page.frameLocator("iframe");
45
63
frameRef = iframe;
···
290
308
291
309
return {
292
310
load,
311
311
+
loadCode,
293
312
step,
294
313
stepAll,
295
314
stepInfo,