+1
-1
embed.html
+1
-1
embed.html
+1
-1
index.html
+1
-1
index.html
+733
-13
package-lock.json
+733
-13
package-lock.json
···
26
26
"web-streams-polyfill": "^4.2.0"
27
27
},
28
28
"devDependencies": {
29
+
"@babel/types": "^7.28.5",
29
30
"@eslint/js": "^9.39.2",
31
+
"@types/babel__core": "^7.20.5",
32
+
"@types/babel__standalone": "^7.1.9",
33
+
"@types/node": "^25.0.2",
34
+
"@types/react": "^19.2.7",
35
+
"@types/react-dom": "^19.2.3",
36
+
"@types/text-encoding": "^0.0.40",
30
37
"@vitejs/plugin-react": "^5.1.2",
31
38
"@vitest/browser": "^4.0.15",
32
39
"@vitest/browser-playwright": "^4.0.15",
···
37
44
"playwright": "^1.57.0",
38
45
"prettier": "^3.7.4",
39
46
"rolldown": "^1.0.0-beta.54",
47
+
"start-server-and-test": "^2.1.3",
48
+
"typescript": "^5.9.3",
40
49
"typescript-eslint": "^8.50.0",
41
50
"vite": "8.0.0-beta.2",
42
51
"vitest": "^4.0.15",
···
795
804
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
796
805
}
797
806
},
807
+
"node_modules/@hapi/address": {
808
+
"version": "5.1.1",
809
+
"resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz",
810
+
"integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==",
811
+
"dev": true,
812
+
"license": "BSD-3-Clause",
813
+
"dependencies": {
814
+
"@hapi/hoek": "^11.0.2"
815
+
},
816
+
"engines": {
817
+
"node": ">=14.0.0"
818
+
}
819
+
},
820
+
"node_modules/@hapi/formula": {
821
+
"version": "3.0.2",
822
+
"resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz",
823
+
"integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==",
824
+
"dev": true,
825
+
"license": "BSD-3-Clause"
826
+
},
827
+
"node_modules/@hapi/hoek": {
828
+
"version": "11.0.7",
829
+
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz",
830
+
"integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==",
831
+
"dev": true,
832
+
"license": "BSD-3-Clause"
833
+
},
834
+
"node_modules/@hapi/pinpoint": {
835
+
"version": "2.0.1",
836
+
"resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz",
837
+
"integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==",
838
+
"dev": true,
839
+
"license": "BSD-3-Clause"
840
+
},
841
+
"node_modules/@hapi/tlds": {
842
+
"version": "1.1.4",
843
+
"resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.4.tgz",
844
+
"integrity": "sha512-Fq+20dxsxLaUn5jSSWrdtSRcIUba2JquuorF9UW1wIJS5cSUwxIsO2GIhaWynPRflvxSzFN+gxKte2HEW1OuoA==",
845
+
"dev": true,
846
+
"license": "BSD-3-Clause",
847
+
"engines": {
848
+
"node": ">=14.0.0"
849
+
}
850
+
},
851
+
"node_modules/@hapi/topo": {
852
+
"version": "6.0.2",
853
+
"resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz",
854
+
"integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==",
855
+
"dev": true,
856
+
"license": "BSD-3-Clause",
857
+
"dependencies": {
858
+
"@hapi/hoek": "^11.0.2"
859
+
}
860
+
},
798
861
"node_modules/@humanfs/core": {
799
862
"version": "0.19.1",
800
863
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
···
1697
1760
"@babel/types": "^7.0.0"
1698
1761
}
1699
1762
},
1763
+
"node_modules/@types/babel__standalone": {
1764
+
"version": "7.1.9",
1765
+
"resolved": "https://registry.npmjs.org/@types/babel__standalone/-/babel__standalone-7.1.9.tgz",
1766
+
"integrity": "sha512-IcCNPLqpevUD7UpV8QB0uwQPOyoOKACFf0YtYWRHcmxcakaje4Q7dbG2+jMqxw/I8Zk0NHvEps66WwS7z/UaaA==",
1767
+
"dev": true,
1768
+
"license": "MIT",
1769
+
"dependencies": {
1770
+
"@babel/parser": "^7.25.6",
1771
+
"@babel/types": "^7.25.6",
1772
+
"@types/babel__core": "^7.20.5",
1773
+
"@types/babel__generator": "^7.6.8",
1774
+
"@types/babel__template": "^7.4.4",
1775
+
"@types/babel__traverse": "^7.20.6"
1776
+
}
1777
+
},
1700
1778
"node_modules/@types/babel__template": {
1701
1779
"version": "7.4.4",
1702
1780
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
···
1771
1849
"license": "MIT"
1772
1850
},
1773
1851
"node_modules/@types/node": {
1774
-
"version": "25.0.1",
1775
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.1.tgz",
1776
-
"integrity": "sha512-czWPzKIAXucn9PtsttxmumiQ9N0ok9FrBwgRWrwmVLlp86BrMExzvXRLFYRJ+Ex3g6yqj+KuaxfX1JTgV2lpfg==",
1852
+
"version": "25.0.2",
1853
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.2.tgz",
1854
+
"integrity": "sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA==",
1777
1855
"license": "MIT",
1778
-
"peer": true,
1779
1856
"dependencies": {
1780
1857
"undici-types": "~7.16.0"
1781
1858
}
1782
1859
},
1860
+
"node_modules/@types/react": {
1861
+
"version": "19.2.7",
1862
+
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
1863
+
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
1864
+
"dev": true,
1865
+
"license": "MIT",
1866
+
"dependencies": {
1867
+
"csstype": "^3.2.2"
1868
+
}
1869
+
},
1870
+
"node_modules/@types/react-dom": {
1871
+
"version": "19.2.3",
1872
+
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
1873
+
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
1874
+
"dev": true,
1875
+
"license": "MIT",
1876
+
"peerDependencies": {
1877
+
"@types/react": "^19.2.0"
1878
+
}
1879
+
},
1880
+
"node_modules/@types/text-encoding": {
1881
+
"version": "0.0.40",
1882
+
"resolved": "https://registry.npmjs.org/@types/text-encoding/-/text-encoding-0.0.40.tgz",
1883
+
"integrity": "sha512-dHzoIdwBfY7jcSTTt6XBkaeiuFQAQD7r/7aJySKDdHkYBCDOvs9jPVt4NYXuwBMn89PP6gSd29WubIS19wTiXg==",
1884
+
"dev": true,
1885
+
"license": "MIT"
1886
+
},
1783
1887
"node_modules/@typescript-eslint/eslint-plugin": {
1784
1888
"version": "8.50.0",
1785
1889
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.50.0.tgz",
···
2580
2684
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
2581
2685
}
2582
2686
},
2687
+
"node_modules/arg": {
2688
+
"version": "5.0.2",
2689
+
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
2690
+
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
2691
+
"dev": true,
2692
+
"license": "MIT"
2693
+
},
2583
2694
"node_modules/argparse": {
2584
2695
"version": "2.0.1",
2585
2696
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
···
2597
2708
"node": ">=12"
2598
2709
}
2599
2710
},
2711
+
"node_modules/asynckit": {
2712
+
"version": "0.4.0",
2713
+
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
2714
+
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
2715
+
"dev": true,
2716
+
"license": "MIT"
2717
+
},
2718
+
"node_modules/axios": {
2719
+
"version": "1.13.2",
2720
+
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
2721
+
"integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
2722
+
"dev": true,
2723
+
"license": "MIT",
2724
+
"dependencies": {
2725
+
"follow-redirects": "^1.15.6",
2726
+
"form-data": "^4.0.4",
2727
+
"proxy-from-env": "^1.1.0"
2728
+
}
2729
+
},
2600
2730
"node_modules/balanced-match": {
2601
2731
"version": "1.0.2",
2602
2732
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
···
2617
2747
"version": "2.1.5",
2618
2748
"resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz",
2619
2749
"integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==",
2750
+
"dev": true,
2751
+
"license": "MIT"
2752
+
},
2753
+
"node_modules/bluebird": {
2754
+
"version": "3.7.2",
2755
+
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
2756
+
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
2620
2757
"dev": true,
2621
2758
"license": "MIT"
2622
2759
},
···
2684
2821
"license": "MIT",
2685
2822
"peer": true
2686
2823
},
2824
+
"node_modules/call-bind-apply-helpers": {
2825
+
"version": "1.0.2",
2826
+
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
2827
+
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
2828
+
"dev": true,
2829
+
"license": "MIT",
2830
+
"dependencies": {
2831
+
"es-errors": "^1.3.0",
2832
+
"function-bind": "^1.1.2"
2833
+
},
2834
+
"engines": {
2835
+
"node": ">= 0.4"
2836
+
}
2837
+
},
2687
2838
"node_modules/callsites": {
2688
2839
"version": "3.1.0",
2689
2840
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
···
2752
2903
},
2753
2904
"engines": {
2754
2905
"node": ">=8"
2906
+
}
2907
+
},
2908
+
"node_modules/check-more-types": {
2909
+
"version": "2.24.0",
2910
+
"resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz",
2911
+
"integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==",
2912
+
"dev": true,
2913
+
"license": "MIT",
2914
+
"engines": {
2915
+
"node": ">= 0.8.0"
2755
2916
}
2756
2917
},
2757
2918
"node_modules/chrome-trace-event": {
···
2864
3025
"dev": true,
2865
3026
"license": "MIT"
2866
3027
},
3028
+
"node_modules/combined-stream": {
3029
+
"version": "1.0.8",
3030
+
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
3031
+
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
3032
+
"dev": true,
3033
+
"license": "MIT",
3034
+
"dependencies": {
3035
+
"delayed-stream": "~1.0.0"
3036
+
},
3037
+
"engines": {
3038
+
"node": ">= 0.8"
3039
+
}
3040
+
},
2867
3041
"node_modules/commander": {
2868
3042
"version": "2.20.3",
2869
3043
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
···
2906
3080
"node": ">= 8"
2907
3081
}
2908
3082
},
3083
+
"node_modules/csstype": {
3084
+
"version": "3.2.3",
3085
+
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
3086
+
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
3087
+
"dev": true,
3088
+
"license": "MIT"
3089
+
},
2909
3090
"node_modules/debug": {
2910
3091
"version": "4.4.3",
2911
3092
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
···
2931
3112
"dev": true,
2932
3113
"license": "MIT"
2933
3114
},
3115
+
"node_modules/delayed-stream": {
3116
+
"version": "1.0.0",
3117
+
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
3118
+
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
3119
+
"dev": true,
3120
+
"license": "MIT",
3121
+
"engines": {
3122
+
"node": ">=0.4.0"
3123
+
}
3124
+
},
2934
3125
"node_modules/detect-libc": {
2935
3126
"version": "2.1.2",
2936
3127
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
···
2941
3132
"node": ">=8"
2942
3133
}
2943
3134
},
3135
+
"node_modules/dunder-proto": {
3136
+
"version": "1.0.1",
3137
+
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
3138
+
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
3139
+
"dev": true,
3140
+
"license": "MIT",
3141
+
"dependencies": {
3142
+
"call-bind-apply-helpers": "^1.0.1",
3143
+
"es-errors": "^1.3.0",
3144
+
"gopd": "^1.2.0"
3145
+
},
3146
+
"engines": {
3147
+
"node": ">= 0.4"
3148
+
}
3149
+
},
3150
+
"node_modules/duplexer": {
3151
+
"version": "0.1.2",
3152
+
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
3153
+
"integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
3154
+
"dev": true,
3155
+
"license": "MIT"
3156
+
},
2944
3157
"node_modules/electron-to-chromium": {
2945
3158
"version": "1.5.267",
2946
3159
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
···
2991
3204
"url": "https://github.com/sponsors/antfu"
2992
3205
}
2993
3206
},
3207
+
"node_modules/es-define-property": {
3208
+
"version": "1.0.1",
3209
+
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
3210
+
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
3211
+
"dev": true,
3212
+
"license": "MIT",
3213
+
"engines": {
3214
+
"node": ">= 0.4"
3215
+
}
3216
+
},
3217
+
"node_modules/es-errors": {
3218
+
"version": "1.3.0",
3219
+
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
3220
+
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
3221
+
"dev": true,
3222
+
"license": "MIT",
3223
+
"engines": {
3224
+
"node": ">= 0.4"
3225
+
}
3226
+
},
2994
3227
"node_modules/es-module-lexer": {
2995
3228
"version": "1.7.0",
2996
3229
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
2997
3230
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
2998
3231
"license": "MIT"
3232
+
},
3233
+
"node_modules/es-object-atoms": {
3234
+
"version": "1.1.1",
3235
+
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
3236
+
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
3237
+
"dev": true,
3238
+
"license": "MIT",
3239
+
"dependencies": {
3240
+
"es-errors": "^1.3.0"
3241
+
},
3242
+
"engines": {
3243
+
"node": ">= 0.4"
3244
+
}
3245
+
},
3246
+
"node_modules/es-set-tostringtag": {
3247
+
"version": "2.1.0",
3248
+
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
3249
+
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
3250
+
"dev": true,
3251
+
"license": "MIT",
3252
+
"dependencies": {
3253
+
"es-errors": "^1.3.0",
3254
+
"get-intrinsic": "^1.2.6",
3255
+
"has-tostringtag": "^1.0.2",
3256
+
"hasown": "^2.0.2"
3257
+
},
3258
+
"engines": {
3259
+
"node": ">= 0.4"
3260
+
}
2999
3261
},
3000
3262
"node_modules/escalade": {
3001
3263
"version": "3.2.0",
···
3292
3554
"node": ">=0.10.0"
3293
3555
}
3294
3556
},
3557
+
"node_modules/event-stream": {
3558
+
"version": "3.3.4",
3559
+
"resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
3560
+
"integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==",
3561
+
"dev": true,
3562
+
"license": "MIT",
3563
+
"dependencies": {
3564
+
"duplexer": "~0.1.1",
3565
+
"from": "~0",
3566
+
"map-stream": "~0.1.0",
3567
+
"pause-stream": "0.0.11",
3568
+
"split": "0.3",
3569
+
"stream-combiner": "~0.0.4",
3570
+
"through": "~2.3.1"
3571
+
}
3572
+
},
3295
3573
"node_modules/eventemitter3": {
3296
3574
"version": "5.0.1",
3297
3575
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
···
3308
3586
"engines": {
3309
3587
"node": ">=0.8.x"
3310
3588
}
3589
+
},
3590
+
"node_modules/execa": {
3591
+
"version": "5.1.1",
3592
+
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
3593
+
"integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
3594
+
"dev": true,
3595
+
"license": "MIT",
3596
+
"dependencies": {
3597
+
"cross-spawn": "^7.0.3",
3598
+
"get-stream": "^6.0.0",
3599
+
"human-signals": "^2.1.0",
3600
+
"is-stream": "^2.0.0",
3601
+
"merge-stream": "^2.0.0",
3602
+
"npm-run-path": "^4.0.1",
3603
+
"onetime": "^5.1.2",
3604
+
"signal-exit": "^3.0.3",
3605
+
"strip-final-newline": "^2.0.0"
3606
+
},
3607
+
"engines": {
3608
+
"node": ">=10"
3609
+
},
3610
+
"funding": {
3611
+
"url": "https://github.com/sindresorhus/execa?sponsor=1"
3612
+
}
3613
+
},
3614
+
"node_modules/execa/node_modules/onetime": {
3615
+
"version": "5.1.2",
3616
+
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
3617
+
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
3618
+
"dev": true,
3619
+
"license": "MIT",
3620
+
"dependencies": {
3621
+
"mimic-fn": "^2.1.0"
3622
+
},
3623
+
"engines": {
3624
+
"node": ">=6"
3625
+
},
3626
+
"funding": {
3627
+
"url": "https://github.com/sponsors/sindresorhus"
3628
+
}
3629
+
},
3630
+
"node_modules/execa/node_modules/signal-exit": {
3631
+
"version": "3.0.7",
3632
+
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
3633
+
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
3634
+
"dev": true,
3635
+
"license": "ISC"
3311
3636
},
3312
3637
"node_modules/exit-hook": {
3313
3638
"version": "2.2.1",
···
3451
3776
"dev": true,
3452
3777
"license": "ISC"
3453
3778
},
3779
+
"node_modules/follow-redirects": {
3780
+
"version": "1.15.11",
3781
+
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
3782
+
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
3783
+
"dev": true,
3784
+
"funding": [
3785
+
{
3786
+
"type": "individual",
3787
+
"url": "https://github.com/sponsors/RubenVerborgh"
3788
+
}
3789
+
],
3790
+
"license": "MIT",
3791
+
"engines": {
3792
+
"node": ">=4.0"
3793
+
},
3794
+
"peerDependenciesMeta": {
3795
+
"debug": {
3796
+
"optional": true
3797
+
}
3798
+
}
3799
+
},
3800
+
"node_modules/form-data": {
3801
+
"version": "4.0.5",
3802
+
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
3803
+
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
3804
+
"dev": true,
3805
+
"license": "MIT",
3806
+
"dependencies": {
3807
+
"asynckit": "^0.4.0",
3808
+
"combined-stream": "^1.0.8",
3809
+
"es-set-tostringtag": "^2.1.0",
3810
+
"hasown": "^2.0.2",
3811
+
"mime-types": "^2.1.12"
3812
+
},
3813
+
"engines": {
3814
+
"node": ">= 6"
3815
+
}
3816
+
},
3817
+
"node_modules/from": {
3818
+
"version": "0.1.7",
3819
+
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
3820
+
"integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==",
3821
+
"dev": true,
3822
+
"license": "MIT"
3823
+
},
3454
3824
"node_modules/fsevents": {
3455
3825
"version": "2.3.2",
3456
3826
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
···
3466
3836
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
3467
3837
}
3468
3838
},
3839
+
"node_modules/function-bind": {
3840
+
"version": "1.1.2",
3841
+
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
3842
+
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
3843
+
"dev": true,
3844
+
"license": "MIT",
3845
+
"funding": {
3846
+
"url": "https://github.com/sponsors/ljharb"
3847
+
}
3848
+
},
3469
3849
"node_modules/gensync": {
3470
3850
"version": "1.0.0-beta.2",
3471
3851
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
···
3489
3869
"url": "https://github.com/sponsors/sindresorhus"
3490
3870
}
3491
3871
},
3872
+
"node_modules/get-intrinsic": {
3873
+
"version": "1.3.0",
3874
+
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
3875
+
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
3876
+
"dev": true,
3877
+
"license": "MIT",
3878
+
"dependencies": {
3879
+
"call-bind-apply-helpers": "^1.0.2",
3880
+
"es-define-property": "^1.0.1",
3881
+
"es-errors": "^1.3.0",
3882
+
"es-object-atoms": "^1.1.1",
3883
+
"function-bind": "^1.1.2",
3884
+
"get-proto": "^1.0.1",
3885
+
"gopd": "^1.2.0",
3886
+
"has-symbols": "^1.1.0",
3887
+
"hasown": "^2.0.2",
3888
+
"math-intrinsics": "^1.1.0"
3889
+
},
3890
+
"engines": {
3891
+
"node": ">= 0.4"
3892
+
},
3893
+
"funding": {
3894
+
"url": "https://github.com/sponsors/ljharb"
3895
+
}
3896
+
},
3897
+
"node_modules/get-proto": {
3898
+
"version": "1.0.1",
3899
+
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
3900
+
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
3901
+
"dev": true,
3902
+
"license": "MIT",
3903
+
"dependencies": {
3904
+
"dunder-proto": "^1.0.1",
3905
+
"es-object-atoms": "^1.0.0"
3906
+
},
3907
+
"engines": {
3908
+
"node": ">= 0.4"
3909
+
}
3910
+
},
3911
+
"node_modules/get-stream": {
3912
+
"version": "6.0.1",
3913
+
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
3914
+
"integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
3915
+
"dev": true,
3916
+
"license": "MIT",
3917
+
"engines": {
3918
+
"node": ">=10"
3919
+
},
3920
+
"funding": {
3921
+
"url": "https://github.com/sponsors/sindresorhus"
3922
+
}
3923
+
},
3492
3924
"node_modules/glob-parent": {
3493
3925
"version": "6.0.2",
3494
3926
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
···
3521
3953
"url": "https://github.com/sponsors/sindresorhus"
3522
3954
}
3523
3955
},
3956
+
"node_modules/gopd": {
3957
+
"version": "1.2.0",
3958
+
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
3959
+
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
3960
+
"dev": true,
3961
+
"license": "MIT",
3962
+
"engines": {
3963
+
"node": ">= 0.4"
3964
+
},
3965
+
"funding": {
3966
+
"url": "https://github.com/sponsors/ljharb"
3967
+
}
3968
+
},
3524
3969
"node_modules/graceful-fs": {
3525
3970
"version": "4.2.11",
3526
3971
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
···
3537
3982
"node": ">=8"
3538
3983
}
3539
3984
},
3985
+
"node_modules/has-symbols": {
3986
+
"version": "1.1.0",
3987
+
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
3988
+
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
3989
+
"dev": true,
3990
+
"license": "MIT",
3991
+
"engines": {
3992
+
"node": ">= 0.4"
3993
+
},
3994
+
"funding": {
3995
+
"url": "https://github.com/sponsors/ljharb"
3996
+
}
3997
+
},
3998
+
"node_modules/has-tostringtag": {
3999
+
"version": "1.0.2",
4000
+
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
4001
+
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
4002
+
"dev": true,
4003
+
"license": "MIT",
4004
+
"dependencies": {
4005
+
"has-symbols": "^1.0.3"
4006
+
},
4007
+
"engines": {
4008
+
"node": ">= 0.4"
4009
+
},
4010
+
"funding": {
4011
+
"url": "https://github.com/sponsors/ljharb"
4012
+
}
4013
+
},
4014
+
"node_modules/hasown": {
4015
+
"version": "2.0.2",
4016
+
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
4017
+
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
4018
+
"dev": true,
4019
+
"license": "MIT",
4020
+
"dependencies": {
4021
+
"function-bind": "^1.1.2"
4022
+
},
4023
+
"engines": {
4024
+
"node": ">= 0.4"
4025
+
}
4026
+
},
3540
4027
"node_modules/hermes-estree": {
3541
4028
"version": "0.25.1",
3542
4029
"resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
···
3552
4039
"license": "MIT",
3553
4040
"dependencies": {
3554
4041
"hermes-estree": "0.25.1"
4042
+
}
4043
+
},
4044
+
"node_modules/human-signals": {
4045
+
"version": "2.1.0",
4046
+
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
4047
+
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
4048
+
"dev": true,
4049
+
"license": "Apache-2.0",
4050
+
"engines": {
4051
+
"node": ">=10.17.0"
3555
4052
}
3556
4053
},
3557
4054
"node_modules/husky": {
···
3663
4160
"node": ">=0.12.0"
3664
4161
}
3665
4162
},
4163
+
"node_modules/is-stream": {
4164
+
"version": "2.0.1",
4165
+
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
4166
+
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
4167
+
"dev": true,
4168
+
"license": "MIT",
4169
+
"engines": {
4170
+
"node": ">=8"
4171
+
},
4172
+
"funding": {
4173
+
"url": "https://github.com/sponsors/sindresorhus"
4174
+
}
4175
+
},
3666
4176
"node_modules/isexe": {
3667
4177
"version": "2.0.0",
3668
4178
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
···
3683
4193
},
3684
4194
"engines": {
3685
4195
"node": ">= 10.13.0"
4196
+
}
4197
+
},
4198
+
"node_modules/joi": {
4199
+
"version": "18.0.2",
4200
+
"resolved": "https://registry.npmjs.org/joi/-/joi-18.0.2.tgz",
4201
+
"integrity": "sha512-RuCOQMIt78LWnktPoeBL0GErkNaJPTBGcYuyaBvUOQSpcpcLfWrHPPihYdOGbV5pam9VTWbeoF7TsGiHugcjGA==",
4202
+
"dev": true,
4203
+
"license": "BSD-3-Clause",
4204
+
"dependencies": {
4205
+
"@hapi/address": "^5.1.1",
4206
+
"@hapi/formula": "^3.0.2",
4207
+
"@hapi/hoek": "^11.0.7",
4208
+
"@hapi/pinpoint": "^2.0.1",
4209
+
"@hapi/tlds": "^1.1.1",
4210
+
"@hapi/topo": "^6.0.2",
4211
+
"@standard-schema/spec": "^1.0.0"
4212
+
},
4213
+
"engines": {
4214
+
"node": ">= 20"
3686
4215
}
3687
4216
},
3688
4217
"node_modules/js-tokens": {
···
3777
4306
"license": "MIT",
3778
4307
"engines": {
3779
4308
"node": ">=6"
4309
+
}
4310
+
},
4311
+
"node_modules/lazy-ass": {
4312
+
"version": "1.6.0",
4313
+
"resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz",
4314
+
"integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==",
4315
+
"dev": true,
4316
+
"license": "MIT",
4317
+
"engines": {
4318
+
"node": "> 0.8"
3780
4319
}
3781
4320
},
3782
4321
"node_modules/levn": {
···
4137
4676
"url": "https://github.com/sponsors/sindresorhus"
4138
4677
}
4139
4678
},
4679
+
"node_modules/lodash": {
4680
+
"version": "4.17.21",
4681
+
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
4682
+
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
4683
+
"dev": true,
4684
+
"license": "MIT"
4685
+
},
4140
4686
"node_modules/lodash.merge": {
4141
4687
"version": "4.6.2",
4142
4688
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
···
4184
4730
"@jridgewell/sourcemap-codec": "^1.5.5"
4185
4731
}
4186
4732
},
4733
+
"node_modules/map-stream": {
4734
+
"version": "0.1.0",
4735
+
"resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
4736
+
"integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==",
4737
+
"dev": true
4738
+
},
4739
+
"node_modules/math-intrinsics": {
4740
+
"version": "1.1.0",
4741
+
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
4742
+
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
4743
+
"dev": true,
4744
+
"license": "MIT",
4745
+
"engines": {
4746
+
"node": ">= 0.4"
4747
+
}
4748
+
},
4187
4749
"node_modules/merge-stream": {
4188
4750
"version": "2.0.0",
4189
4751
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
4190
4752
"integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
4191
-
"license": "MIT",
4192
-
"peer": true
4753
+
"license": "MIT"
4193
4754
},
4194
4755
"node_modules/micromatch": {
4195
4756
"version": "4.0.8",
···
4223
4784
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
4224
4785
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
4225
4786
"license": "MIT",
4226
-
"peer": true,
4227
4787
"engines": {
4228
4788
"node": ">= 0.6"
4229
4789
}
···
4233
4793
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
4234
4794
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
4235
4795
"license": "MIT",
4236
-
"peer": true,
4237
4796
"dependencies": {
4238
4797
"mime-db": "1.52.0"
4239
4798
},
···
4241
4800
"node": ">= 0.6"
4242
4801
}
4243
4802
},
4803
+
"node_modules/mimic-fn": {
4804
+
"version": "2.1.0",
4805
+
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
4806
+
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
4807
+
"dev": true,
4808
+
"license": "MIT",
4809
+
"engines": {
4810
+
"node": ">=6"
4811
+
}
4812
+
},
4244
4813
"node_modules/mimic-function": {
4245
4814
"version": "5.0.1",
4246
4815
"resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
···
4329
4898
"node": "*"
4330
4899
}
4331
4900
},
4901
+
"node_modules/minimist": {
4902
+
"version": "1.2.8",
4903
+
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
4904
+
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
4905
+
"dev": true,
4906
+
"license": "MIT",
4907
+
"funding": {
4908
+
"url": "https://github.com/sponsors/ljharb"
4909
+
}
4910
+
},
4332
4911
"node_modules/mrmime": {
4333
4912
"version": "2.0.1",
4334
4913
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
···
4397
4976
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
4398
4977
"license": "MIT"
4399
4978
},
4979
+
"node_modules/npm-run-path": {
4980
+
"version": "4.0.1",
4981
+
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
4982
+
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
4983
+
"dev": true,
4984
+
"license": "MIT",
4985
+
"dependencies": {
4986
+
"path-key": "^3.0.0"
4987
+
},
4988
+
"engines": {
4989
+
"node": ">=8"
4990
+
}
4991
+
},
4400
4992
"node_modules/obug": {
4401
4993
"version": "2.1.1",
4402
4994
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
···
4513
5105
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
4514
5106
"dev": true,
4515
5107
"license": "MIT"
5108
+
},
5109
+
"node_modules/pause-stream": {
5110
+
"version": "0.0.11",
5111
+
"resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
5112
+
"integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==",
5113
+
"dev": true,
5114
+
"license": [
5115
+
"MIT",
5116
+
"Apache2"
5117
+
],
5118
+
"dependencies": {
5119
+
"through": "~2.3"
5120
+
}
4516
5121
},
4517
5122
"node_modules/picocolors": {
4518
5123
"version": "1.1.1",
···
4656
5261
"url": "https://github.com/prettier/prettier?sponsor=1"
4657
5262
}
4658
5263
},
5264
+
"node_modules/proxy-from-env": {
5265
+
"version": "1.1.0",
5266
+
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
5267
+
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
5268
+
"dev": true,
5269
+
"license": "MIT"
5270
+
},
5271
+
"node_modules/ps-tree": {
5272
+
"version": "1.2.0",
5273
+
"resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.2.0.tgz",
5274
+
"integrity": "sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==",
5275
+
"dev": true,
5276
+
"license": "MIT",
5277
+
"dependencies": {
5278
+
"event-stream": "=3.3.4"
5279
+
},
5280
+
"bin": {
5281
+
"ps-tree": "bin/ps-tree.js"
5282
+
},
5283
+
"engines": {
5284
+
"node": ">= 0.10"
5285
+
}
5286
+
},
4659
5287
"node_modules/punycode": {
4660
5288
"version": "2.3.1",
4661
5289
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
···
4809
5437
"dev": true,
4810
5438
"license": "MIT"
4811
5439
},
5440
+
"node_modules/rxjs": {
5441
+
"version": "7.8.2",
5442
+
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
5443
+
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
5444
+
"dev": true,
5445
+
"license": "Apache-2.0",
5446
+
"dependencies": {
5447
+
"tslib": "^2.1.0"
5448
+
}
5449
+
},
4812
5450
"node_modules/safe-buffer": {
4813
5451
"version": "5.2.1",
4814
5452
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
···
5058
5696
"source-map": "^0.6.0"
5059
5697
}
5060
5698
},
5699
+
"node_modules/split": {
5700
+
"version": "0.3.3",
5701
+
"resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
5702
+
"integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==",
5703
+
"dev": true,
5704
+
"license": "MIT",
5705
+
"dependencies": {
5706
+
"through": "2"
5707
+
},
5708
+
"engines": {
5709
+
"node": "*"
5710
+
}
5711
+
},
5061
5712
"node_modules/stackback": {
5062
5713
"version": "0.0.2",
5063
5714
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
···
5065
5716
"dev": true,
5066
5717
"license": "MIT"
5067
5718
},
5719
+
"node_modules/start-server-and-test": {
5720
+
"version": "2.1.3",
5721
+
"resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.1.3.tgz",
5722
+
"integrity": "sha512-k4EcbNjeg0odaDkAMlIeDVDByqX9PIgL4tivgP2tES6Zd8o+4pTq/HgbWCyA3VHIoZopB+wGnNPKYGGSByNriQ==",
5723
+
"dev": true,
5724
+
"license": "MIT",
5725
+
"dependencies": {
5726
+
"arg": "^5.0.2",
5727
+
"bluebird": "3.7.2",
5728
+
"check-more-types": "2.24.0",
5729
+
"debug": "4.4.3",
5730
+
"execa": "5.1.1",
5731
+
"lazy-ass": "1.6.0",
5732
+
"ps-tree": "1.2.0",
5733
+
"wait-on": "9.0.3"
5734
+
},
5735
+
"bin": {
5736
+
"server-test": "src/bin/start.js",
5737
+
"start-server-and-test": "src/bin/start.js",
5738
+
"start-test": "src/bin/start.js"
5739
+
},
5740
+
"engines": {
5741
+
"node": ">=16"
5742
+
}
5743
+
},
5068
5744
"node_modules/std-env": {
5069
5745
"version": "3.10.0",
5070
5746
"resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
···
5083
5759
"npm": ">=6"
5084
5760
}
5085
5761
},
5762
+
"node_modules/stream-combiner": {
5763
+
"version": "0.0.4",
5764
+
"resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
5765
+
"integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==",
5766
+
"dev": true,
5767
+
"license": "MIT",
5768
+
"dependencies": {
5769
+
"duplexer": "~0.1.1"
5770
+
}
5771
+
},
5086
5772
"node_modules/string-argv": {
5087
5773
"version": "0.3.2",
5088
5774
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
···
5124
5810
},
5125
5811
"funding": {
5126
5812
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
5813
+
}
5814
+
},
5815
+
"node_modules/strip-final-newline": {
5816
+
"version": "2.0.0",
5817
+
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
5818
+
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
5819
+
"dev": true,
5820
+
"license": "MIT",
5821
+
"engines": {
5822
+
"node": ">=6"
5127
5823
}
5128
5824
},
5129
5825
"node_modules/strip-json-comments": {
···
5236
5932
"deprecated": "no longer maintained",
5237
5933
"license": "(Unlicense OR Apache-2.0)"
5238
5934
},
5935
+
"node_modules/through": {
5936
+
"version": "2.3.8",
5937
+
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
5938
+
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
5939
+
"dev": true,
5940
+
"license": "MIT"
5941
+
},
5239
5942
"node_modules/tinybench": {
5240
5943
"version": "2.9.0",
5241
5944
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
···
5321
6024
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
5322
6025
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
5323
6026
"dev": true,
5324
-
"license": "0BSD",
5325
-
"optional": true
6027
+
"license": "0BSD"
5326
6028
},
5327
6029
"node_modules/type-check": {
5328
6030
"version": "0.4.0",
···
5343
6045
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
5344
6046
"dev": true,
5345
6047
"license": "Apache-2.0",
5346
-
"peer": true,
5347
6048
"bin": {
5348
6049
"tsc": "bin/tsc",
5349
6050
"tsserver": "bin/tsserver"
···
5390
6091
"version": "7.16.0",
5391
6092
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
5392
6093
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
5393
-
"license": "MIT",
5394
-
"peer": true
6094
+
"license": "MIT"
5395
6095
},
5396
6096
"node_modules/unenv": {
5397
6097
"version": "2.0.0-rc.24",
···
5644
6344
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
5645
6345
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
5646
6346
"license": "MIT"
6347
+
},
6348
+
"node_modules/wait-on": {
6349
+
"version": "9.0.3",
6350
+
"resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.0.3.tgz",
6351
+
"integrity": "sha512-13zBnyYvFDW1rBvWiJ6Av3ymAaq8EDQuvxZnPIw3g04UqGi4TyoIJABmfJ6zrvKo9yeFQExNkOk7idQbDJcuKA==",
6352
+
"dev": true,
6353
+
"license": "MIT",
6354
+
"dependencies": {
6355
+
"axios": "^1.13.2",
6356
+
"joi": "^18.0.1",
6357
+
"lodash": "^4.17.21",
6358
+
"minimist": "^1.2.8",
6359
+
"rxjs": "^7.8.2"
6360
+
},
6361
+
"bin": {
6362
+
"wait-on": "bin/wait-on"
6363
+
},
6364
+
"engines": {
6365
+
"node": ">=20.0.0"
6366
+
}
5647
6367
},
5648
6368
"node_modules/watchpack": {
5649
6369
"version": "2.4.4",
+11
-1
package.json
+11
-1
package.json
···
12
12
"preview": "vite preview",
13
13
"deploy": "npm install && npm run lint && npm test && npm run build:versions && wrangler pages deploy dist --project-name=rscexplorer",
14
14
"test": "vitest run --reporter=verbose",
15
-
"lint": "eslint .",
15
+
"typecheck": "tsc",
16
+
"lint": "tsc && eslint .",
16
17
"format": "prettier --write .",
17
18
"format:check": "prettier --check .",
18
19
"prepare": "husky"
···
41
42
"vite": "8.0.0-beta.2"
42
43
},
43
44
"devDependencies": {
45
+
"@babel/types": "^7.28.5",
44
46
"@eslint/js": "^9.39.2",
47
+
"@types/babel__core": "^7.20.5",
48
+
"@types/babel__standalone": "^7.1.9",
49
+
"@types/node": "^25.0.2",
50
+
"@types/react": "^19.2.7",
51
+
"@types/react-dom": "^19.2.3",
52
+
"@types/text-encoding": "^0.0.40",
45
53
"@vitejs/plugin-react": "^5.1.2",
46
54
"@vitest/browser": "^4.0.15",
47
55
"@vitest/browser-playwright": "^4.0.15",
···
52
60
"playwright": "^1.57.0",
53
61
"prettier": "^3.7.4",
54
62
"rolldown": "^1.0.0-beta.54",
63
+
"start-server-and-test": "^2.1.3",
64
+
"typescript": "^5.9.3",
55
65
"typescript-eslint": "^8.50.0",
56
66
"vite": "8.0.0-beta.2",
57
67
"vitest": "^4.0.15",
+2
-1
src/client/byte-stream-polyfill.js
src/client/byte-stream-polyfill.ts
+2
-1
src/client/byte-stream-polyfill.js
src/client/byte-stream-polyfill.ts
···
10
10
if (typeof globalThis.ReadableByteStreamController === "undefined") {
11
11
// Safari doesn't have byte stream support - use the polyfill's ReadableStream
12
12
// which includes full byte stream support
13
-
globalThis.ReadableStream = PolyfillReadableStream;
13
+
globalThis.ReadableStream = PolyfillReadableStream as typeof ReadableStream;
14
+
// @ts-expect-error: ReadableByteStreamController polyfill assignment
14
15
globalThis.ReadableByteStreamController = PolyfillReadableByteStreamController;
15
16
}
-116
src/client/embed.jsx
-116
src/client/embed.jsx
···
1
-
// Must be first - shims webpack globals for react-server-dom-webpack
2
-
import "./webpack-shim.js";
3
-
4
-
import "./byte-stream-polyfill.js";
5
-
import "web-streams-polyfill/polyfill";
6
-
import "text-encoding";
7
-
8
-
import React, { useState, useEffect } from "react";
9
-
import { createRoot } from "react-dom/client";
10
-
import { Workspace } from "./ui/Workspace.jsx";
11
-
import "./styles/workspace.css";
12
-
13
-
// Default code shown when no code is provided
14
-
const DEFAULT_SERVER = `export default function App() {
15
-
return <h1>RSC Explorer</h1>;
16
-
}`;
17
-
18
-
const DEFAULT_CLIENT = `'use client'
19
-
20
-
export function Button({ children }) {
21
-
return <button>{children}</button>;
22
-
}`;
23
-
24
-
function EmbedApp() {
25
-
const [code, setCode] = useState(null);
26
-
const [showFullscreen, setShowFullscreen] = useState(false);
27
-
28
-
useEffect(() => {
29
-
const handleMessage = (event) => {
30
-
const { data } = event;
31
-
if (data?.type === "rsc-embed:init") {
32
-
setCode({
33
-
server: (data.code?.server || DEFAULT_SERVER).trim(),
34
-
client: (data.code?.client || DEFAULT_CLIENT).trim(),
35
-
});
36
-
if (data.showFullscreen !== false) {
37
-
setShowFullscreen(true);
38
-
}
39
-
}
40
-
};
41
-
42
-
window.addEventListener("message", handleMessage);
43
-
44
-
// Signal to parent that we're ready to receive code
45
-
if (window.parent !== window) {
46
-
window.parent.postMessage({ type: "rsc-embed:ready" }, "*");
47
-
}
48
-
49
-
return () => window.removeEventListener("message", handleMessage);
50
-
}, []);
51
-
52
-
// Report code changes back to parent
53
-
const handleCodeChange = (server, client) => {
54
-
if (window.parent !== window) {
55
-
window.parent.postMessage(
56
-
{
57
-
type: "rsc-embed:code-changed",
58
-
code: { server, client },
59
-
},
60
-
"*",
61
-
);
62
-
}
63
-
};
64
-
65
-
// Generate fullscreen URL
66
-
const getFullscreenUrl = () => {
67
-
if (!code) return "#";
68
-
const json = JSON.stringify({ server: code.server, client: code.client });
69
-
const encoded = encodeURIComponent(btoa(unescape(encodeURIComponent(json))));
70
-
return `https://rscexplorer.dev/?c=${encoded}`;
71
-
};
72
-
73
-
// Show nothing until we receive code from parent
74
-
if (!code) {
75
-
return null;
76
-
}
77
-
78
-
return (
79
-
<>
80
-
{showFullscreen && (
81
-
<div className="embed-header">
82
-
<span className="embed-title">RSC Explorer</span>
83
-
<a
84
-
href={getFullscreenUrl()}
85
-
target="_blank"
86
-
rel="noopener noreferrer"
87
-
className="embed-fullscreen-link"
88
-
title="Open in RSC Explorer"
89
-
>
90
-
<svg
91
-
width="14"
92
-
height="14"
93
-
viewBox="0 0 24 24"
94
-
fill="none"
95
-
stroke="currentColor"
96
-
strokeWidth="2"
97
-
>
98
-
<path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3" />
99
-
</svg>
100
-
</a>
101
-
</div>
102
-
)}
103
-
<Workspace
104
-
key={`${code.server}:${code.client}`}
105
-
initialServerCode={code.server}
106
-
initialClientCode={code.client}
107
-
onCodeChange={handleCodeChange}
108
-
/>
109
-
</>
110
-
);
111
-
}
112
-
113
-
document.addEventListener("DOMContentLoaded", () => {
114
-
const root = createRoot(document.getElementById("embed-root"));
115
-
root.render(<EmbedApp />);
116
-
});
+148
src/client/embed.tsx
+148
src/client/embed.tsx
···
1
+
// Must be first - shims webpack globals for react-server-dom-webpack
2
+
import "./webpack-shim.ts";
3
+
4
+
import "./byte-stream-polyfill.ts";
5
+
import "web-streams-polyfill/polyfill";
6
+
import "text-encoding";
7
+
8
+
import React, { useState, useEffect } from "react";
9
+
import { createRoot } from "react-dom/client";
10
+
import { Workspace } from "./ui/Workspace.tsx";
11
+
import "./styles/workspace.css";
12
+
13
+
const DEFAULT_SERVER = `export default function App() {
14
+
return <h1>RSC Explorer</h1>;
15
+
}`;
16
+
17
+
const DEFAULT_CLIENT = `'use client'
18
+
19
+
export function Button({ children }) {
20
+
return <button>{children}</button>;
21
+
}`;
22
+
23
+
type CodeState = {
24
+
server: string;
25
+
client: string;
26
+
};
27
+
28
+
type EmbedInitMessage = {
29
+
type: "rsc-embed:init";
30
+
code?: {
31
+
server?: string;
32
+
client?: string;
33
+
};
34
+
showFullscreen?: boolean;
35
+
};
36
+
37
+
type EmbedReadyMessage = {
38
+
type: "rsc-embed:ready";
39
+
};
40
+
41
+
type EmbedCodeChangedMessage = {
42
+
type: "rsc-embed:code-changed";
43
+
code: {
44
+
server: string;
45
+
client: string;
46
+
};
47
+
};
48
+
49
+
function isEmbedInitMessage(data: unknown): data is EmbedInitMessage {
50
+
return (
51
+
typeof data === "object" &&
52
+
data !== null &&
53
+
(data as { type?: string }).type === "rsc-embed:init"
54
+
);
55
+
}
56
+
57
+
function EmbedApp(): React.ReactElement | null {
58
+
const [code, setCode] = useState<CodeState | null>(null);
59
+
const [showFullscreen, setShowFullscreen] = useState(false);
60
+
61
+
useEffect(() => {
62
+
const handleMessage = (event: MessageEvent<unknown>): void => {
63
+
const { data } = event;
64
+
if (isEmbedInitMessage(data)) {
65
+
setCode({
66
+
server: (data.code?.server ?? DEFAULT_SERVER).trim(),
67
+
client: (data.code?.client ?? DEFAULT_CLIENT).trim(),
68
+
});
69
+
if (data.showFullscreen !== false) {
70
+
setShowFullscreen(true);
71
+
}
72
+
}
73
+
};
74
+
75
+
window.addEventListener("message", handleMessage);
76
+
77
+
if (window.parent !== window) {
78
+
const readyMessage: EmbedReadyMessage = { type: "rsc-embed:ready" };
79
+
window.parent.postMessage(readyMessage, "*");
80
+
}
81
+
82
+
return () => window.removeEventListener("message", handleMessage);
83
+
}, []);
84
+
85
+
const handleCodeChange = (server: string, client: string): void => {
86
+
if (window.parent !== window) {
87
+
const changedMessage: EmbedCodeChangedMessage = {
88
+
type: "rsc-embed:code-changed",
89
+
code: { server, client },
90
+
};
91
+
window.parent.postMessage(changedMessage, "*");
92
+
}
93
+
};
94
+
95
+
const getFullscreenUrl = (): string => {
96
+
if (!code) return "#";
97
+
const json = JSON.stringify({ server: code.server, client: code.client });
98
+
const encoded = encodeURIComponent(btoa(unescape(encodeURIComponent(json))));
99
+
return `https://rscexplorer.dev/?c=${encoded}`;
100
+
};
101
+
102
+
if (!code) {
103
+
return null;
104
+
}
105
+
106
+
return (
107
+
<>
108
+
{showFullscreen && (
109
+
<div className="embed-header">
110
+
<span className="embed-title">RSC Explorer</span>
111
+
<a
112
+
href={getFullscreenUrl()}
113
+
target="_blank"
114
+
rel="noopener noreferrer"
115
+
className="embed-fullscreen-link"
116
+
title="Open in RSC Explorer"
117
+
>
118
+
<svg
119
+
width="14"
120
+
height="14"
121
+
viewBox="0 0 24 24"
122
+
fill="none"
123
+
stroke="currentColor"
124
+
strokeWidth="2"
125
+
>
126
+
<path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3" />
127
+
</svg>
128
+
</a>
129
+
</div>
130
+
)}
131
+
<Workspace
132
+
key={`${code.server}:${code.client}`}
133
+
initialServerCode={code.server}
134
+
initialClientCode={code.client}
135
+
onCodeChange={handleCodeChange}
136
+
/>
137
+
</>
138
+
);
139
+
}
140
+
141
+
document.addEventListener("DOMContentLoaded", () => {
142
+
const container = document.getElementById("embed-root");
143
+
if (!container) {
144
+
throw new Error("Could not find #embed-root element");
145
+
}
146
+
const root = createRoot(container);
147
+
root.render(<EmbedApp />);
148
+
});
-16
src/client/index.jsx
-16
src/client/index.jsx
···
1
-
// Must be first - shims webpack globals for react-server-dom-webpack
2
-
import "./webpack-shim.js";
3
-
4
-
import "./byte-stream-polyfill.js";
5
-
import "web-streams-polyfill/polyfill";
6
-
import "text-encoding";
7
-
8
-
import React from "react";
9
-
import { createRoot } from "react-dom/client";
10
-
import { App } from "./ui/App.jsx";
11
-
12
-
// Mount the app
13
-
document.addEventListener("DOMContentLoaded", () => {
14
-
const root = createRoot(document.getElementById("app"));
15
-
root.render(<App />);
16
-
});
+18
src/client/index.tsx
+18
src/client/index.tsx
···
1
+
// Must be first - shims webpack globals for react-server-dom-webpack
2
+
import "./webpack-shim.ts";
3
+
4
+
import "./byte-stream-polyfill.ts";
5
+
import "web-streams-polyfill/polyfill";
6
+
import "text-encoding";
7
+
8
+
import { createRoot } from "react-dom/client";
9
+
import { App } from "./ui/App.tsx";
10
+
11
+
document.addEventListener("DOMContentLoaded", () => {
12
+
const container = document.getElementById("app");
13
+
if (!container) {
14
+
throw new Error("Could not find #app element");
15
+
}
16
+
const root = createRoot(container);
17
+
root.render(<App />);
18
+
});
-3
src/client/runtime/index.js
-3
src/client/runtime/index.js
+15
src/client/runtime/index.ts
+15
src/client/runtime/index.ts
···
1
+
export { registerClientModule, evaluateClientModule } from "./module-registry.ts";
2
+
export {
3
+
SteppableStream,
4
+
type SteppableStreamOptions,
5
+
type Thenable,
6
+
type CallServerCallback,
7
+
} from "./steppable-stream.ts";
8
+
export {
9
+
Timeline,
10
+
type TimelineEntry,
11
+
type RenderEntry,
12
+
type ActionEntry,
13
+
type TimelineSnapshot,
14
+
type TimelinePosition,
15
+
} from "./timeline.ts";
-18
src/client/runtime/module-registry.js
-18
src/client/runtime/module-registry.js
···
1
-
import React from "react";
2
-
3
-
export function registerClientModule(moduleId, moduleExports) {
4
-
if (typeof __webpack_module_cache__ !== "undefined") {
5
-
__webpack_module_cache__[moduleId] = { exports: moduleExports };
6
-
}
7
-
}
8
-
9
-
export function evaluateClientModule(compiledCode) {
10
-
const module = { exports: {} };
11
-
const require = (id) => {
12
-
if (id === "react") return React;
13
-
throw new Error(`Module "${id}" not found in client context`);
14
-
};
15
-
const fn = new Function("module", "exports", "require", "React", compiledCode);
16
-
fn(module, module.exports, require, React);
17
-
return module.exports;
18
-
}
+28
src/client/runtime/module-registry.ts
+28
src/client/runtime/module-registry.ts
···
1
+
import React from "react";
2
+
3
+
declare const __webpack_module_cache__: Record<string, { exports: unknown }>;
4
+
5
+
export function registerClientModule(moduleId: string, moduleExports: unknown): void {
6
+
if (typeof __webpack_module_cache__ !== "undefined") {
7
+
__webpack_module_cache__[moduleId] = { exports: moduleExports };
8
+
}
9
+
}
10
+
11
+
type ModuleExports = Record<string, unknown>;
12
+
type RequireFn = (id: string) => unknown;
13
+
14
+
export function evaluateClientModule(compiledCode: string): ModuleExports {
15
+
const module: { exports: ModuleExports } = { exports: {} };
16
+
const require: RequireFn = (id: string): unknown => {
17
+
if (id === "react") return React;
18
+
throw new Error(`Module "${id}" not found in client context`);
19
+
};
20
+
const fn = new Function("module", "exports", "require", "React", compiledCode) as (
21
+
module: { exports: ModuleExports },
22
+
exports: ModuleExports,
23
+
require: RequireFn,
24
+
ReactLib: typeof React,
25
+
) => void;
26
+
fn(module, module.exports, require, React);
27
+
return module.exports;
28
+
}
-68
src/client/runtime/steppable-stream.js
-68
src/client/runtime/steppable-stream.js
···
1
-
import { createFromReadableStream } from "react-server-dom-webpack/client";
2
-
3
-
/**
4
-
* SteppableStream - makes a Flight stream steppable for debugging.
5
-
*
6
-
* Buffers incoming rows and controls their release to the Flight decoder.
7
-
* The flightPromise only resolves when all rows have been released.
8
-
*/
9
-
export class SteppableStream {
10
-
constructor(source, { callServer } = {}) {
11
-
this.rows = [];
12
-
this.releasedCount = 0;
13
-
this.buffered = false;
14
-
this.closed = false;
15
-
16
-
const encoder = new TextEncoder();
17
-
let controller;
18
-
const output = new ReadableStream({
19
-
start: (c) => {
20
-
controller = c;
21
-
},
22
-
});
23
-
24
-
this.release = (count) => {
25
-
while (this.releasedCount < count && this.releasedCount < this.rows.length) {
26
-
controller.enqueue(encoder.encode(this.rows[this.releasedCount] + "\n"));
27
-
this.releasedCount++;
28
-
}
29
-
if (this.releasedCount >= this.rows.length && this.buffered && !this.closed) {
30
-
controller.close();
31
-
this.closed = true;
32
-
}
33
-
};
34
-
35
-
this.flightPromise = createFromReadableStream(output, { callServer });
36
-
this.bufferPromise = this.buffer(source);
37
-
}
38
-
39
-
async buffer(stream) {
40
-
const reader = stream.getReader();
41
-
const decoder = new TextDecoder();
42
-
let partial = "";
43
-
44
-
try {
45
-
while (true) {
46
-
const { done, value } = await reader.read();
47
-
if (done) break;
48
-
49
-
partial += decoder.decode(value, { stream: true });
50
-
const lines = partial.split("\n");
51
-
partial = lines.pop();
52
-
53
-
for (const line of lines) {
54
-
if (line.trim()) this.rows.push(line);
55
-
}
56
-
}
57
-
58
-
partial += decoder.decode();
59
-
if (partial.trim()) this.rows.push(partial);
60
-
} finally {
61
-
this.buffered = true;
62
-
}
63
-
}
64
-
65
-
async waitForBuffer() {
66
-
await this.bufferPromise;
67
-
}
68
-
}
+94
src/client/runtime/steppable-stream.ts
+94
src/client/runtime/steppable-stream.ts
···
1
+
import {
2
+
createFromReadableStream,
3
+
type CallServerCallback as ImportedCallServerCallback,
4
+
} from "react-server-dom-webpack/client";
5
+
6
+
export type CallServerCallback = ImportedCallServerCallback;
7
+
8
+
// React's Thenable type (not exported from react package)
9
+
export interface Thenable<T> {
10
+
then<TResult1 = T, TResult2 = never>(
11
+
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
12
+
onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
13
+
): PromiseLike<TResult1 | TResult2>;
14
+
}
15
+
16
+
export interface SteppableStreamOptions {
17
+
callServer?: CallServerCallback;
18
+
}
19
+
20
+
/**
21
+
* SteppableStream - makes a Flight stream steppable for debugging.
22
+
*
23
+
* Buffers incoming rows and controls their release to the Flight decoder.
24
+
* The flightPromise only resolves when all rows have been released.
25
+
*/
26
+
export class SteppableStream {
27
+
rows: string[] = [];
28
+
releasedCount = 0;
29
+
buffered = false;
30
+
closed = false;
31
+
release: (count: number) => void;
32
+
flightPromise: Thenable<unknown>;
33
+
bufferPromise: Promise<void>;
34
+
35
+
constructor(source: ReadableStream<Uint8Array>, options: SteppableStreamOptions = {}) {
36
+
const { callServer } = options;
37
+
38
+
const encoder = new TextEncoder();
39
+
let controller: ReadableStreamDefaultController<Uint8Array>;
40
+
const output = new ReadableStream<Uint8Array>({
41
+
start: (c) => {
42
+
controller = c;
43
+
},
44
+
});
45
+
46
+
this.release = (count: number): void => {
47
+
while (this.releasedCount < count && this.releasedCount < this.rows.length) {
48
+
const row = this.rows[this.releasedCount];
49
+
if (row !== undefined) {
50
+
controller.enqueue(encoder.encode(row + "\n"));
51
+
}
52
+
this.releasedCount++;
53
+
}
54
+
if (this.releasedCount >= this.rows.length && this.buffered && !this.closed) {
55
+
controller.close();
56
+
this.closed = true;
57
+
}
58
+
};
59
+
60
+
const streamOptions = callServer ? { callServer } : {};
61
+
this.flightPromise = createFromReadableStream(output, streamOptions);
62
+
this.bufferPromise = this.buffer(source);
63
+
}
64
+
65
+
private async buffer(stream: ReadableStream<Uint8Array>): Promise<void> {
66
+
const reader = stream.getReader();
67
+
const decoder = new TextDecoder();
68
+
let partial = "";
69
+
70
+
try {
71
+
while (true) {
72
+
const { done, value } = await reader.read();
73
+
if (done) break;
74
+
75
+
partial += decoder.decode(value, { stream: true });
76
+
const lines = partial.split("\n");
77
+
partial = lines.pop() ?? "";
78
+
79
+
for (const line of lines) {
80
+
if (line.trim()) this.rows.push(line);
81
+
}
82
+
}
83
+
84
+
partial += decoder.decode();
85
+
if (partial.trim()) this.rows.push(partial);
86
+
} finally {
87
+
this.buffered = true;
88
+
}
89
+
}
90
+
91
+
async waitForBuffer(): Promise<void> {
92
+
await this.bufferPromise;
93
+
}
94
+
}
-125
src/client/runtime/timeline.js
-125
src/client/runtime/timeline.js
···
1
-
/**
2
-
* Timeline - manages a sequence of Flight responses for debugging.
3
-
*
4
-
* Each entry owns its SteppableStream(s). The cursor controls playback.
5
-
* Stepping releases data to streams; I/O is handled externally.
6
-
*
7
-
* Entry types:
8
-
* - render: { type, stream } - initial render
9
-
* - action: { type, name, args, stream } - action invoked from client or added manually
10
-
*/
11
-
export class Timeline {
12
-
constructor() {
13
-
this.entries = [];
14
-
this.cursor = 0;
15
-
this.listeners = new Set();
16
-
this.snapshot = null;
17
-
}
18
-
19
-
subscribe = (listener) => {
20
-
this.listeners.add(listener);
21
-
return () => this.listeners.delete(listener);
22
-
};
23
-
24
-
notify() {
25
-
this.snapshot = null; // Invalidate cache
26
-
this.listeners.forEach((fn) => fn());
27
-
}
28
-
29
-
getChunkCount(entry) {
30
-
return entry.stream?.rows?.length || 0;
31
-
}
32
-
33
-
getTotalChunks() {
34
-
return this.entries.reduce((sum, e) => sum + this.getChunkCount(e), 0);
35
-
}
36
-
37
-
getPosition(globalChunk) {
38
-
let remaining = globalChunk;
39
-
for (let i = 0; i < this.entries.length; i++) {
40
-
const count = this.getChunkCount(this.entries[i]);
41
-
if (remaining < count) {
42
-
return { entryIndex: i, localChunk: remaining };
43
-
}
44
-
remaining -= count;
45
-
}
46
-
return null;
47
-
}
48
-
49
-
getEntryStart(entryIndex) {
50
-
let start = 0;
51
-
for (let i = 0; i < entryIndex; i++) {
52
-
start += this.getChunkCount(this.entries[i]);
53
-
}
54
-
return start;
55
-
}
56
-
57
-
canDeleteEntry(entryIndex) {
58
-
if (entryIndex < 0 || entryIndex >= this.entries.length) return false;
59
-
return this.cursor <= this.getEntryStart(entryIndex);
60
-
}
61
-
62
-
// For useSyncExternalStore compatibility - must return cached object
63
-
getSnapshot = () => {
64
-
if (this.snapshot) return this.snapshot;
65
-
66
-
const totalChunks = this.getTotalChunks();
67
-
this.snapshot = {
68
-
entries: this.entries,
69
-
cursor: this.cursor,
70
-
totalChunks,
71
-
isAtStart: this.cursor === 0,
72
-
isAtEnd: this.cursor >= totalChunks,
73
-
};
74
-
return this.snapshot;
75
-
};
76
-
77
-
setRender(stream) {
78
-
this.entries = [{ type: "render", stream }];
79
-
this.cursor = 0;
80
-
this.notify();
81
-
}
82
-
83
-
addAction(name, args, stream) {
84
-
this.entries = [...this.entries, { type: "action", name, args, stream }];
85
-
this.notify();
86
-
}
87
-
88
-
deleteEntry(entryIndex) {
89
-
if (!this.canDeleteEntry(entryIndex)) return false;
90
-
this.entries = this.entries.filter((_, i) => i !== entryIndex);
91
-
this.notify();
92
-
return true;
93
-
}
94
-
95
-
stepForward() {
96
-
const total = this.getTotalChunks();
97
-
if (this.cursor >= total) return;
98
-
99
-
const pos = this.getPosition(this.cursor);
100
-
if (!pos) return;
101
-
102
-
const entry = this.entries[pos.entryIndex];
103
-
this.cursor++;
104
-
entry.stream.release(pos.localChunk + 1);
105
-
106
-
this.notify();
107
-
}
108
-
109
-
skipToEntryEnd() {
110
-
const pos = this.getPosition(this.cursor);
111
-
if (!pos) return;
112
-
113
-
const entryEnd =
114
-
this.getEntryStart(pos.entryIndex) + this.getChunkCount(this.entries[pos.entryIndex]);
115
-
while (this.cursor < entryEnd) {
116
-
this.stepForward();
117
-
}
118
-
}
119
-
120
-
clear() {
121
-
this.entries = [];
122
-
this.cursor = 0;
123
-
this.notify();
124
-
}
125
-
}
+166
src/client/runtime/timeline.ts
+166
src/client/runtime/timeline.ts
···
1
+
import type { SteppableStream } from "./steppable-stream.ts";
2
+
3
+
/**
4
+
* Timeline - manages a sequence of Flight responses for debugging.
5
+
*
6
+
* Each entry owns its SteppableStream(s). The cursor controls playback.
7
+
* Stepping releases data to streams; I/O is handled externally.
8
+
*
9
+
* Entry types:
10
+
* - render: { type, stream } - initial render
11
+
* - action: { type, name, args, stream } - action invoked from client or added manually
12
+
*/
13
+
14
+
export interface RenderEntry {
15
+
type: "render";
16
+
stream: SteppableStream;
17
+
}
18
+
19
+
export interface ActionEntry {
20
+
type: "action";
21
+
name: string;
22
+
args: string;
23
+
stream: SteppableStream;
24
+
}
25
+
26
+
export type TimelineEntry = RenderEntry | ActionEntry;
27
+
28
+
export interface TimelineSnapshot {
29
+
entries: TimelineEntry[];
30
+
cursor: number;
31
+
totalChunks: number;
32
+
isAtStart: boolean;
33
+
isAtEnd: boolean;
34
+
}
35
+
36
+
export interface TimelinePosition {
37
+
entryIndex: number;
38
+
localChunk: number;
39
+
}
40
+
41
+
type TimelineListener = () => void;
42
+
43
+
export class Timeline {
44
+
entries: TimelineEntry[] = [];
45
+
cursor = 0;
46
+
private listeners: Set<TimelineListener> = new Set();
47
+
private snapshot: TimelineSnapshot | null = null;
48
+
49
+
subscribe = (listener: TimelineListener): (() => void) => {
50
+
this.listeners.add(listener);
51
+
return () => {
52
+
this.listeners.delete(listener);
53
+
};
54
+
};
55
+
56
+
private notify(): void {
57
+
this.snapshot = null; // Invalidate cache
58
+
this.listeners.forEach((fn) => fn());
59
+
}
60
+
61
+
getChunkCount(entry: TimelineEntry): number {
62
+
return entry.stream.rows.length;
63
+
}
64
+
65
+
getTotalChunks(): number {
66
+
return this.entries.reduce((sum, e) => sum + this.getChunkCount(e), 0);
67
+
}
68
+
69
+
getPosition(globalChunk: number): TimelinePosition | null {
70
+
let remaining = globalChunk;
71
+
for (let i = 0; i < this.entries.length; i++) {
72
+
const entry = this.entries[i];
73
+
if (!entry) continue;
74
+
const count = this.getChunkCount(entry);
75
+
if (remaining < count) {
76
+
return { entryIndex: i, localChunk: remaining };
77
+
}
78
+
remaining -= count;
79
+
}
80
+
return null;
81
+
}
82
+
83
+
getEntryStart(entryIndex: number): number {
84
+
let start = 0;
85
+
for (let i = 0; i < entryIndex; i++) {
86
+
const entry = this.entries[i];
87
+
if (entry) {
88
+
start += this.getChunkCount(entry);
89
+
}
90
+
}
91
+
return start;
92
+
}
93
+
94
+
canDeleteEntry(entryIndex: number): boolean {
95
+
if (entryIndex < 0 || entryIndex >= this.entries.length) return false;
96
+
return this.cursor <= this.getEntryStart(entryIndex);
97
+
}
98
+
99
+
// For useSyncExternalStore compatibility - must return cached object
100
+
getSnapshot = (): TimelineSnapshot => {
101
+
if (this.snapshot) return this.snapshot;
102
+
103
+
const totalChunks = this.getTotalChunks();
104
+
this.snapshot = {
105
+
entries: this.entries,
106
+
cursor: this.cursor,
107
+
totalChunks,
108
+
isAtStart: this.cursor === 0,
109
+
isAtEnd: this.cursor >= totalChunks,
110
+
};
111
+
return this.snapshot;
112
+
};
113
+
114
+
setRender(stream: SteppableStream): void {
115
+
this.entries = [{ type: "render", stream }];
116
+
this.cursor = 0;
117
+
this.notify();
118
+
}
119
+
120
+
addAction(name: string, args: string, stream: SteppableStream): void {
121
+
this.entries = [...this.entries, { type: "action", name, args, stream }];
122
+
this.notify();
123
+
}
124
+
125
+
deleteEntry(entryIndex: number): boolean {
126
+
if (!this.canDeleteEntry(entryIndex)) return false;
127
+
this.entries = this.entries.filter((_, i) => i !== entryIndex);
128
+
this.notify();
129
+
return true;
130
+
}
131
+
132
+
stepForward(): void {
133
+
const total = this.getTotalChunks();
134
+
if (this.cursor >= total) return;
135
+
136
+
const pos = this.getPosition(this.cursor);
137
+
if (!pos) return;
138
+
139
+
const entry = this.entries[pos.entryIndex];
140
+
if (!entry) return;
141
+
142
+
this.cursor++;
143
+
entry.stream.release(pos.localChunk + 1);
144
+
145
+
this.notify();
146
+
}
147
+
148
+
skipToEntryEnd(): void {
149
+
const pos = this.getPosition(this.cursor);
150
+
if (!pos) return;
151
+
152
+
const entry = this.entries[pos.entryIndex];
153
+
if (!entry) return;
154
+
155
+
const entryEnd = this.getEntryStart(pos.entryIndex) + this.getChunkCount(entry);
156
+
while (this.cursor < entryEnd) {
157
+
this.stepForward();
158
+
}
159
+
}
160
+
161
+
clear(): void {
162
+
this.entries = [];
163
+
this.cursor = 0;
164
+
this.notify();
165
+
}
166
+
}
+7
-1
src/client/samples.js
src/client/samples.ts
+7
-1
src/client/samples.js
src/client/samples.ts
-175
src/client/server-worker.js
-175
src/client/server-worker.js
···
1
-
import workerUrl from "../server/worker.js?rolldown-worker";
2
-
3
-
const randomUUID =
4
-
crypto.randomUUID?.bind(crypto) ??
5
-
function () {
6
-
const bytes = crypto.getRandomValues(new Uint8Array(16));
7
-
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
8
-
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1
9
-
const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0"));
10
-
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
11
-
};
12
-
13
-
function serializeForTransfer(encoded) {
14
-
if (encoded instanceof FormData) {
15
-
return { type: "formdata", data: new URLSearchParams(encoded).toString() };
16
-
}
17
-
return { type: "string", data: encoded };
18
-
}
19
-
20
-
export class ServerWorker {
21
-
constructor() {
22
-
this.worker = new Worker(workerUrl);
23
-
this.pending = new Map();
24
-
this.streams = new Map();
25
-
this.readyPromise = new Promise((resolve) => {
26
-
this.readyResolve = resolve;
27
-
});
28
-
29
-
this.worker.onmessage = this.handleMessage.bind(this);
30
-
this.worker.onerror = this.handleError.bind(this);
31
-
}
32
-
33
-
handleMessage(event) {
34
-
const { type, requestId, error, chunk } = event.data;
35
-
36
-
if (type === "ready") {
37
-
this.readyResolve();
38
-
return;
39
-
}
40
-
41
-
if (type === "stream-start") {
42
-
const pending = this.pending.get(requestId);
43
-
if (!pending) return;
44
-
this.pending.delete(requestId);
45
-
46
-
let controller;
47
-
const stream = new ReadableStream({
48
-
start: (c) => {
49
-
controller = c;
50
-
},
51
-
});
52
-
this.streams.set(requestId, controller);
53
-
pending.resolve(stream);
54
-
return;
55
-
}
56
-
57
-
if (type === "stream-chunk") {
58
-
const controller = this.streams.get(requestId);
59
-
if (controller) controller.enqueue(chunk);
60
-
return;
61
-
}
62
-
63
-
if (type === "stream-end") {
64
-
const controller = this.streams.get(requestId);
65
-
if (controller) {
66
-
controller.close();
67
-
this.streams.delete(requestId);
68
-
}
69
-
return;
70
-
}
71
-
72
-
if (type === "stream-error") {
73
-
const controller = this.streams.get(requestId);
74
-
if (controller) {
75
-
controller.error(new Error(error.message));
76
-
this.streams.delete(requestId);
77
-
}
78
-
return;
79
-
}
80
-
81
-
const pending = this.pending.get(requestId);
82
-
if (!pending) {
83
-
console.warn(`No pending request for ${requestId}`);
84
-
return;
85
-
}
86
-
87
-
this.pending.delete(requestId);
88
-
89
-
if (type === "error") {
90
-
const err = new Error(error.message);
91
-
err.stack = error.stack;
92
-
pending.reject(err);
93
-
} else if (type === "deployed") {
94
-
pending.resolve();
95
-
}
96
-
}
97
-
98
-
handleError(event) {
99
-
const errorMsg = event.message || event.error?.message || "Unknown worker error";
100
-
console.error(`Worker error: ${errorMsg}`);
101
-
102
-
for (const [, pending] of this.pending) {
103
-
pending.reject(new Error(`Worker error: ${errorMsg}`));
104
-
}
105
-
this.pending.clear();
106
-
}
107
-
108
-
async deploy({ compiledCode, manifest, actionNames }) {
109
-
await this.readyPromise;
110
-
const requestId = randomUUID();
111
-
112
-
return new Promise((resolve, reject) => {
113
-
this.pending.set(requestId, { resolve, reject });
114
-
this.worker.postMessage({
115
-
type: "deploy",
116
-
requestId,
117
-
compiledCode,
118
-
manifest,
119
-
actionNames,
120
-
});
121
-
});
122
-
}
123
-
124
-
async render() {
125
-
await this.readyPromise;
126
-
const requestId = randomUUID();
127
-
128
-
return new Promise((resolve, reject) => {
129
-
this.pending.set(requestId, { resolve, reject });
130
-
this.worker.postMessage({ type: "render", requestId });
131
-
});
132
-
}
133
-
134
-
async callAction(actionId, encodedArgs) {
135
-
await this.readyPromise;
136
-
const requestId = randomUUID();
137
-
138
-
return new Promise((resolve, reject) => {
139
-
this.pending.set(requestId, { resolve, reject });
140
-
this.worker.postMessage({
141
-
type: "action",
142
-
requestId,
143
-
actionId,
144
-
encodedArgs: serializeForTransfer(encodedArgs),
145
-
});
146
-
});
147
-
}
148
-
149
-
async callActionRaw(actionId, rawPayload) {
150
-
await this.readyPromise;
151
-
const requestId = randomUUID();
152
-
153
-
return new Promise((resolve, reject) => {
154
-
this.pending.set(requestId, { resolve, reject });
155
-
this.worker.postMessage({
156
-
type: "action",
157
-
requestId,
158
-
actionId,
159
-
encodedArgs: { type: "formdata", data: rawPayload },
160
-
});
161
-
});
162
-
}
163
-
164
-
terminate() {
165
-
this.worker.terminate();
166
-
for (const [, pending] of this.pending) {
167
-
pending.reject(new Error("Worker terminated"));
168
-
}
169
-
this.pending.clear();
170
-
for (const [, controller] of this.streams) {
171
-
controller.error(new Error("Worker terminated"));
172
-
}
173
-
this.streams.clear();
174
-
}
175
-
}
+272
src/client/server-worker.ts
+272
src/client/server-worker.ts
···
1
+
import workerUrl from "../server/worker.ts?rolldown-worker";
2
+
import type { ClientManifest } from "../shared/compiler.ts";
3
+
4
+
const randomUUID: () => string =
5
+
crypto.randomUUID?.bind(crypto) ??
6
+
function (): string {
7
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
8
+
bytes[6] = (bytes[6]! & 0x0f) | 0x40; // version 4
9
+
bytes[8] = (bytes[8]! & 0x3f) | 0x80; // variant 1
10
+
const hex = [...bytes].map((b) => b.toString(16).padStart(2, "0"));
11
+
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
12
+
};
13
+
14
+
type EncodedArgsFormData = {
15
+
type: "formdata";
16
+
data: string;
17
+
};
18
+
19
+
type EncodedArgsString = {
20
+
type: "string";
21
+
data: string;
22
+
};
23
+
24
+
type EncodedArgsTransfer = EncodedArgsFormData | EncodedArgsString;
25
+
26
+
function serializeForTransfer(encoded: FormData | string): EncodedArgsTransfer {
27
+
if (encoded instanceof FormData) {
28
+
return {
29
+
type: "formdata",
30
+
data: new URLSearchParams(encoded as unknown as Record<string, string>).toString(),
31
+
};
32
+
}
33
+
return { type: "string", data: encoded };
34
+
}
35
+
36
+
type WorkerReadyMessage = {
37
+
type: "ready";
38
+
};
39
+
40
+
type WorkerStreamStartMessage = {
41
+
type: "stream-start";
42
+
requestId: string;
43
+
};
44
+
45
+
type WorkerStreamChunkMessage = {
46
+
type: "stream-chunk";
47
+
requestId: string;
48
+
chunk: Uint8Array;
49
+
};
50
+
51
+
type WorkerStreamEndMessage = {
52
+
type: "stream-end";
53
+
requestId: string;
54
+
};
55
+
56
+
type WorkerStreamErrorMessage = {
57
+
type: "stream-error";
58
+
requestId: string;
59
+
error: { message: string };
60
+
};
61
+
62
+
type WorkerDeployedMessage = {
63
+
type: "deployed";
64
+
requestId: string;
65
+
};
66
+
67
+
type WorkerErrorMessage = {
68
+
type: "error";
69
+
requestId: string;
70
+
error: { message: string; stack?: string };
71
+
};
72
+
73
+
type WorkerMessage =
74
+
| WorkerReadyMessage
75
+
| WorkerStreamStartMessage
76
+
| WorkerStreamChunkMessage
77
+
| WorkerStreamEndMessage
78
+
| WorkerStreamErrorMessage
79
+
| WorkerDeployedMessage
80
+
| WorkerErrorMessage;
81
+
82
+
type PendingRequest = {
83
+
resolve: (value: unknown) => void;
84
+
reject: (error: Error) => void;
85
+
};
86
+
87
+
export class ServerWorker {
88
+
private worker: Worker;
89
+
private pending: Map<string, PendingRequest> = new Map();
90
+
private streams: Map<string, ReadableStreamDefaultController<Uint8Array>> = new Map();
91
+
private readyPromise: Promise<void>;
92
+
private readyResolve!: () => void;
93
+
94
+
constructor() {
95
+
this.worker = new Worker(workerUrl);
96
+
this.readyPromise = new Promise((resolve) => {
97
+
this.readyResolve = resolve;
98
+
});
99
+
100
+
this.worker.onmessage = this.handleMessage.bind(this);
101
+
this.worker.onerror = this.handleError.bind(this);
102
+
}
103
+
104
+
private handleMessage(event: MessageEvent<WorkerMessage>): void {
105
+
const { type } = event.data;
106
+
107
+
if (type === "ready") {
108
+
this.readyResolve();
109
+
return;
110
+
}
111
+
112
+
if (type === "stream-start") {
113
+
const { requestId } = event.data;
114
+
const pending = this.pending.get(requestId);
115
+
if (!pending) return;
116
+
this.pending.delete(requestId);
117
+
118
+
let controller: ReadableStreamDefaultController<Uint8Array>;
119
+
const stream = new ReadableStream<Uint8Array>({
120
+
start: (c) => {
121
+
controller = c;
122
+
},
123
+
});
124
+
this.streams.set(requestId, controller!);
125
+
pending.resolve(stream);
126
+
return;
127
+
}
128
+
129
+
if (type === "stream-chunk") {
130
+
const { requestId, chunk } = event.data;
131
+
const controller = this.streams.get(requestId);
132
+
if (controller) controller.enqueue(chunk);
133
+
return;
134
+
}
135
+
136
+
if (type === "stream-end") {
137
+
const { requestId } = event.data;
138
+
const controller = this.streams.get(requestId);
139
+
if (controller) {
140
+
controller.close();
141
+
this.streams.delete(requestId);
142
+
}
143
+
return;
144
+
}
145
+
146
+
if (type === "stream-error") {
147
+
const { requestId, error } = event.data;
148
+
const controller = this.streams.get(requestId);
149
+
if (controller) {
150
+
controller.error(new Error(error.message));
151
+
this.streams.delete(requestId);
152
+
}
153
+
return;
154
+
}
155
+
156
+
if (type === "deployed") {
157
+
const { requestId } = event.data as WorkerDeployedMessage;
158
+
const pending = this.pending.get(requestId);
159
+
if (!pending) {
160
+
console.warn(`No pending request for ${requestId}`);
161
+
return;
162
+
}
163
+
this.pending.delete(requestId);
164
+
pending.resolve(undefined);
165
+
return;
166
+
}
167
+
168
+
if (type === "error") {
169
+
const { requestId, error } = event.data as WorkerErrorMessage;
170
+
const pending = this.pending.get(requestId);
171
+
if (!pending) {
172
+
console.warn(`No pending request for ${requestId}`);
173
+
return;
174
+
}
175
+
this.pending.delete(requestId);
176
+
const err = new Error(error.message);
177
+
if (error.stack) {
178
+
err.stack = error.stack;
179
+
}
180
+
pending.reject(err);
181
+
}
182
+
}
183
+
184
+
private handleError(event: ErrorEvent): void {
185
+
const errorMsg = event.message || "Unknown worker error";
186
+
console.error(`Worker error: ${errorMsg}`);
187
+
188
+
for (const [, pending] of this.pending) {
189
+
pending.reject(new Error(`Worker error: ${errorMsg}`));
190
+
}
191
+
this.pending.clear();
192
+
}
193
+
194
+
async deploy({
195
+
compiledCode,
196
+
manifest,
197
+
actionNames,
198
+
}: {
199
+
compiledCode: string;
200
+
manifest: ClientManifest;
201
+
actionNames: string[];
202
+
}): Promise<void> {
203
+
await this.readyPromise;
204
+
const requestId = randomUUID();
205
+
206
+
return new Promise((resolve, reject) => {
207
+
this.pending.set(requestId, { resolve: resolve as (value: unknown) => void, reject });
208
+
this.worker.postMessage({
209
+
type: "deploy",
210
+
requestId,
211
+
compiledCode,
212
+
manifest,
213
+
actionNames,
214
+
});
215
+
});
216
+
}
217
+
218
+
async render(): Promise<ReadableStream<Uint8Array>> {
219
+
await this.readyPromise;
220
+
const requestId = randomUUID();
221
+
222
+
return new Promise((resolve, reject) => {
223
+
this.pending.set(requestId, { resolve: resolve as (value: unknown) => void, reject });
224
+
this.worker.postMessage({ type: "render", requestId });
225
+
});
226
+
}
227
+
228
+
async callAction(
229
+
actionId: string,
230
+
encodedArgs: FormData | string,
231
+
): Promise<ReadableStream<Uint8Array>> {
232
+
await this.readyPromise;
233
+
const requestId = randomUUID();
234
+
235
+
return new Promise((resolve, reject) => {
236
+
this.pending.set(requestId, { resolve: resolve as (value: unknown) => void, reject });
237
+
this.worker.postMessage({
238
+
type: "action",
239
+
requestId,
240
+
actionId,
241
+
encodedArgs: serializeForTransfer(encodedArgs),
242
+
});
243
+
});
244
+
}
245
+
246
+
async callActionRaw(actionId: string, rawPayload: string): Promise<ReadableStream<Uint8Array>> {
247
+
await this.readyPromise;
248
+
const requestId = randomUUID();
249
+
250
+
return new Promise((resolve, reject) => {
251
+
this.pending.set(requestId, { resolve: resolve as (value: unknown) => void, reject });
252
+
this.worker.postMessage({
253
+
type: "action",
254
+
requestId,
255
+
actionId,
256
+
encodedArgs: { type: "formdata", data: rawPayload },
257
+
});
258
+
});
259
+
}
260
+
261
+
terminate(): void {
262
+
this.worker.terminate();
263
+
for (const [, pending] of this.pending) {
264
+
pending.reject(new Error("Worker terminated"));
265
+
}
266
+
this.pending.clear();
267
+
for (const [, controller] of this.streams) {
268
+
controller.error(new Error("Worker terminated"));
269
+
}
270
+
this.streams.clear();
271
+
}
272
+
}
+60
-36
src/client/ui/App.jsx
src/client/ui/App.tsx
+60
-36
src/client/ui/App.jsx
src/client/ui/App.tsx
···
1
-
import React, { useState, useRef, useEffect, useMemo, version } from "react";
2
-
import { SAMPLES } from "../samples.js";
1
+
import React, { useState, useRef, useEffect, type ChangeEvent, type MouseEvent } from "react";
2
+
import { version } from "react";
3
+
import { SAMPLES, type Sample } from "../samples.ts";
3
4
import REACT_VERSIONS from "../../../scripts/versions.json";
4
5
5
6
const isDev = process.env.NODE_ENV === "development";
6
7
7
-
function BuildSwitcher() {
8
+
function BuildSwitcher(): React.ReactElement {
8
9
const isDisabled = !import.meta.env.PROD;
9
10
10
-
const handleVersionChange = (e) => {
11
+
const handleVersionChange = (e: ChangeEvent<HTMLSelectElement>): void => {
11
12
const newVersion = e.target.value;
12
13
if (newVersion !== version) {
13
14
const modePath = isDev ? "/dev" : "";
···
15
16
}
16
17
};
17
18
18
-
const handleModeChange = (e) => {
19
+
const handleModeChange = (e: ChangeEvent<HTMLSelectElement>): void => {
19
20
const newIsDev = e.target.value === "dev";
20
21
if (newIsDev !== isDev) {
21
22
const modePath = newIsDev ? "/dev" : "";
···
27
28
<div className="build-switcher">
28
29
<label>React</label>
29
30
<select value={version} onChange={handleVersionChange} disabled={isDisabled}>
30
-
{REACT_VERSIONS.map((v) => (
31
+
{(REACT_VERSIONS as string[]).map((v) => (
31
32
<option key={v} value={v}>
32
33
{v}
33
34
</option>
···
46
47
);
47
48
}
48
49
49
-
function getInitialCode() {
50
+
type InitialCode = {
51
+
server: string;
52
+
client: string;
53
+
sampleKey: string | null;
54
+
};
55
+
56
+
function getInitialCode(): InitialCode {
50
57
const params = new URLSearchParams(window.location.search);
51
58
const sampleKey = params.get("s");
52
59
const encodedCode = params.get("c");
53
60
54
61
if (encodedCode) {
55
62
try {
56
-
const decoded = JSON.parse(decodeURIComponent(escape(atob(encodedCode))));
63
+
const decoded = JSON.parse(decodeURIComponent(escape(atob(encodedCode)))) as {
64
+
server: string;
65
+
client: string;
66
+
};
57
67
return {
58
68
server: decoded.server,
59
69
client: decoded.client,
···
65
75
}
66
76
67
77
if (sampleKey && SAMPLES[sampleKey]) {
78
+
const sample = SAMPLES[sampleKey] as Sample;
68
79
return {
69
-
server: SAMPLES[sampleKey].server,
70
-
client: SAMPLES[sampleKey].client,
80
+
server: sample.server,
81
+
client: sample.client,
71
82
sampleKey,
72
83
};
73
84
}
74
85
86
+
const defaultSample = SAMPLES.pagination as Sample;
75
87
return {
76
-
server: SAMPLES.pagination.server,
77
-
client: SAMPLES.pagination.client,
88
+
server: defaultSample.server,
89
+
client: defaultSample.client,
78
90
sampleKey: "pagination",
79
91
};
80
92
}
81
93
82
-
function saveToUrl(serverCode, clientCode) {
94
+
function saveToUrl(serverCode: string, clientCode: string): void {
83
95
const json = JSON.stringify({ server: serverCode, client: clientCode });
84
96
// btoa(unescape(encodeURIComponent(...))) is the standard way to base64 encode UTF-8
85
97
// Don't wrap in encodeURIComponent - searchParams.set() handles that
···
90
102
window.history.pushState({}, "", url);
91
103
}
92
104
93
-
function EmbedModal({ code, onClose }) {
94
-
const textareaRef = useRef(null);
105
+
type CodeState = {
106
+
server: string;
107
+
client: string;
108
+
};
109
+
110
+
type EmbedModalProps = {
111
+
code: CodeState;
112
+
onClose: () => void;
113
+
};
114
+
115
+
function EmbedModal({ code, onClose }: EmbedModalProps): React.ReactElement {
116
+
const textareaRef = useRef<HTMLTextAreaElement>(null);
95
117
const [copied, setCopied] = useState(false);
96
118
97
119
const [embedCode] = useState(() => {
···
110
132
\`
111
133
});
112
134
</script>`;
113
-
}, [code]);
135
+
});
114
136
115
-
const handleCopy = () => {
137
+
const handleCopy = (): void => {
116
138
navigator.clipboard.writeText(embedCode);
117
139
setCopied(true);
118
140
setTimeout(() => setCopied(false), 2000);
···
120
142
121
143
return (
122
144
<div className="modal-overlay" onClick={onClose}>
123
-
<div className="modal" onClick={(e) => e.stopPropagation()}>
145
+
<div className="modal" onClick={(e: MouseEvent) => e.stopPropagation()}>
124
146
<div className="modal-header">
125
147
<h2>Embed this example</h2>
126
148
<button className="modal-close" onClick={onClose}>
···
133
155
ref={textareaRef}
134
156
readOnly
135
157
value={embedCode}
136
-
onClick={(e) => e.target.select()}
158
+
onClick={(e) => (e.target as HTMLTextAreaElement).select()}
137
159
/>
138
160
</div>
139
161
<div className="modal-footer">
···
146
168
);
147
169
}
148
170
149
-
export function App() {
171
+
export function App(): React.ReactElement {
150
172
const [initialCode] = useState(getInitialCode);
151
-
const [currentSample, setCurrentSample] = useState(initialCode.sampleKey);
152
-
const [workspaceCode, setWorkspaceCode] = useState({
173
+
const [currentSample, setCurrentSample] = useState<string | null>(initialCode.sampleKey);
174
+
const [workspaceCode, setWorkspaceCode] = useState<CodeState>({
153
175
server: initialCode.server,
154
176
client: initialCode.client,
155
177
});
156
-
const [liveCode, setLiveCode] = useState(workspaceCode);
178
+
const [liveCode, setLiveCode] = useState<CodeState>(workspaceCode);
157
179
const [showEmbedModal, setShowEmbedModal] = useState(false);
158
-
const iframeRef = useRef(null);
180
+
const iframeRef = useRef<HTMLIFrameElement>(null);
159
181
160
182
useEffect(() => {
161
-
const handleMessage = (event) => {
162
-
if (event.data?.type === "rsc-embed:ready") {
183
+
const handleMessage = (event: MessageEvent): void => {
184
+
const data = event.data as { type?: string; code?: CodeState };
185
+
if (data?.type === "rsc-embed:ready") {
163
186
iframeRef.current?.contentWindow?.postMessage(
164
187
{
165
188
type: "rsc-embed:init",
···
169
192
"*",
170
193
);
171
194
}
172
-
if (event.data?.type === "rsc-embed:code-changed") {
173
-
setLiveCode(event.data.code);
195
+
if (data?.type === "rsc-embed:code-changed" && data.code) {
196
+
setLiveCode(data.code);
174
197
}
175
198
};
176
199
···
193
216
}
194
217
}, [workspaceCode]);
195
218
196
-
const handleSave = () => {
219
+
const handleSave = (): void => {
197
220
saveToUrl(liveCode.server, liveCode.client);
198
221
setCurrentSample(null);
199
222
};
200
223
201
224
const isDirty = currentSample
202
-
? liveCode.server !== SAMPLES[currentSample].server ||
203
-
liveCode.client !== SAMPLES[currentSample].client
225
+
? liveCode.server !== (SAMPLES[currentSample] as Sample).server ||
226
+
liveCode.client !== (SAMPLES[currentSample] as Sample).client
204
227
: liveCode.server !== initialCode.server || liveCode.client !== initialCode.client;
205
228
206
-
const handleSampleChange = (e) => {
229
+
const handleSampleChange = (e: ChangeEvent<HTMLSelectElement>): void => {
207
230
const key = e.target.value;
208
231
if (key && SAMPLES[key]) {
209
-
const newCode = {
210
-
server: SAMPLES[key].server,
211
-
client: SAMPLES[key].client,
232
+
const sample = SAMPLES[key] as Sample;
233
+
const newCode: CodeState = {
234
+
server: sample.server,
235
+
client: sample.client,
212
236
};
213
237
setWorkspaceCode(newCode);
214
238
setCurrentSample(key);
···
225
249
<h1>RSC Explorer</h1>
226
250
<div className="example-select-wrapper">
227
251
<label>Example</label>
228
-
<select value={currentSample || ""} onChange={handleSampleChange}>
252
+
<select value={currentSample ?? ""} onChange={handleSampleChange}>
229
253
{!currentSample && <option value="">Custom</option>}
230
254
{Object.entries(SAMPLES).map(([key, sample]) => (
231
255
<option key={key} value={key}>
+10
-5
src/client/ui/CodeEditor.jsx
src/client/ui/CodeEditor.tsx
+10
-5
src/client/ui/CodeEditor.jsx
src/client/ui/CodeEditor.tsx
···
3
3
import { javascript } from "@codemirror/lang-javascript";
4
4
import { syntaxHighlighting, HighlightStyle } from "@codemirror/language";
5
5
import { tags } from "@lezer/highlight";
6
-
import { history, historyKeymap } from "@codemirror/commands";
6
+
import { history, historyKeymap, defaultKeymap } from "@codemirror/commands";
7
7
import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
8
-
import { defaultKeymap } from "@codemirror/commands";
9
8
10
9
const highlightStyle = HighlightStyle.define([
11
10
{ tag: tags.keyword, color: "#c678dd" },
···
40
39
{ dark: true },
41
40
);
42
41
43
-
export function CodeEditor({ defaultValue, onChange, label }) {
42
+
type CodeEditorProps = {
43
+
defaultValue: string;
44
+
onChange: (code: string) => void;
45
+
label: string;
46
+
};
47
+
48
+
export function CodeEditor({ defaultValue, onChange, label }: CodeEditorProps): React.ReactElement {
44
49
const [initialDefaultValue] = useState(defaultValue);
45
-
const containerRef = useRef(null);
50
+
const containerRef = useRef<HTMLDivElement>(null);
46
51
47
-
const onEditorChange = useEffectEvent((doc) => {
52
+
const onEditorChange = useEffectEvent((doc: string) => {
48
53
onChange(doc);
49
54
});
50
55
+53
-19
src/client/ui/FlightLog.jsx
src/client/ui/FlightLog.tsx
+53
-19
src/client/ui/FlightLog.jsx
src/client/ui/FlightLog.tsx
···
1
1
import React, { useState, useRef, useEffect } from "react";
2
-
import { FlightTreeView } from "./TreeView.jsx";
2
+
import { FlightTreeView } from "./TreeView.tsx";
3
+
import type { Timeline, TimelineEntry, Thenable } from "../runtime/index.ts";
3
4
4
-
function escapeHtml(str) {
5
+
function escapeHtml(str: string): string {
5
6
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
6
7
}
7
8
8
-
function RenderLogView({ lines, chunkStart, cursor, flightPromise }) {
9
-
const activeRef = useRef(null);
9
+
type RenderLogViewProps = {
10
+
lines: string[];
11
+
chunkStart: number;
12
+
cursor: number;
13
+
flightPromise: Thenable<unknown> | undefined;
14
+
};
15
+
16
+
function RenderLogView({
17
+
lines,
18
+
chunkStart,
19
+
cursor,
20
+
flightPromise,
21
+
}: RenderLogViewProps): React.ReactElement | null {
22
+
const activeRef = useRef<HTMLSpanElement>(null);
10
23
const nextLineIndex =
11
24
cursor >= chunkStart && cursor < chunkStart + lines.length ? cursor - chunkStart : -1;
12
25
···
18
31
19
32
if (lines.length === 0) return null;
20
33
21
-
const getLineClass = (i) => {
34
+
const getLineClass = (i: number): string => {
22
35
const globalChunk = chunkStart + i;
23
36
if (globalChunk < cursor) return "line-done";
24
37
if (globalChunk === cursor) return "line-next";
···
44
57
</pre>
45
58
</div>
46
59
<div className="log-entry-tree">
47
-
{showTree && <FlightTreeView flightPromise={flightPromise} />}
60
+
{showTree && <FlightTreeView flightPromise={flightPromise ?? null} />}
48
61
</div>
49
62
</div>
50
63
</div>
51
64
);
52
65
}
53
66
67
+
type FlightLogEntryProps = {
68
+
entry: TimelineEntry;
69
+
entryIndex: number;
70
+
chunkStart: number;
71
+
cursor: number;
72
+
canDelete: boolean;
73
+
onDelete: (index: number) => void;
74
+
getChunkCount: (entry: TimelineEntry) => number;
75
+
};
76
+
54
77
function FlightLogEntry({
55
78
entry,
56
79
entryIndex,
···
59
82
canDelete,
60
83
onDelete,
61
84
getChunkCount,
62
-
}) {
85
+
}: FlightLogEntryProps): React.ReactElement | null {
63
86
const chunkCount = getChunkCount(entry);
64
87
const entryEnd = chunkStart + chunkCount;
65
88
const isEntryActive = cursor >= chunkStart && cursor < entryEnd;
···
68
91
const entryClass = isEntryActive ? "active" : isEntryDone ? "done-entry" : "pending-entry";
69
92
70
93
if (entry.type === "render") {
71
-
const lines = entry.stream?.rows || [];
94
+
const lines = entry.stream.rows;
72
95
return (
73
96
<div className={`log-entry ${entryClass}`}>
74
97
<div className="log-entry-header">
···
89
112
lines={lines}
90
113
chunkStart={chunkStart}
91
114
cursor={cursor}
92
-
flightPromise={entry.stream?.flightPromise}
115
+
flightPromise={entry.stream.flightPromise}
93
116
/>
94
117
</div>
95
118
);
96
119
}
97
120
98
121
if (entry.type === "action") {
99
-
const responseLines = entry.stream?.rows || [];
122
+
const responseLines = entry.stream.rows;
100
123
101
124
return (
102
125
<div className={`log-entry ${entryClass}`}>
···
123
146
lines={responseLines}
124
147
chunkStart={chunkStart}
125
148
cursor={cursor}
126
-
flightPromise={entry.stream?.flightPromise}
149
+
flightPromise={entry.stream.flightPromise}
127
150
/>
128
151
</div>
129
152
);
···
132
155
return null;
133
156
}
134
157
158
+
type FlightLogProps = {
159
+
timeline: Timeline;
160
+
entries: TimelineEntry[];
161
+
cursor: number;
162
+
error: string | null;
163
+
availableActions: string[];
164
+
onAddRawAction: (actionName: string, rawPayload: string) => void;
165
+
onDeleteEntry: (index: number) => void;
166
+
};
167
+
135
168
export function FlightLog({
136
169
timeline,
137
170
entries,
···
140
173
availableActions,
141
174
onAddRawAction,
142
175
onDeleteEntry,
143
-
}) {
144
-
const logRef = useRef(null);
176
+
}: FlightLogProps): React.ReactElement {
177
+
const logRef = useRef<HTMLDivElement>(null);
145
178
const [showRawInput, setShowRawInput] = useState(false);
146
179
const [selectedAction, setSelectedAction] = useState("");
147
180
const [rawPayload, setRawPayload] = useState("");
148
181
149
-
const handleAddRaw = () => {
182
+
const handleAddRaw = (): void => {
150
183
if (rawPayload.trim()) {
151
184
onAddRawAction(selectedAction, rawPayload);
152
-
setSelectedAction(availableActions[0] || "");
185
+
setSelectedAction(availableActions[0] ?? "");
153
186
setRawPayload("");
154
187
setShowRawInput(false);
155
188
}
156
189
};
157
190
158
-
const handleShowRawInput = () => {
159
-
setSelectedAction(availableActions[0] || "");
191
+
const handleShowRawInput = (): void => {
192
+
setSelectedAction(availableActions[0] ?? "");
160
193
setShowRawInput(true);
161
194
};
162
195
···
172
205
);
173
206
}
174
207
175
-
const getChunkCount = (entry) => timeline.getChunkCount(entry);
208
+
const getChunkCount = (entry: TimelineEntry): number => timeline.getChunkCount(entry);
176
209
177
-
const entryElements = [];
210
+
const entryElements: React.ReactElement[] = [];
178
211
let chunkOffset = 0;
179
212
for (let i = 0; i < entries.length; i++) {
180
213
const entry = entries[i];
214
+
if (!entry) continue;
181
215
const chunkStart = chunkOffset;
182
216
chunkOffset += getChunkCount(entry);
183
217
entryElements.push(
+45
-13
src/client/ui/LivePreview.jsx
src/client/ui/LivePreview.tsx
+45
-13
src/client/ui/LivePreview.jsx
src/client/ui/LivePreview.tsx
···
1
-
import React, { Suspense, Component, useState, useEffect, useSyncExternalStore } from "react";
1
+
import React, {
2
+
Suspense,
3
+
Component,
4
+
useState,
5
+
useEffect,
6
+
useSyncExternalStore,
7
+
type ReactNode,
8
+
} from "react";
9
+
import type { Timeline, Thenable } from "../runtime/index.ts";
10
+
11
+
type PreviewErrorBoundaryProps = {
12
+
children: ReactNode;
13
+
};
14
+
15
+
type PreviewErrorBoundaryState = {
16
+
error: Error | null;
17
+
};
2
18
3
-
class PreviewErrorBoundary extends Component {
4
-
constructor(props) {
19
+
class PreviewErrorBoundary extends Component<PreviewErrorBoundaryProps, PreviewErrorBoundaryState> {
20
+
constructor(props: PreviewErrorBoundaryProps) {
5
21
super(props);
6
22
this.state = { error: null };
7
23
}
8
24
9
-
static getDerivedStateFromError(error) {
25
+
static getDerivedStateFromError(error: Error): PreviewErrorBoundaryState {
10
26
return { error };
11
27
}
12
28
13
-
render() {
29
+
render(): ReactNode {
14
30
if (this.state.error) {
15
31
return (
16
32
<span className="empty error">
···
22
38
}
23
39
}
24
40
25
-
function StreamingContent({ streamPromise }) {
26
-
return React.use(streamPromise);
41
+
type StreamingContentProps = {
42
+
streamPromise: Thenable<unknown>;
43
+
};
44
+
45
+
function StreamingContent({ streamPromise }: StreamingContentProps): ReactNode {
46
+
return React.use(streamPromise) as ReactNode;
27
47
}
28
48
49
+
type LivePreviewProps = {
50
+
timeline: Timeline;
51
+
clientModuleReady: boolean;
52
+
totalChunks: number;
53
+
cursor: number;
54
+
isAtStart: boolean;
55
+
isAtEnd: boolean;
56
+
onStep: () => void;
57
+
onSkip: () => void;
58
+
onReset: () => void;
59
+
};
60
+
29
61
export function LivePreview({
30
62
timeline,
31
63
clientModuleReady,
···
36
68
onStep,
37
69
onSkip,
38
70
onReset,
39
-
}) {
71
+
}: LivePreviewProps): React.ReactElement {
40
72
const snapshot = useSyncExternalStore(timeline.subscribe, timeline.getSnapshot);
41
73
const { entries } = snapshot;
42
74
const renderEntry = entries[0];
43
-
const flightPromise = renderEntry?.stream?.flightPromise;
75
+
const flightPromise = renderEntry?.stream.flightPromise;
44
76
45
77
const [isPlaying, setIsPlaying] = useState(false);
46
78
···
61
93
62
94
const showPlaceholder = !clientModuleReady || cursor === 0;
63
95
64
-
const handlePlayPause = () => setIsPlaying(!isPlaying);
65
-
const handleStep = () => {
96
+
const handlePlayPause = (): void => setIsPlaying(!isPlaying);
97
+
const handleStep = (): void => {
66
98
setIsPlaying(false);
67
99
onStep();
68
100
};
69
-
const handleSkip = () => {
101
+
const handleSkip = (): void => {
70
102
setIsPlaying(false);
71
103
onSkip();
72
104
};
73
-
const handleReset = () => {
105
+
const handleReset = (): void => {
74
106
setIsPlaying(false);
75
107
onReset();
76
108
};
+133
-39
src/client/ui/TreeView.jsx
src/client/ui/TreeView.tsx
+133
-39
src/client/ui/TreeView.jsx
src/client/ui/TreeView.tsx
···
1
-
import React, { Suspense, Component } from "react";
1
+
import React, { Suspense, Component, type ReactNode } from "react";
2
+
import type { Thenable } from "../runtime/index.ts";
2
3
3
-
function isReactElement(value) {
4
+
// Internal React element type with $$typeof
5
+
type ReactElementInternal = {
6
+
$$typeof: symbol;
7
+
type: unknown;
8
+
key: string | null;
9
+
ref: unknown;
10
+
props: Record<string, unknown>;
11
+
};
12
+
13
+
// Lazy element type
14
+
type ReactLazy = {
15
+
$$typeof: symbol;
16
+
_payload: unknown;
17
+
_init: (payload: unknown) => unknown;
18
+
};
19
+
20
+
function isReactElement(value: unknown): value is ReactElementInternal {
4
21
if (!value || typeof value !== "object") return false;
5
-
const typeofSymbol = value.$$typeof;
22
+
const typeofSymbol = (value as { $$typeof?: symbol }).$$typeof;
6
23
return typeofSymbol === Symbol.for("react.transitional.element");
7
24
}
8
25
9
-
function escapeHtml(str) {
26
+
function escapeHtml(str: string): string {
10
27
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
11
28
}
12
29
13
-
function PendingFallback() {
30
+
function PendingFallback(): React.ReactElement {
14
31
return <span className="tree-pending">Pending</span>;
15
32
}
16
33
17
-
function ErrorFallback({ error }) {
18
-
const message = error?.message || String(error);
34
+
type ErrorFallbackProps = {
35
+
error: unknown;
36
+
};
37
+
38
+
function ErrorFallback({ error }: ErrorFallbackProps): React.ReactElement {
39
+
const message = error instanceof Error ? error.message : String(error);
19
40
return <span className="tree-error">Error: {message}</span>;
20
41
}
21
42
22
-
class ErrorBoundary extends Component {
23
-
constructor(props) {
43
+
type ErrorBoundaryProps = {
44
+
children: ReactNode;
45
+
};
46
+
47
+
type ErrorBoundaryState = {
48
+
error: Error | null;
49
+
};
50
+
51
+
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
52
+
constructor(props: ErrorBoundaryProps) {
24
53
super(props);
25
54
this.state = { error: null };
26
55
}
27
56
28
-
static getDerivedStateFromError(error) {
57
+
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
29
58
return { error };
30
59
}
31
60
32
-
render() {
61
+
render(): ReactNode {
33
62
if (this.state.error) {
34
63
return <ErrorFallback error={this.state.error} />;
35
64
}
···
37
66
}
38
67
}
39
68
40
-
function Await({ promise, children }) {
69
+
type AwaitProps<T> = {
70
+
promise: Thenable<T>;
71
+
children: (value: T) => ReactNode;
72
+
};
73
+
74
+
function Await<T>({ promise, children }: AwaitProps<T>): ReactNode {
41
75
const value = React.use(promise);
42
76
return children(value);
43
77
}
44
78
45
-
function LazyValue({ value, indent, ancestors = [] }) {
79
+
type LazyValueProps = {
80
+
value: ReactLazy;
81
+
indent: number;
82
+
ancestors?: unknown[];
83
+
};
84
+
85
+
function LazyValue({ value, indent, ancestors = [] }: LazyValueProps): React.ReactElement {
46
86
const resolved = value._init(value._payload);
47
87
return <JSXValue value={resolved} indent={indent} ancestors={ancestors} />;
48
88
}
49
89
50
-
function LazyType({ element, indent, ancestors = [] }) {
90
+
type LazyTypeProps = {
91
+
element: ReactElementInternal & { type: ReactLazy };
92
+
indent: number;
93
+
ancestors?: unknown[];
94
+
};
95
+
96
+
function LazyType({ element, indent, ancestors = [] }: LazyTypeProps): React.ReactElement {
51
97
const resolvedType = element.type._init(element.type._payload);
52
98
const newElement = { ...element, type: resolvedType };
53
-
return <JSXElement element={newElement} indent={indent} ancestors={ancestors} />;
99
+
return (
100
+
<JSXElement
101
+
element={newElement as ReactElementInternal}
102
+
indent={indent}
103
+
ancestors={ancestors}
104
+
/>
105
+
);
54
106
}
55
107
108
+
type JSXValueProps = {
109
+
value: unknown;
110
+
indent?: number;
111
+
ancestors?: unknown[];
112
+
};
113
+
56
114
// `ancestors` tracks the current path for cycle detection
57
-
function JSXValue({ value, indent = 0, ancestors = [] }) {
115
+
function JSXValue({ value, indent = 0, ancestors = [] }: JSXValueProps): React.ReactElement {
58
116
if (value === null) return <span className="tree-null">null</span>;
59
117
if (value === undefined) return <span className="tree-undefined">undefined</span>;
60
118
···
74
132
return <span className="tree-symbol">{value.toString()}</span>;
75
133
}
76
134
if (typeof value === "function") {
77
-
return <span className="tree-function">[Function: {value.name || "anonymous"}]</span>;
135
+
return (
136
+
<span className="tree-function">
137
+
[Function: {(value as { name?: string }).name || "anonymous"}]
138
+
</span>
139
+
);
78
140
}
79
141
80
142
if (typeof value === "object" && value !== null) {
···
170
232
171
233
if (ArrayBuffer.isView(value)) {
172
234
const name = value.constructor.name;
173
-
const preview = Array.from(value.slice(0, 5)).join(", ");
174
-
const suffix = value.length > 5 ? ", ..." : "";
235
+
const arr = value as Uint8Array;
236
+
const preview = Array.from(arr.slice(0, 5)).join(", ");
237
+
const suffix = arr.length > 5 ? ", ..." : "";
175
238
return (
176
239
<span className="tree-collection">
177
-
{name}({value.length}) [{preview}
240
+
{name}({arr.length}) [{preview}
178
241
{suffix}]
179
242
</span>
180
243
);
···
187
250
if (value.length === 0) return <>[]</>;
188
251
const hasElements = value.some((v, i) => i in value && isReactElement(v));
189
252
190
-
const renderItem = (i) => {
253
+
const renderItem = (i: number): React.ReactElement => {
191
254
if (!(i in value)) {
192
255
return <span className="tree-empty">empty</span>;
193
256
}
···
231
294
}
232
295
233
296
if (typeof value === "object") {
234
-
if (typeof value.next === "function" && typeof value[Symbol.iterator] === "function") {
297
+
const obj = value as Record<string | symbol, unknown>;
298
+
299
+
if (typeof obj.next === "function" && typeof obj[Symbol.iterator] === "function") {
235
300
return <span className="tree-iterator">Iterator {"{}"}</span>;
236
301
}
237
302
238
-
if (typeof value[Symbol.asyncIterator] === "function") {
303
+
if (typeof obj[Symbol.asyncIterator] === "function") {
239
304
return <span className="tree-iterator">AsyncIterator {"{}"}</span>;
240
305
}
241
306
···
243
308
return <span className="tree-stream">ReadableStream {"{}"}</span>;
244
309
}
245
310
246
-
if (typeof value.then === "function") {
311
+
if (typeof obj.then === "function") {
247
312
return (
248
313
<ErrorBoundary>
249
314
<Suspense fallback={<PendingFallback />}>
250
-
<Await promise={value}>
315
+
<Await promise={value as Thenable<unknown>}>
251
316
{(resolved) => (
252
317
<JSXValue value={resolved} indent={indent} ancestors={nextAncestors} />
253
318
)}
···
257
322
);
258
323
}
259
324
260
-
if (value.$$typeof === Symbol.for("react.lazy")) {
325
+
if (obj.$$typeof === Symbol.for("react.lazy")) {
261
326
return (
262
327
<ErrorBoundary>
263
328
<Suspense fallback={<PendingFallback />}>
264
-
<LazyValue value={value} indent={indent} ancestors={nextAncestors} />
329
+
<LazyValue value={value as ReactLazy} indent={indent} ancestors={nextAncestors} />
265
330
</Suspense>
266
331
</ErrorBoundary>
267
332
);
268
333
}
269
334
270
-
const entries = Object.entries(value);
335
+
const entries = Object.entries(obj);
271
336
if (entries.length === 0) return <>{"{}"}</>;
272
337
if (entries.length <= 2 && entries.every(([, v]) => typeof v !== "object" || v === null)) {
273
338
return (
···
307
372
return <span className="tree-unknown">{String(value)}</span>;
308
373
}
309
374
310
-
function JSXElement({ element, indent, ancestors = [] }) {
375
+
type JSXElementProps = {
376
+
element: ReactElementInternal;
377
+
indent: number;
378
+
ancestors?: unknown[];
379
+
};
380
+
381
+
function JSXElement({ element, indent, ancestors = [] }: JSXElementProps): React.ReactElement {
311
382
const { type, props, key } = element;
312
383
const pad = " ".repeat(indent);
313
384
const padInner = " ".repeat(indent + 1);
314
385
315
-
let tagName,
316
-
tagClass = "tree-tag";
386
+
let tagName: string;
387
+
let tagClass = "tree-tag";
317
388
if (typeof type === "string") {
318
389
tagName = type;
319
390
} else if (typeof type === "function") {
320
-
tagName = type.displayName || type.name || "Component";
391
+
const funcType = type as { displayName?: string; name?: string };
392
+
tagName = funcType.displayName || funcType.name || "Component";
321
393
tagClass = "tree-client-tag";
322
394
} else if (typeof type === "symbol") {
323
395
switch (type) {
···
346
418
tagName = "Unknown";
347
419
}
348
420
tagClass = "tree-react-tag";
349
-
} else if (type && typeof type === "object" && type.$$typeof) {
350
-
if (type.$$typeof === Symbol.for("react.lazy")) {
421
+
} else if (type && typeof type === "object" && (type as { $$typeof?: symbol }).$$typeof) {
422
+
const lazyType = type as ReactLazy;
423
+
if (lazyType.$$typeof === Symbol.for("react.lazy")) {
351
424
return (
352
425
<ErrorBoundary>
353
426
<Suspense fallback={<PendingFallback />}>
354
-
<LazyType element={element} indent={indent} ancestors={ancestors} />
427
+
<LazyType
428
+
element={element as ReactElementInternal & { type: ReactLazy }}
429
+
indent={indent}
430
+
ancestors={ancestors}
431
+
/>
355
432
</Suspense>
356
433
</ErrorBoundary>
357
434
);
···
362
439
tagName = "Unknown";
363
440
}
364
441
365
-
const { children, ...otherProps } = props || {};
442
+
const { children, ...otherProps } = props;
366
443
const propEntries = Object.entries(otherProps).filter(
367
444
([k]) => !["key", "ref", "__self", "__source"].includes(k),
368
445
);
···
419
496
);
420
497
}
421
498
422
-
function JSXProp({ name, value, indent, ancestors = [] }) {
499
+
type JSXPropProps = {
500
+
name: string;
501
+
value: unknown;
502
+
indent: number;
503
+
ancestors?: unknown[];
504
+
};
505
+
506
+
function JSXProp({ name, value, indent, ancestors = [] }: JSXPropProps): React.ReactElement {
423
507
if (typeof value === "string") {
424
508
return (
425
509
<>
···
476
560
);
477
561
}
478
562
479
-
function JSXChildren({ value, indent, ancestors = [] }) {
563
+
type JSXChildrenProps = {
564
+
value: unknown;
565
+
indent: number;
566
+
ancestors?: unknown[];
567
+
};
568
+
569
+
function JSXChildren({ value, indent, ancestors = [] }: JSXChildrenProps): React.ReactElement {
480
570
if (typeof value === "string") return <>{escapeHtml(value)}</>;
481
571
if (typeof value === "number") return <>{"{" + value + "}"}</>;
482
572
if (Array.isArray(value)) {
···
507
597
return <JSXValue value={value} indent={indent} ancestors={ancestors} />;
508
598
}
509
599
510
-
export function FlightTreeView({ flightPromise }) {
600
+
type FlightTreeViewProps = {
601
+
flightPromise: Thenable<unknown> | null;
602
+
};
603
+
604
+
export function FlightTreeView({ flightPromise }: FlightTreeViewProps): React.ReactElement {
511
605
if (!flightPromise) {
512
606
return (
513
607
<div className="flight-tree">
+47
-28
src/client/ui/Workspace.jsx
src/client/ui/Workspace.tsx
+47
-28
src/client/ui/Workspace.jsx
src/client/ui/Workspace.tsx
···
5
5
SteppableStream,
6
6
registerClientModule,
7
7
evaluateClientModule,
8
-
} from "../runtime/index.js";
9
-
import { ServerWorker } from "../server-worker.js";
8
+
type CallServerCallback,
9
+
} from "../runtime/index.ts";
10
+
import { ServerWorker } from "../server-worker.ts";
10
11
import {
11
12
parseClientModule,
12
13
parseServerActions,
13
14
compileToCommonJS,
14
15
buildManifest,
15
-
} from "../../shared/compiler.js";
16
-
import { CodeEditor } from "./CodeEditor.jsx";
17
-
import { FlightLog } from "./FlightLog.jsx";
18
-
import { LivePreview } from "./LivePreview.jsx";
16
+
} from "../../shared/compiler.ts";
17
+
import { CodeEditor } from "./CodeEditor.tsx";
18
+
import { FlightLog } from "./FlightLog.tsx";
19
+
import { LivePreview } from "./LivePreview.tsx";
19
20
20
-
export function Workspace({ initialServerCode, initialClientCode, onCodeChange }) {
21
+
type WorkspaceProps = {
22
+
initialServerCode: string;
23
+
initialClientCode: string;
24
+
onCodeChange?: (server: string, client: string) => void;
25
+
};
26
+
27
+
type CallServerRef = {
28
+
current: ((actionId: string, args: unknown[]) => Promise<unknown>) | null;
29
+
};
30
+
31
+
export function Workspace({
32
+
initialServerCode,
33
+
initialClientCode,
34
+
onCodeChange,
35
+
}: WorkspaceProps): React.ReactElement {
21
36
const [serverCode, setServerCode] = useState(initialServerCode);
22
37
const [clientCode, setClientCode] = useState(initialClientCode);
23
38
const [serverWorker] = useState(() => new ServerWorker());
24
39
const [timeline] = useState(() => new Timeline());
25
-
const [callServerRef] = useState({ current: null });
40
+
const [callServerRef] = useState<CallServerRef>({ current: null });
26
41
27
42
const snapshot = useSyncExternalStore(timeline.subscribe, timeline.getSnapshot);
28
43
const { entries, cursor, totalChunks, isAtStart, isAtEnd } = snapshot;
29
44
30
45
const [clientModuleReady, setClientModuleReady] = useState(false);
31
-
const [error, setError] = useState(null);
32
-
const [availableActions, setAvailableActions] = useState([]);
33
-
const compileTimeoutRef = useRef(null);
46
+
const [error, setError] = useState<string | null>(null);
47
+
const [availableActions, setAvailableActions] = useState<string[]>([]);
48
+
const compileTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
34
49
35
-
useEffect(() => {
36
-
window.__DEBUG_TIMELINE__ = timeline;
37
-
}, [timeline]);
38
-
39
-
const handleServerChange = (code) => {
50
+
const handleServerChange = (code: string): void => {
40
51
setServerCode(code);
41
52
onCodeChange?.(code, clientCode);
42
53
};
43
54
44
-
const handleClientChange = (code) => {
55
+
const handleClientChange = (code: string): void => {
45
56
setClientCode(code);
46
57
onCodeChange?.(serverCode, code);
47
58
};
···
55
66
}, [timeline]);
56
67
57
68
const handleAddRawAction = useCallback(
58
-
async (actionName, rawPayload) => {
69
+
async (actionName: string, rawPayload: string) => {
59
70
try {
60
71
const responseRaw = await serverWorker.callActionRaw(actionName, rawPayload);
61
-
const stream = new SteppableStream(responseRaw, { callServer: callServerRef.current });
72
+
const streamOptions = callServerRef.current ? { callServer: callServerRef.current } : {};
73
+
const stream = new SteppableStream(responseRaw, streamOptions);
62
74
await stream.waitForBuffer();
63
75
timeline.addAction(actionName, rawPayload, stream);
64
76
} catch (err) {
···
69
81
);
70
82
71
83
const compile = useCallback(
72
-
async (sCode, cCode) => {
84
+
async (sCode: string, cCode: string) => {
73
85
try {
74
86
setError(null);
75
87
timeline.clear();
···
90
102
actionNames,
91
103
});
92
104
93
-
const callServer =
105
+
const callServer: CallServerCallback | null =
94
106
actionNames.length > 0
95
-
? async (actionId, args) => {
96
-
const actionName = actionId.split("#")[0];
107
+
? async (actionId: string, args: unknown[]): Promise<unknown> => {
108
+
const actionName = actionId.split("#")[0] ?? actionId;
97
109
const encodedArgs = await encodeReply(args);
98
110
const argsDisplay =
99
111
typeof encodedArgs === "string"
100
112
? `0=${encodedArgs}`
101
-
: new URLSearchParams(encodedArgs).toString();
113
+
: new URLSearchParams(
114
+
encodedArgs as unknown as Record<string, string>,
115
+
).toString();
102
116
103
117
const responseRaw = await serverWorker.callAction(actionName, encodedArgs);
104
-
const stream = new SteppableStream(responseRaw, { callServer });
118
+
const stream = new SteppableStream(responseRaw, {
119
+
callServer: callServer as CallServerCallback,
120
+
});
105
121
await stream.waitForBuffer();
106
122
timeline.addAction(actionName, argsDisplay, stream);
107
123
return stream.flightPromise;
···
111
127
callServerRef.current = callServer;
112
128
113
129
const renderRaw = await serverWorker.render();
114
-
const renderStream = new SteppableStream(renderRaw, { callServer });
130
+
const renderStreamOptions = callServer ? { callServer } : {};
131
+
const renderStream = new SteppableStream(renderRaw, renderStreamOptions);
115
132
await renderStream.waitForBuffer();
116
133
117
134
timeline.setRender(renderStream);
118
135
setClientModuleReady(true);
119
136
} catch (err) {
120
137
console.error("[compile] Error:", err);
121
-
setError(err.message || String(err));
138
+
setError(err instanceof Error ? err.message : String(err));
122
139
timeline.clear();
123
140
setClientModuleReady(false);
124
141
}
···
131
148
}, [compile, serverCode, clientCode]);
132
149
133
150
useEffect(() => {
134
-
clearTimeout(compileTimeoutRef.current);
151
+
if (compileTimeoutRef.current) {
152
+
clearTimeout(compileTimeoutRef.current);
153
+
}
135
154
compileTimeoutRef.current = setTimeout(() => {
136
155
compile(serverCode, clientCode);
137
156
}, 300);
-50
src/client/webpack-shim.js
-50
src/client/webpack-shim.js
···
1
-
const moduleCache = {};
2
-
const moduleFactories = {};
3
-
4
-
window.__webpack_module_cache__ = moduleCache;
5
-
window.__webpack_modules__ = moduleFactories;
6
-
7
-
window.__webpack_require__ = function (moduleId) {
8
-
if (moduleCache[moduleId]) {
9
-
return moduleCache[moduleId].exports || moduleCache[moduleId];
10
-
}
11
-
if (moduleFactories[moduleId]) {
12
-
const module = { exports: {} };
13
-
moduleFactories[moduleId](module);
14
-
moduleCache[moduleId] = module;
15
-
return module.exports;
16
-
}
17
-
throw new Error(`Module ${moduleId} not found in webpack shim`);
18
-
};
19
-
20
-
window.__webpack_require__.m = moduleFactories;
21
-
window.__webpack_require__.c = moduleCache;
22
-
window.__webpack_require__.d = function (exports, definition) {
23
-
for (const key in definition) {
24
-
if (
25
-
Object.prototype.hasOwnProperty.call(definition, key) &&
26
-
!Object.prototype.hasOwnProperty.call(exports, key)
27
-
) {
28
-
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
29
-
}
30
-
}
31
-
};
32
-
window.__webpack_require__.r = function (exports) {
33
-
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
34
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
35
-
}
36
-
Object.defineProperty(exports, "__esModule", { value: true });
37
-
};
38
-
window.__webpack_require__.o = function (obj, prop) {
39
-
return Object.prototype.hasOwnProperty.call(obj, prop);
40
-
};
41
-
42
-
window.__webpack_chunk_load__ = function (chunkId) {
43
-
return Promise.resolve();
44
-
};
45
-
46
-
window.__webpack_require__.e = function (chunkId) {
47
-
return Promise.resolve();
48
-
};
49
-
50
-
window.__webpack_require__.p = "/";
+76
src/client/webpack-shim.ts
+76
src/client/webpack-shim.ts
···
1
+
// Shim webpack globals for react-server-dom-webpack/client in browser context
2
+
3
+
type ModuleFactory = {
4
+
(module: { exports: unknown }): void;
5
+
};
6
+
7
+
type WebpackRequire = {
8
+
(moduleId: string): unknown;
9
+
m: Record<string, ModuleFactory>;
10
+
c: Record<string, { exports: unknown } | unknown>;
11
+
d: (exports: object, definition: Record<string, () => unknown>) => void;
12
+
r: (exports: object) => void;
13
+
o: (obj: object, prop: string) => boolean;
14
+
e: (chunkId: string) => Promise<void>;
15
+
p: string;
16
+
};
17
+
18
+
const clientModuleCache: Record<string, { exports: unknown }> = {};
19
+
const clientModuleFactories: Record<string, ModuleFactory> = {};
20
+
21
+
window.__webpack_module_cache__ = clientModuleCache;
22
+
window.__webpack_modules__ = clientModuleFactories;
23
+
24
+
const clientWebpackRequire: WebpackRequire = function (moduleId: string): unknown {
25
+
const cached = clientModuleCache[moduleId];
26
+
if (cached) {
27
+
return cached.exports ?? cached;
28
+
}
29
+
const factory = clientModuleFactories[moduleId];
30
+
if (factory) {
31
+
const module: { exports: unknown } = { exports: {} };
32
+
factory(module);
33
+
clientModuleCache[moduleId] = module;
34
+
return module.exports;
35
+
}
36
+
throw new Error(`Module ${moduleId} not found in webpack shim`);
37
+
} as WebpackRequire;
38
+
39
+
clientWebpackRequire.m = clientModuleFactories;
40
+
clientWebpackRequire.c = clientModuleCache;
41
+
clientWebpackRequire.d = function (
42
+
exports: object,
43
+
definition: Record<string, () => unknown>,
44
+
): void {
45
+
for (const key in definition) {
46
+
const getter = definition[key];
47
+
if (
48
+
getter &&
49
+
Object.prototype.hasOwnProperty.call(definition, key) &&
50
+
!Object.prototype.hasOwnProperty.call(exports, key)
51
+
) {
52
+
Object.defineProperty(exports, key, { enumerable: true, get: getter });
53
+
}
54
+
}
55
+
};
56
+
clientWebpackRequire.r = function (exports: object): void {
57
+
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
58
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
59
+
}
60
+
Object.defineProperty(exports, "__esModule", { value: true });
61
+
};
62
+
clientWebpackRequire.o = function (obj: object, prop: string): boolean {
63
+
return Object.prototype.hasOwnProperty.call(obj, prop);
64
+
};
65
+
clientWebpackRequire.e = function (_chunkId: string): Promise<void> {
66
+
return Promise.resolve();
67
+
};
68
+
clientWebpackRequire.p = "/";
69
+
70
+
window.__webpack_require__ = clientWebpackRequire;
71
+
72
+
window.__webpack_chunk_load__ = function (_chunkId: string): Promise<void> {
73
+
return Promise.resolve();
74
+
};
75
+
76
+
export {};
-82
src/embed.js
-82
src/embed.js
···
1
-
/**
2
-
* RSC Explorer Embed API
3
-
*
4
-
* Usage:
5
-
* ```html
6
-
* <div id="demo" style="height: 500px;"></div>
7
-
* <script type="module">
8
-
* import { mount } from 'https://rscexplorer.dev/embed.js';
9
-
*
10
-
* mount('#demo', {
11
-
* server: `
12
-
* export default function App() {
13
-
* return <h1>Hello RSC</h1>;
14
-
* }
15
-
* `,
16
-
* client: `
17
-
* 'use client'
18
-
* export function Button() {
19
-
* return <button>Click</button>;
20
-
* }
21
-
* `
22
-
* });
23
-
* </script>
24
-
* ```
25
-
*/
26
-
27
-
// Get the embed URL relative to this script's location
28
-
const getEmbedUrl = () => {
29
-
return new URL("embed.html", import.meta.url).href;
30
-
};
31
-
32
-
/**
33
-
* Mount an RSC Explorer embed into a container element
34
-
* @param {string|HTMLElement} container - CSS selector or DOM element
35
-
* @param {Object} options - Configuration options
36
-
* @param {string} options.server - Server component code
37
-
* @param {string} options.client - Client component code
38
-
* @returns {Object} - Control object with methods to interact with the embed
39
-
*/
40
-
export function mount(container, { server, client }) {
41
-
const el = typeof container === "string" ? document.querySelector(container) : container;
42
-
43
-
if (!el) {
44
-
throw new Error(`RSC Explorer: Container not found: ${container}`);
45
-
}
46
-
47
-
// Create iframe
48
-
const iframe = document.createElement("iframe");
49
-
iframe.src = getEmbedUrl();
50
-
iframe.style.cssText =
51
-
"width: 100%; height: 100%; border: 1px solid #e0e0e0; border-radius: 8px;";
52
-
53
-
// Wait for iframe to be ready, then send code
54
-
const handleMessage = (event) => {
55
-
if (event.source !== iframe.contentWindow) return;
56
-
57
-
if (event.data?.type === "rsc-embed:ready") {
58
-
iframe.contentWindow.postMessage(
59
-
{
60
-
type: "rsc-embed:init",
61
-
code: { server: server.trim(), client: client.trim() },
62
-
},
63
-
"*",
64
-
);
65
-
}
66
-
};
67
-
68
-
window.addEventListener("message", handleMessage);
69
-
70
-
// Clear container and add iframe
71
-
el.innerHTML = "";
72
-
el.appendChild(iframe);
73
-
74
-
// Return control object
75
-
return {
76
-
iframe,
77
-
destroy: () => {
78
-
window.removeEventListener("message", handleMessage);
79
-
el.innerHTML = "";
80
-
},
81
-
};
82
-
}
+107
src/embed.ts
+107
src/embed.ts
···
1
+
/**
2
+
* RSC Explorer Embed API
3
+
*
4
+
* Usage:
5
+
* ```html
6
+
* <div id="demo" style="height: 500px;"></div>
7
+
* <script type="module">
8
+
* import { mount } from 'https://rscexplorer.dev/embed.js';
9
+
*
10
+
* mount('#demo', {
11
+
* server: `
12
+
* export default function App() {
13
+
* return <h1>Hello RSC</h1>;
14
+
* }
15
+
* `,
16
+
* client: `
17
+
* 'use client'
18
+
* export function Button() {
19
+
* return <button>Click</button>;
20
+
* }
21
+
* `
22
+
* });
23
+
* </script>
24
+
* ```
25
+
*/
26
+
27
+
type EmbedOptions = {
28
+
server: string;
29
+
client: string;
30
+
};
31
+
32
+
type EmbedControl = {
33
+
iframe: HTMLIFrameElement;
34
+
destroy: () => void;
35
+
};
36
+
37
+
type EmbedReadyMessage = {
38
+
type: "rsc-embed:ready";
39
+
};
40
+
41
+
type EmbedInitMessage = {
42
+
type: "rsc-embed:init";
43
+
code: {
44
+
server: string;
45
+
client: string;
46
+
};
47
+
};
48
+
49
+
function isEmbedReadyMessage(data: unknown): data is EmbedReadyMessage {
50
+
return (
51
+
typeof data === "object" &&
52
+
data !== null &&
53
+
(data as { type?: string }).type === "rsc-embed:ready"
54
+
);
55
+
}
56
+
57
+
const getEmbedUrl = (): string => {
58
+
return new URL("embed.html", import.meta.url).href;
59
+
};
60
+
61
+
/**
62
+
* Mount an RSC Explorer embed into a container element
63
+
* @param container - CSS selector or DOM element
64
+
* @param options - Configuration options
65
+
* @returns Control object with methods to interact with the embed
66
+
*/
67
+
export function mount(
68
+
container: string | HTMLElement,
69
+
{ server, client }: EmbedOptions,
70
+
): EmbedControl {
71
+
const el =
72
+
typeof container === "string" ? document.querySelector<HTMLElement>(container) : container;
73
+
74
+
if (!el) {
75
+
throw new Error(`RSC Explorer: Container not found: ${container}`);
76
+
}
77
+
78
+
const iframe = document.createElement("iframe");
79
+
iframe.src = getEmbedUrl();
80
+
iframe.style.cssText =
81
+
"width: 100%; height: 100%; border: 1px solid #e0e0e0; border-radius: 8px;";
82
+
83
+
const handleMessage = (event: MessageEvent<unknown>): void => {
84
+
if (event.source !== iframe.contentWindow) return;
85
+
86
+
if (isEmbedReadyMessage(event.data)) {
87
+
const initMessage: EmbedInitMessage = {
88
+
type: "rsc-embed:init",
89
+
code: { server: server.trim(), client: client.trim() },
90
+
};
91
+
iframe.contentWindow?.postMessage(initMessage, "*");
92
+
}
93
+
};
94
+
95
+
window.addEventListener("message", handleMessage);
96
+
97
+
el.innerHTML = "";
98
+
el.appendChild(iframe);
99
+
100
+
return {
101
+
iframe,
102
+
destroy: (): void => {
103
+
window.removeEventListener("message", handleMessage);
104
+
el.innerHTML = "";
105
+
},
106
+
};
107
+
}
-43
src/server/webpack-shim.js
-43
src/server/webpack-shim.js
···
1
-
// Shim webpack globals for react-server-dom-webpack/server in worker context
2
-
// Uses self instead of window since this runs in a Web Worker
3
-
4
-
const moduleCache = {};
5
-
6
-
self.__webpack_require__ = function (moduleId) {
7
-
if (moduleCache[moduleId]) {
8
-
return moduleCache[moduleId];
9
-
}
10
-
throw new Error(`Module ${moduleId} not found in webpack shim`);
11
-
};
12
-
13
-
self.__webpack_require__.m = {};
14
-
self.__webpack_require__.c = moduleCache;
15
-
self.__webpack_require__.d = function (exports, definition) {
16
-
for (const key in definition) {
17
-
if (
18
-
Object.prototype.hasOwnProperty.call(definition, key) &&
19
-
!Object.prototype.hasOwnProperty.call(exports, key)
20
-
) {
21
-
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
22
-
}
23
-
}
24
-
};
25
-
self.__webpack_require__.r = function (exports) {
26
-
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
27
-
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
28
-
}
29
-
Object.defineProperty(exports, "__esModule", { value: true });
30
-
};
31
-
self.__webpack_require__.o = function (obj, prop) {
32
-
return Object.prototype.hasOwnProperty.call(obj, prop);
33
-
};
34
-
35
-
self.__webpack_chunk_load__ = function (chunkId) {
36
-
return Promise.resolve();
37
-
};
38
-
39
-
self.__webpack_require__.e = function (chunkId) {
40
-
return Promise.resolve();
41
-
};
42
-
43
-
self.__webpack_require__.p = "/";
+68
src/server/webpack-shim.ts
+68
src/server/webpack-shim.ts
···
1
+
// Shim webpack globals for react-server-dom-webpack/server in worker context
2
+
// Uses self instead of window since this runs in a Web Worker
3
+
4
+
type WebpackRequire = {
5
+
(moduleId: string): unknown;
6
+
m: Record<string, (module: { exports: unknown }) => void>;
7
+
c: Record<string, unknown>;
8
+
d: (exports: object, definition: Record<string, () => unknown>) => void;
9
+
r: (exports: object) => void;
10
+
o: (obj: object, prop: string) => boolean;
11
+
e: (chunkId: string) => Promise<void>;
12
+
p: string;
13
+
};
14
+
15
+
type WorkerSelf = DedicatedWorkerGlobalScope & {
16
+
__webpack_require__: WebpackRequire;
17
+
__webpack_chunk_load__: (chunkId: string) => Promise<void>;
18
+
};
19
+
20
+
const workerSelf = self as unknown as WorkerSelf;
21
+
22
+
const moduleCache: Record<string, unknown> = {};
23
+
24
+
const webpackRequire: WebpackRequire = function (moduleId: string): unknown {
25
+
if (moduleCache[moduleId]) {
26
+
return moduleCache[moduleId];
27
+
}
28
+
throw new Error(`Module ${moduleId} not found in webpack shim`);
29
+
} as WebpackRequire;
30
+
31
+
webpackRequire.m = {};
32
+
webpackRequire.c = moduleCache;
33
+
webpackRequire.d = function (exports: object, definition: Record<string, () => unknown>): void {
34
+
for (const key in definition) {
35
+
const getter = definition[key];
36
+
if (
37
+
getter &&
38
+
Object.prototype.hasOwnProperty.call(definition, key) &&
39
+
!Object.prototype.hasOwnProperty.call(exports, key)
40
+
) {
41
+
Object.defineProperty(exports, key, {
42
+
enumerable: true,
43
+
get: getter,
44
+
});
45
+
}
46
+
}
47
+
};
48
+
webpackRequire.r = function (exports: object): void {
49
+
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
50
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
51
+
}
52
+
Object.defineProperty(exports, "__esModule", { value: true });
53
+
};
54
+
webpackRequire.o = function (obj: object, prop: string): boolean {
55
+
return Object.prototype.hasOwnProperty.call(obj, prop);
56
+
};
57
+
webpackRequire.e = function (_chunkId: string): Promise<void> {
58
+
return Promise.resolve();
59
+
};
60
+
webpackRequire.p = "/";
61
+
62
+
workerSelf.__webpack_require__ = webpackRequire;
63
+
64
+
workerSelf.__webpack_chunk_load__ = function (_chunkId: string): Promise<void> {
65
+
return Promise.resolve();
66
+
};
67
+
68
+
export {};
-154
src/server/worker.js
-154
src/server/worker.js
···
1
-
// Server Worker - RSC server simulation
2
-
//
3
-
// Models a real server: deploy code once, then handle requests against it.
4
-
// - `deploy`: Store compiled code, manifest, etc. (like deploying to production)
5
-
// - `render`/`action`: Execute against deployed code
6
-
7
-
import "./webpack-shim.js";
8
-
import "../client/byte-stream-polyfill.js";
9
-
import "text-encoding";
10
-
11
-
import {
12
-
renderToReadableStream,
13
-
registerServerReference,
14
-
createClientModuleProxy,
15
-
decodeReply,
16
-
} from "react-server-dom-webpack/server";
17
-
import React from "react";
18
-
19
-
let deployed = null;
20
-
21
-
// Safari doesn't support transferable streams
22
-
async function streamToMain(stream, requestId) {
23
-
const reader = stream.getReader();
24
-
try {
25
-
while (true) {
26
-
const { done, value } = await reader.read();
27
-
if (done) {
28
-
self.postMessage({ type: "stream-end", requestId });
29
-
break;
30
-
}
31
-
self.postMessage({ type: "stream-chunk", requestId, chunk: value });
32
-
}
33
-
} catch (err) {
34
-
self.postMessage({ type: "stream-error", requestId, error: { message: err.message } });
35
-
}
36
-
}
37
-
38
-
self.onmessage = async (event) => {
39
-
const { type, requestId } = event.data;
40
-
41
-
try {
42
-
switch (type) {
43
-
case "deploy":
44
-
handleDeploy(event.data);
45
-
break;
46
-
case "render":
47
-
await handleRender(event.data);
48
-
break;
49
-
case "action":
50
-
await handleAction(event.data);
51
-
break;
52
-
default:
53
-
throw new Error(`Unknown message type: ${type}`);
54
-
}
55
-
} catch (error) {
56
-
self.postMessage({
57
-
type: "error",
58
-
requestId,
59
-
error: { message: error.message, stack: error.stack },
60
-
});
61
-
}
62
-
};
63
-
64
-
function handleDeploy({ compiledCode, manifest, actionNames, requestId }) {
65
-
const clientModule = createClientModuleProxy("client");
66
-
const modules = { react: React, "./client": clientModule };
67
-
const serverModule = evalModule(compiledCode, modules, actionNames);
68
-
69
-
deployed = { manifest, serverModule, actionNames };
70
-
71
-
self.postMessage({ type: "deployed", requestId });
72
-
}
73
-
74
-
function requireDeployed() {
75
-
if (!deployed) throw new Error("No code deployed");
76
-
return deployed;
77
-
}
78
-
79
-
async function handleRender({ requestId }) {
80
-
const { manifest, serverModule } = requireDeployed();
81
-
82
-
const App = serverModule.default || serverModule;
83
-
const element = typeof App === "function" ? React.createElement(App) : App;
84
-
85
-
const flightStream = renderToReadableStream(element, manifest, {
86
-
onError: (error) => error.message || String(error),
87
-
});
88
-
89
-
self.postMessage({ type: "stream-start", requestId });
90
-
streamToMain(flightStream, requestId);
91
-
}
92
-
93
-
async function handleAction({ actionId, encodedArgs, requestId }) {
94
-
const { manifest, serverModule } = requireDeployed();
95
-
96
-
const actionFn = serverModule[actionId];
97
-
if (typeof actionFn !== "function") {
98
-
throw new Error(`Action "${actionId}" not found`);
99
-
}
100
-
101
-
const toDecode = reconstructEncodedArgs(encodedArgs);
102
-
const args = await decodeReply(toDecode, {});
103
-
const result = await actionFn(...(Array.isArray(args) ? args : [args]));
104
-
105
-
const flightStream = renderToReadableStream(result, manifest, {
106
-
onError: (error) => error.message || String(error),
107
-
});
108
-
109
-
self.postMessage({ type: "stream-start", requestId });
110
-
streamToMain(flightStream, requestId);
111
-
}
112
-
113
-
function reconstructEncodedArgs(encodedArgs) {
114
-
if (encodedArgs.type === "formdata") {
115
-
const formData = new FormData();
116
-
for (const [key, value] of new URLSearchParams(encodedArgs.data)) {
117
-
formData.append(key, value);
118
-
}
119
-
return formData;
120
-
}
121
-
return encodedArgs.data;
122
-
}
123
-
124
-
function evalModule(code, modules, actionNames) {
125
-
let finalCode = code;
126
-
if (actionNames?.length > 0) {
127
-
finalCode +=
128
-
"\n" +
129
-
actionNames
130
-
.map(
131
-
(name) =>
132
-
`__registerServerReference(${name}, "${name}", "${name}"); exports.${name} = ${name};`,
133
-
)
134
-
.join("\n");
135
-
}
136
-
137
-
const module = { exports: {} };
138
-
const require = (id) => {
139
-
if (!modules[id]) throw new Error(`Module "${id}" not found`);
140
-
return modules[id];
141
-
};
142
-
143
-
new Function("module", "exports", "require", "React", "__registerServerReference", finalCode)(
144
-
module,
145
-
module.exports,
146
-
require,
147
-
React,
148
-
registerServerReference,
149
-
);
150
-
151
-
return module.exports;
152
-
}
153
-
154
-
self.postMessage({ type: "ready" });
+273
src/server/worker.ts
+273
src/server/worker.ts
···
1
+
// Server Worker - RSC server simulation
2
+
//
3
+
// Models a real server: deploy code once, then handle requests against it.
4
+
// - `deploy`: Store compiled code, manifest, etc. (like deploying to production)
5
+
// - `render`/`action`: Execute against deployed code
6
+
7
+
import "./webpack-shim.ts";
8
+
import "../client/byte-stream-polyfill.ts";
9
+
import "text-encoding";
10
+
11
+
import {
12
+
renderToReadableStream,
13
+
registerServerReference,
14
+
createClientModuleProxy,
15
+
decodeReply,
16
+
type ClientManifest,
17
+
} from "react-server-dom-webpack/server";
18
+
import React from "react";
19
+
20
+
declare const self: DedicatedWorkerGlobalScope;
21
+
22
+
type DeployMessage = {
23
+
type: "deploy";
24
+
requestId: string;
25
+
compiledCode: string;
26
+
manifest: ClientManifest;
27
+
actionNames: string[];
28
+
};
29
+
30
+
type RenderMessage = {
31
+
type: "render";
32
+
requestId: string;
33
+
};
34
+
35
+
type ActionMessage = {
36
+
type: "action";
37
+
requestId: string;
38
+
actionId: string;
39
+
encodedArgs: EncodedArgs;
40
+
};
41
+
42
+
type WorkerMessage = DeployMessage | RenderMessage | ActionMessage;
43
+
44
+
type EncodedArgs = {
45
+
type: "formdata" | "string";
46
+
data: string;
47
+
};
48
+
49
+
type ErrorResponse = {
50
+
type: "error";
51
+
requestId: string;
52
+
error: { message: string; stack?: string };
53
+
};
54
+
55
+
type DeployedResponse = {
56
+
type: "deployed";
57
+
requestId: string;
58
+
};
59
+
60
+
type StreamStartResponse = {
61
+
type: "stream-start";
62
+
requestId: string;
63
+
};
64
+
65
+
type StreamChunkResponse = {
66
+
type: "stream-chunk";
67
+
requestId: string;
68
+
chunk: Uint8Array;
69
+
};
70
+
71
+
type StreamEndResponse = {
72
+
type: "stream-end";
73
+
requestId: string;
74
+
};
75
+
76
+
type StreamErrorResponse = {
77
+
type: "stream-error";
78
+
requestId: string;
79
+
error: { message: string };
80
+
};
81
+
82
+
type ReadyResponse = {
83
+
type: "ready";
84
+
};
85
+
86
+
type ServerModule = {
87
+
default?: React.ComponentType | React.ReactNode;
88
+
[key: string]: unknown;
89
+
};
90
+
91
+
type DeployedState = {
92
+
manifest: ClientManifest;
93
+
serverModule: ServerModule;
94
+
actionNames: string[];
95
+
};
96
+
97
+
let deployed: DeployedState | null = null;
98
+
99
+
// Safari doesn't support transferable streams
100
+
async function streamToMain(stream: ReadableStream<Uint8Array>, requestId: string): Promise<void> {
101
+
const reader = stream.getReader();
102
+
try {
103
+
while (true) {
104
+
const { done, value } = await reader.read();
105
+
if (done) {
106
+
self.postMessage({ type: "stream-end", requestId } satisfies StreamEndResponse);
107
+
break;
108
+
}
109
+
self.postMessage({
110
+
type: "stream-chunk",
111
+
requestId,
112
+
chunk: value,
113
+
} satisfies StreamChunkResponse);
114
+
}
115
+
} catch (err) {
116
+
const error = err instanceof Error ? err : new Error(String(err));
117
+
self.postMessage({
118
+
type: "stream-error",
119
+
requestId,
120
+
error: { message: error.message },
121
+
} satisfies StreamErrorResponse);
122
+
}
123
+
}
124
+
125
+
self.onmessage = async (event: MessageEvent<WorkerMessage>) => {
126
+
const { type, requestId } = event.data;
127
+
128
+
try {
129
+
switch (type) {
130
+
case "deploy":
131
+
handleDeploy(event.data);
132
+
break;
133
+
case "render":
134
+
await handleRender(event.data);
135
+
break;
136
+
case "action":
137
+
await handleAction(event.data);
138
+
break;
139
+
default: {
140
+
const _exhaustive: never = type;
141
+
throw new Error(`Unknown message type: ${_exhaustive}`);
142
+
}
143
+
}
144
+
} catch (error) {
145
+
const err = error instanceof Error ? error : new Error(String(error));
146
+
const errorPayload: { message: string; stack?: string } = { message: err.message };
147
+
if (err.stack) {
148
+
errorPayload.stack = err.stack;
149
+
}
150
+
self.postMessage({
151
+
type: "error",
152
+
requestId,
153
+
error: errorPayload,
154
+
} satisfies ErrorResponse);
155
+
}
156
+
};
157
+
158
+
function handleDeploy({ compiledCode, manifest, actionNames, requestId }: DeployMessage): void {
159
+
const clientModule = createClientModuleProxy("client");
160
+
const modules: Record<string, unknown> = { react: React, "./client": clientModule };
161
+
const serverModule = evalModule(compiledCode, modules, actionNames);
162
+
163
+
deployed = { manifest, serverModule, actionNames };
164
+
165
+
self.postMessage({ type: "deployed", requestId } satisfies DeployedResponse);
166
+
}
167
+
168
+
function requireDeployed(): DeployedState {
169
+
if (!deployed) throw new Error("No code deployed");
170
+
return deployed;
171
+
}
172
+
173
+
async function handleRender({ requestId }: RenderMessage): Promise<void> {
174
+
const { manifest, serverModule } = requireDeployed();
175
+
176
+
const App = serverModule.default ?? serverModule;
177
+
const element =
178
+
typeof App === "function"
179
+
? React.createElement(App as React.ComponentType)
180
+
: (App as React.ReactNode);
181
+
182
+
const flightStream = renderToReadableStream(element, manifest, {
183
+
onError: (error: unknown) => {
184
+
if (error instanceof Error) return error.message;
185
+
return String(error);
186
+
},
187
+
});
188
+
189
+
self.postMessage({ type: "stream-start", requestId } satisfies StreamStartResponse);
190
+
streamToMain(flightStream, requestId);
191
+
}
192
+
193
+
async function handleAction({ actionId, encodedArgs, requestId }: ActionMessage): Promise<void> {
194
+
const { manifest, serverModule } = requireDeployed();
195
+
196
+
const actionFn = serverModule[actionId];
197
+
if (typeof actionFn !== "function") {
198
+
throw new Error(`Action "${actionId}" not found`);
199
+
}
200
+
201
+
const toDecode = reconstructEncodedArgs(encodedArgs);
202
+
const args = await decodeReply(toDecode, {});
203
+
const argsArray = Array.isArray(args) ? args : [args];
204
+
const result = (await (actionFn as (...args: unknown[]) => Promise<unknown>)(
205
+
...argsArray,
206
+
)) as React.ReactNode;
207
+
208
+
const flightStream = renderToReadableStream(result, manifest, {
209
+
onError: (error: unknown) => {
210
+
if (error instanceof Error) return error.message;
211
+
return String(error);
212
+
},
213
+
});
214
+
215
+
self.postMessage({ type: "stream-start", requestId } satisfies StreamStartResponse);
216
+
streamToMain(flightStream, requestId);
217
+
}
218
+
219
+
function reconstructEncodedArgs(encodedArgs: EncodedArgs): FormData | string {
220
+
if (encodedArgs.type === "formdata") {
221
+
const formData = new FormData();
222
+
for (const [key, value] of new URLSearchParams(encodedArgs.data)) {
223
+
formData.append(key, value);
224
+
}
225
+
return formData;
226
+
}
227
+
return encodedArgs.data;
228
+
}
229
+
230
+
function evalModule(
231
+
code: string,
232
+
modules: Record<string, unknown>,
233
+
actionNames: string[] | undefined,
234
+
): ServerModule {
235
+
let finalCode = code;
236
+
if (actionNames && actionNames.length > 0) {
237
+
finalCode +=
238
+
"\n" +
239
+
actionNames
240
+
.map(
241
+
(name) =>
242
+
`__registerServerReference(${name}, "${name}", "${name}"); exports.${name} = ${name};`,
243
+
)
244
+
.join("\n");
245
+
}
246
+
247
+
const module: { exports: ServerModule } = { exports: {} };
248
+
const require = (id: string): unknown => {
249
+
if (!modules[id]) throw new Error(`Module "${id}" not found`);
250
+
return modules[id];
251
+
};
252
+
253
+
const fn = new Function(
254
+
"module",
255
+
"exports",
256
+
"require",
257
+
"React",
258
+
"__registerServerReference",
259
+
finalCode,
260
+
) as (
261
+
module: { exports: ServerModule },
262
+
exports: ServerModule,
263
+
require: (id: string) => unknown,
264
+
ReactLib: typeof React,
265
+
registerServerRef: typeof registerServerReference,
266
+
) => void;
267
+
268
+
fn(module, module.exports, require, React, registerServerReference);
269
+
270
+
return module.exports;
271
+
}
272
+
273
+
self.postMessage({ type: "ready" } satisfies ReadyResponse);
+5
-2
tests/async.spec.js
tests/async.spec.ts
+5
-2
tests/async.spec.js
tests/async.spec.ts
···
1
1
import { test, expect, beforeAll, afterAll, afterEach } from "vitest";
2
-
import { createHelpers, launchBrowser } from "./helpers.js";
2
+
import { createHelpers, launchBrowser, type TestHelpers } from "./helpers.ts";
3
+
import type { Browser, Page } from "playwright";
3
4
4
-
let browser, page, h;
5
+
let browser: Browser;
6
+
let page: Page;
7
+
let h: TestHelpers;
5
8
6
9
beforeAll(async () => {
7
10
browser = await launchBrowser();
+5
-2
tests/bound.spec.js
tests/bound.spec.ts
+5
-2
tests/bound.spec.js
tests/bound.spec.ts
···
1
1
import { test, expect, beforeAll, afterAll, afterEach } from "vitest";
2
-
import { createHelpers, launchBrowser } from "./helpers.js";
2
+
import { createHelpers, launchBrowser, type TestHelpers } from "./helpers.ts";
3
+
import type { Browser, Page } from "playwright";
3
4
4
-
let browser, page, h;
5
+
let browser: Browser;
6
+
let page: Page;
7
+
let h: TestHelpers;
5
8
6
9
beforeAll(async () => {
7
10
browser = await launchBrowser();
+6
-3
tests/clientref.spec.js
tests/clientref.spec.ts
+6
-3
tests/clientref.spec.js
tests/clientref.spec.ts
···
1
1
import { test, expect, beforeAll, afterAll, afterEach } from "vitest";
2
-
import { createHelpers, launchBrowser } from "./helpers.js";
2
+
import { createHelpers, launchBrowser, type TestHelpers } from "./helpers.ts";
3
+
import type { Browser, Page } from "playwright";
3
4
4
-
let browser, page, h;
5
+
let browser: Browser;
6
+
let page: Page;
7
+
let h: TestHelpers;
5
8
6
9
beforeAll(async () => {
7
10
browser = await launchBrowser();
···
22
25
23
26
// Check flight rows include client references for themes
24
27
const rows = await h.getRows();
25
-
expect(rows.some((r) => r.text.includes("darkTheme") || r.text.includes("lightTheme"))).toBe(
28
+
expect(rows.some((r) => r.text?.includes("darkTheme") || r.text?.includes("lightTheme"))).toBe(
26
29
true,
27
30
);
28
31
+5
-2
tests/counter.spec.js
tests/counter.spec.ts
+5
-2
tests/counter.spec.js
tests/counter.spec.ts
···
1
1
import { test, expect, beforeAll, afterAll, afterEach } from "vitest";
2
-
import { createHelpers, launchBrowser } from "./helpers.js";
2
+
import { createHelpers, launchBrowser, type TestHelpers } from "./helpers.ts";
3
+
import type { Browser, Page } from "playwright";
3
4
4
-
let browser, page, h;
5
+
let browser: Browser;
6
+
let page: Page;
7
+
let h: TestHelpers;
5
8
6
9
beforeAll(async () => {
7
10
browser = await launchBrowser();
+5
-2
tests/errors.spec.js
tests/errors.spec.ts
+5
-2
tests/errors.spec.js
tests/errors.spec.ts
···
1
1
import { test, expect, beforeAll, afterAll, afterEach } from "vitest";
2
-
import { createHelpers, launchBrowser } from "./helpers.js";
2
+
import { createHelpers, launchBrowser, type TestHelpers } from "./helpers.ts";
3
+
import type { Browser, Page } from "playwright";
3
4
4
-
let browser, page, h;
5
+
let browser: Browser;
6
+
let page: Page;
7
+
let h: TestHelpers;
5
8
6
9
beforeAll(async () => {
7
10
browser = await launchBrowser();
+5
-2
tests/form.spec.js
tests/form.spec.ts
+5
-2
tests/form.spec.js
tests/form.spec.ts
···
1
1
import { test, expect, beforeAll, afterAll, afterEach } from "vitest";
2
-
import { createHelpers, launchBrowser } from "./helpers.js";
2
+
import { createHelpers, launchBrowser, type TestHelpers } from "./helpers.ts";
3
+
import type { Browser, Page } from "playwright";
3
4
4
-
let browser, page, h;
5
+
let browser: Browser;
6
+
let page: Page;
7
+
let h: TestHelpers;
5
8
6
9
beforeAll(async () => {
7
10
browser = await launchBrowser();
+5
-5
tests/globalSetup.js
tests/globalSetup.ts
+5
-5
tests/globalSetup.js
tests/globalSetup.ts
···
1
-
import { spawn } from "child_process";
1
+
import { spawn, type ChildProcess } from "child_process";
2
2
3
-
let server;
3
+
let server: ChildProcess | null = null;
4
4
5
-
async function waitForServer(url, timeout = 30000) {
5
+
async function waitForServer(url: string, timeout = 30000): Promise<void> {
6
6
const start = Date.now();
7
7
while (Date.now() - start < timeout) {
8
8
try {
···
16
16
throw new Error("Test server start timeout");
17
17
}
18
18
19
-
export async function setup() {
19
+
export async function setup(): Promise<void> {
20
20
server = spawn("npx", ["vite", "--port", "5599", "--strictPort"], {
21
21
stdio: "inherit",
22
22
shell: true,
···
32
32
await waitForServer("http://localhost:5599");
33
33
}
34
34
35
-
export async function teardown() {
35
+
export async function teardown(): Promise<void> {
36
36
if (server) {
37
37
server.kill();
38
38
}
+5
-2
tests/hello.spec.js
tests/hello.spec.ts
+5
-2
tests/hello.spec.js
tests/hello.spec.ts
···
1
1
import { test, expect, beforeAll, afterAll, afterEach } from "vitest";
2
-
import { createHelpers, launchBrowser } from "./helpers.js";
2
+
import { createHelpers, launchBrowser, type TestHelpers } from "./helpers.ts";
3
+
import type { Browser, Page } from "playwright";
3
4
4
-
let browser, page, h;
5
+
let browser: Browser;
6
+
let page: Page;
7
+
let h: TestHelpers;
5
8
6
9
beforeAll(async () => {
7
10
browser = await launchBrowser();
+61
-28
tests/helpers.js
tests/helpers.ts
+61
-28
tests/helpers.js
tests/helpers.ts
···
1
1
import { expect } from "vitest";
2
-
import { chromium } from "playwright";
2
+
import { chromium, type Browser, type Page, type FrameLocator } from "playwright";
3
3
4
-
export async function launchBrowser() {
4
+
export async function launchBrowser(): Promise<Browser> {
5
5
const executablePath = process.env.CHROMIUM_PATH;
6
6
return chromium.launch(executablePath ? { executablePath } : undefined);
7
7
}
8
8
9
-
let prevRowTexts = [];
10
-
let prevStatuses = [];
9
+
type RowData = {
10
+
text: string | null;
11
+
status: "done" | "next" | "pending";
12
+
};
13
+
14
+
type WaitForOptions = {
15
+
timeout?: number;
16
+
};
17
+
18
+
export type TestHelpers = {
19
+
load: (sample: string) => Promise<void>;
20
+
step: () => Promise<string | null>;
21
+
stepAll: () => Promise<string | null>;
22
+
stepInfo: () => Promise<string>;
23
+
getRows: () => Promise<RowData[]>;
24
+
preview: (waitFor?: string) => Promise<string>;
25
+
tree: () => Promise<string | null>;
26
+
checkNoRemainingSteps: () => Promise<void>;
27
+
frame: () => FrameLocator;
28
+
waitFor: (predicate: () => boolean, options?: WaitForOptions) => Promise<void>;
29
+
};
30
+
31
+
let prevRowTexts: (string | null)[] = [];
32
+
let prevStatuses: ("done" | "next" | "pending")[] = [];
11
33
let prevPreview = "";
12
34
let previewAsserted = true;
13
-
let pageRef = null;
14
-
let frameRef = null;
35
+
let pageRef: Page | null = null;
36
+
let frameRef: FrameLocator | null = null;
15
37
16
-
export function createHelpers(page) {
38
+
export function createHelpers(page: Page): TestHelpers {
17
39
pageRef = page;
18
40
19
-
async function load(sample) {
41
+
async function load(sample: string): Promise<void> {
20
42
await page.goto(`http://localhost:5599/?s=${sample}`);
21
43
// Wait for iframe to load and get frame reference
22
44
const iframe = page.frameLocator("iframe");
···
30
52
previewAsserted = true;
31
53
}
32
54
33
-
async function getPreviewText() {
55
+
async function getPreviewText(): Promise<string> {
56
+
if (!frameRef) throw new Error("frameRef not initialized");
34
57
return (await frameRef.locator(".preview-container").innerText()).trim();
35
58
}
36
59
37
-
async function doStep() {
60
+
async function doStep(): Promise<string | null> {
61
+
if (!frameRef || !pageRef) throw new Error("refs not initialized");
38
62
const btn = frameRef.locator(".control-btn").nth(2);
39
63
if (await btn.isDisabled()) return null;
40
64
await btn.click();
···
69
93
return await tree();
70
94
}
71
95
72
-
async function step() {
96
+
async function step(): Promise<string | null> {
73
97
// Check for unasserted preview changes before stepping
74
98
const currentPreview = await getPreviewText();
75
99
if (currentPreview !== prevPreview && !previewAsserted) {
···
81
105
return await doStep();
82
106
}
83
107
84
-
async function waitForStepButton() {
108
+
async function waitForStepButton(): Promise<void> {
109
+
if (!frameRef || !pageRef) throw new Error("refs not initialized");
85
110
const btn = frameRef.locator(".control-btn").nth(2);
86
111
// Wait for button to be enabled
87
112
await expect
···
95
120
await pageRef.waitForTimeout(50);
96
121
}
97
122
98
-
async function stepAll() {
123
+
async function stepAll(): Promise<string | null> {
99
124
// Check for unasserted preview changes before stepping
100
125
const currentPreview = await getPreviewText();
101
126
if (currentPreview !== prevPreview && !previewAsserted) {
···
136
161
return await tree();
137
162
}
138
163
139
-
async function preview(waitFor) {
164
+
async function preview(waitFor?: string): Promise<string> {
140
165
if (waitFor) {
141
166
// Wait for preview to contain the marker
142
167
await expect.poll(() => getPreviewText(), { timeout: 10000 }).toContain(waitFor);
···
149
174
return current;
150
175
}
151
176
152
-
async function stepInfo() {
177
+
async function stepInfo(): Promise<string> {
178
+
if (!frameRef) throw new Error("frameRef not initialized");
153
179
return (await frameRef.locator(".step-info").innerText()).trim();
154
180
}
155
181
156
-
async function getRows() {
182
+
async function getRows(): Promise<RowData[]> {
183
+
if (!frameRef) throw new Error("frameRef not initialized");
157
184
return frameRef.locator(".flight-line").evaluateAll((els) =>
158
-
els
185
+
(els as HTMLElement[])
159
186
.map((el) => ({
160
187
text: el.textContent,
161
188
status: el.classList.contains("line-done")
162
-
? "done"
189
+
? ("done" as const)
163
190
: el.classList.contains("line-next")
164
-
? "next"
165
-
: "pending",
191
+
? ("next" as const)
192
+
: ("pending" as const),
166
193
}))
167
194
.filter(
168
195
({ text }) =>
196
+
text !== null &&
169
197
!text.startsWith(":N") &&
170
198
!/^\w+:D/.test(text) &&
171
199
!/^\w+:\{.*"name"/.test(text) &&
···
174
202
);
175
203
}
176
204
177
-
async function tree() {
205
+
async function tree(): Promise<string | null> {
206
+
if (!frameRef) throw new Error("frameRef not initialized");
178
207
// Find the log entry containing the "next" line, or the last done entry
179
208
const treeText = await frameRef.locator(".log-entry").evaluateAll((entries) => {
180
209
const nextLine = document.querySelector(".line-next");
181
210
if (nextLine) {
182
211
const entry = nextLine.closest(".log-entry");
183
-
const tree = entry?.querySelector(".log-entry-tree");
212
+
const tree = entry?.querySelector(".log-entry-tree") as HTMLElement | null;
184
213
return tree?.innerText?.trim() || null;
185
214
}
186
215
// No next line - get the last entry's tree
187
216
if (entries.length === 0) return null;
188
-
const lastEntry = entries[entries.length - 1];
189
-
const tree = lastEntry.querySelector(".log-entry-tree");
217
+
const lastEntry = entries[entries.length - 1] as HTMLElement | undefined;
218
+
if (!lastEntry) return null;
219
+
const tree = lastEntry.querySelector(".log-entry-tree") as HTMLElement | null;
190
220
return tree?.innerText?.trim() || null;
191
221
});
192
222
return treeText;
193
223
}
194
224
195
-
async function checkNoRemainingSteps() {
225
+
async function checkNoRemainingSteps(): Promise<void> {
226
+
if (!frameRef || !pageRef) throw new Error("refs not initialized");
196
227
const initialTree = await tree();
197
228
const initialPreview = await getPreviewText();
198
229
···
220
251
}
221
252
}
222
253
223
-
function frame() {
254
+
function frame(): FrameLocator {
255
+
if (!frameRef) throw new Error("frameRef not initialized");
224
256
return frameRef;
225
257
}
226
258
227
-
async function waitFor(predicate, options = {}) {
228
-
const timeout = options.timeout || 10000;
259
+
async function waitFor(predicate: () => boolean, options: WaitForOptions = {}): Promise<void> {
260
+
if (!frameRef || !pageRef) throw new Error("refs not initialized");
261
+
const timeout = options.timeout ?? 10000;
229
262
const interval = 50;
230
263
const start = Date.now();
231
264
while (Date.now() - start < timeout) {
+5
-2
tests/kitchensink.spec.js
tests/kitchensink.spec.ts
+5
-2
tests/kitchensink.spec.js
tests/kitchensink.spec.ts
···
1
1
import { test, expect, beforeAll, afterAll, afterEach } from "vitest";
2
-
import { createHelpers, launchBrowser } from "./helpers.js";
2
+
import { createHelpers, launchBrowser, type TestHelpers } from "./helpers.ts";
3
+
import type { Browser, Page } from "playwright";
3
4
4
-
let browser, page, h;
5
+
let browser: Browser;
6
+
let page: Page;
7
+
let h: TestHelpers;
5
8
6
9
beforeAll(async () => {
7
10
browser = await launchBrowser();
+5
-2
tests/pagination.spec.js
tests/pagination.spec.ts
+5
-2
tests/pagination.spec.js
tests/pagination.spec.ts
···
1
1
import { test, expect, beforeAll, afterAll, afterEach } from "vitest";
2
-
import { createHelpers, launchBrowser } from "./helpers.js";
2
+
import { createHelpers, launchBrowser, type TestHelpers } from "./helpers.ts";
3
+
import type { Browser, Page } from "playwright";
3
4
4
-
let browser, page, h;
5
+
let browser: Browser;
6
+
let page: Page;
7
+
let h: TestHelpers;
5
8
6
9
beforeAll(async () => {
7
10
browser = await launchBrowser();
+5
-2
tests/refresh.spec.js
tests/refresh.spec.ts
+5
-2
tests/refresh.spec.js
tests/refresh.spec.ts
···
1
1
import { test, expect, beforeAll, afterAll, afterEach } from "vitest";
2
-
import { createHelpers, launchBrowser } from "./helpers.js";
2
+
import { createHelpers, launchBrowser, type TestHelpers } from "./helpers.ts";
3
+
import type { Browser, Page } from "playwright";
3
4
4
-
let browser, page, h;
5
+
let browser: Browser;
6
+
let page: Page;
7
+
let h: TestHelpers;
5
8
6
9
beforeAll(async () => {
7
10
browser = await launchBrowser();
+26
tsconfig.json
+26
tsconfig.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"target": "ES2022",
4
+
"lib": ["ES2022", "DOM", "DOM.Iterable", "WebWorker"],
5
+
"module": "ESNext",
6
+
"moduleResolution": "bundler",
7
+
"resolveJsonModule": true,
8
+
"isolatedModules": true,
9
+
"noEmit": true,
10
+
"jsx": "react-jsx",
11
+
"jsxImportSource": "react",
12
+
"strict": true,
13
+
"noUnusedLocals": true,
14
+
"noUnusedParameters": true,
15
+
"noFallthroughCasesInSwitch": true,
16
+
"exactOptionalPropertyTypes": true,
17
+
"noUncheckedIndexedAccess": true,
18
+
"skipLibCheck": true,
19
+
"allowImportingTsExtensions": true,
20
+
"esModuleInterop": true,
21
+
"allowSyntheticDefaultImports": true,
22
+
"types": ["node", "vite/client"]
23
+
},
24
+
"include": ["src/**/*", "tests/**/*", "types/**/*", "vite.config.ts", "vitest.config.ts"],
25
+
"exclude": ["node_modules", "dist"]
26
+
}
+60
types/global.d.ts
+60
types/global.d.ts
···
1
+
// Global type declarations
2
+
3
+
// Rolldown worker import pattern
4
+
declare module "*?rolldown-worker" {
5
+
const workerUrl: string;
6
+
export default workerUrl;
7
+
}
8
+
9
+
// Allow importing JSON modules
10
+
declare module "*.json" {
11
+
const value: unknown;
12
+
export default value;
13
+
}
14
+
15
+
// Webpack shim globals for client-side (window context)
16
+
// Note: interface required here for declaration merging with global Window
17
+
interface Window {
18
+
__webpack_module_cache__: Record<string, { exports: unknown }>;
19
+
__webpack_modules__: Record<string, (module: { exports: unknown }) => void>;
20
+
__webpack_require__: WebpackRequire;
21
+
__webpack_chunk_load__: (chunkId: string) => Promise<void>;
22
+
}
23
+
24
+
// Webpack shim globals for worker context (self/globalThis)
25
+
declare const __webpack_module_cache__: Record<string, { exports: unknown }>;
26
+
27
+
type WebpackRequire = {
28
+
(moduleId: string): unknown;
29
+
m: Record<string, (module: { exports: unknown }) => void>;
30
+
c: Record<string, { exports: unknown } | unknown>;
31
+
d: (exports: object, definition: Record<string, () => unknown>) => void;
32
+
r: (exports: object) => void;
33
+
o: (obj: object, prop: string) => boolean;
34
+
e: (chunkId: string) => Promise<void>;
35
+
p: string;
36
+
};
37
+
38
+
// Worker global context extensions
39
+
type WorkerGlobalScope = {
40
+
__webpack_require__: WebpackRequire;
41
+
__webpack_chunk_load__: (chunkId: string) => Promise<void>;
42
+
};
43
+
44
+
// Extend globalThis for worker context
45
+
declare namespace globalThis {
46
+
let ReadableStream: typeof globalThis.ReadableStream;
47
+
let ReadableByteStreamController: typeof globalThis.ReadableByteStreamController;
48
+
}
49
+
50
+
// Vite environment
51
+
type ImportMeta = {
52
+
readonly env: {
53
+
readonly PROD: boolean;
54
+
readonly DEV: boolean;
55
+
readonly MODE: string;
56
+
};
57
+
readonly hot?: {
58
+
accept: (callback?: () => void) => void;
59
+
};
60
+
};
+27
types/react-internals.d.ts
+27
types/react-internals.d.ts
···
1
+
// Internal React types adapted from React source (Flow -> TypeScript)
2
+
3
+
import type { ReactNode, ReactElement as ReactElementPublic } from "react";
4
+
5
+
export type ReactKey = string | null;
6
+
7
+
/** Internal React element structure with $$typeof */
8
+
export interface ReactElementInternal {
9
+
$$typeof: symbol;
10
+
type: unknown;
11
+
key: ReactKey;
12
+
ref: unknown;
13
+
props: Record<string, unknown>;
14
+
}
15
+
16
+
/** Lazy element type */
17
+
export interface ReactLazy<T = unknown> {
18
+
$$typeof: symbol;
19
+
_payload: unknown;
20
+
_init: (payload: unknown) => T;
21
+
}
22
+
23
+
/** Check if value is a React element (internal) */
24
+
export function isReactElement(value: unknown): value is ReactElementInternal;
25
+
26
+
// Re-export React types we use
27
+
export type { ReactNode, ReactElementPublic };
+135
types/react-server-dom-webpack.d.ts
+135
types/react-server-dom-webpack.d.ts
···
1
+
// Type declarations for react-server-dom-webpack
2
+
// Based on Flow types from React source
3
+
4
+
declare module "react-server-dom-webpack/server" {
5
+
import type { Thenable, ReactNode } from "react";
6
+
7
+
export type TemporaryReferenceSet = Set<unknown>;
8
+
9
+
export type ClientManifest = {
10
+
[moduleId: string]: {
11
+
id: string;
12
+
chunks: string[];
13
+
name: string;
14
+
};
15
+
};
16
+
17
+
export type ServerManifest = {
18
+
[id: string]: {
19
+
id: string;
20
+
chunks: string[];
21
+
name: string;
22
+
};
23
+
};
24
+
25
+
export type RenderOptions = {
26
+
debugChannel?: { readable?: ReadableStream; writable?: WritableStream };
27
+
environmentName?: string | (() => string);
28
+
filterStackFrame?: (url: string, functionName: string) => boolean;
29
+
identifierPrefix?: string;
30
+
signal?: AbortSignal;
31
+
temporaryReferences?: TemporaryReferenceSet;
32
+
onError?: (error: unknown) => void;
33
+
};
34
+
35
+
export type StaticResult = {
36
+
prelude: ReadableStream;
37
+
};
38
+
39
+
export function renderToReadableStream(
40
+
model: ReactNode,
41
+
webpackMap: ClientManifest,
42
+
options?: RenderOptions,
43
+
): ReadableStream;
44
+
45
+
export function prerender(
46
+
model: ReactNode,
47
+
webpackMap: ClientManifest,
48
+
options?: RenderOptions,
49
+
): Promise<StaticResult>;
50
+
51
+
export function decodeReply<T = unknown>(
52
+
body: string | FormData,
53
+
webpackMap: ServerManifest,
54
+
options?: { temporaryReferences?: TemporaryReferenceSet },
55
+
): Thenable<T>;
56
+
57
+
export function decodeAction<T = unknown>(
58
+
body: FormData,
59
+
serverManifest: ServerManifest,
60
+
): Promise<() => T> | null;
61
+
62
+
export function decodeFormState<S>(
63
+
actionResult: S,
64
+
body: FormData,
65
+
serverManifest: ServerManifest,
66
+
): Promise<unknown>;
67
+
68
+
export function registerServerReference<T extends Function>(
69
+
reference: T,
70
+
id: string,
71
+
exportName: string | null,
72
+
): T;
73
+
74
+
export function registerClientReference<T>(
75
+
proxyImplementation: T,
76
+
id: string,
77
+
exportName: string,
78
+
): T;
79
+
80
+
export function createClientModuleProxy<T = Record<string, unknown>>(moduleId: string): T;
81
+
82
+
export function createTemporaryReferenceSet(): TemporaryReferenceSet;
83
+
}
84
+
85
+
declare module "react-server-dom-webpack/client" {
86
+
import type { Thenable } from "react";
87
+
88
+
export type TemporaryReferenceSet = Set<unknown>;
89
+
90
+
export type CallServerCallback = (id: string, args: unknown[]) => Promise<unknown>;
91
+
92
+
export type FindSourceMapURLCallback = (
93
+
fileName: string,
94
+
environmentName: string,
95
+
) => string | null | undefined;
96
+
97
+
export type Options = {
98
+
callServer?: CallServerCallback;
99
+
debugChannel?: { writable?: WritableStream; readable?: ReadableStream };
100
+
temporaryReferences?: TemporaryReferenceSet;
101
+
findSourceMapURL?: FindSourceMapURLCallback;
102
+
replayConsoleLogs?: boolean;
103
+
environmentName?: string;
104
+
startTime?: number;
105
+
endTime?: number;
106
+
};
107
+
108
+
export function createFromReadableStream<T = unknown>(
109
+
stream: ReadableStream,
110
+
options?: Options,
111
+
): Thenable<T>;
112
+
113
+
export function createFromFetch<T = unknown>(
114
+
promiseForResponse: Promise<Response>,
115
+
options?: Options,
116
+
): Thenable<T>;
117
+
118
+
export function encodeReply(
119
+
value: unknown,
120
+
options?: { temporaryReferences?: TemporaryReferenceSet; signal?: AbortSignal },
121
+
): Promise<string | FormData>;
122
+
123
+
export function createServerReference<T extends Function>(
124
+
id: string,
125
+
callServer: CallServerCallback,
126
+
): T;
127
+
128
+
export function registerServerReference<T extends Function>(
129
+
reference: T,
130
+
id: string,
131
+
exportName: string | null,
132
+
): T;
133
+
134
+
export function createTemporaryReferenceSet(): TemporaryReferenceSet;
135
+
}
+19
types/web-streams-polyfill.d.ts
+19
types/web-streams-polyfill.d.ts
···
1
+
// Type declarations for web-streams-polyfill specific exports
2
+
3
+
declare module "web-streams-polyfill" {
4
+
export const ReadableStream: {
5
+
new <R = unknown>(
6
+
underlyingSource?: UnderlyingSource<R>,
7
+
strategy?: QueuingStrategy<R>,
8
+
): ReadableStream<R>;
9
+
prototype: ReadableStream;
10
+
};
11
+
// ReadableByteStreamController is an internal class, we type it loosely
12
+
export const ReadableByteStreamController: unknown;
13
+
export const WritableStream: typeof globalThis.WritableStream;
14
+
export const TransformStream: typeof globalThis.TransformStream;
15
+
}
16
+
17
+
declare module "web-streams-polyfill/polyfill" {
18
+
// Side-effect only import that polyfills globals
19
+
}
+2
-2
vite.config.js
+2
-2
vite.config.js
···
60
60
configureServer(server) {
61
61
server.middlewares.use((req, res, next) => {
62
62
if (req.url === "/embed.js") {
63
-
req.url = "/src/embed.js";
63
+
req.url = "/src/embed.ts";
64
64
}
65
65
next();
66
66
});
···
85
85
input: {
86
86
main: resolve(__dirname, "index.html"),
87
87
embed: resolve(__dirname, "embed.html"),
88
-
"embed-js": resolve(__dirname, "src/embed.js"),
88
+
"embed-js": resolve(__dirname, "src/embed.ts"),
89
89
},
90
90
output: {
91
91
entryFileNames: (chunkInfo) => {