diff --git a/.changeset/react-router-8-default-template.md b/.changeset/react-router-8-default-template.md new file mode 100644 index 00000000..03e007b2 --- /dev/null +++ b/.changeset/react-router-8-default-template.md @@ -0,0 +1,10 @@ +--- +'rsbuild-plugin-react-router': minor +--- + +Add React Router 8 compatibility while preserving React Router 7 behavior. +The plugin now supports stable React Router 8 config fields, resolves +prerender data requests for the installed React Router major version, supports +React Router RSC mode, analyzes transformed MDX route modules for manifest +generation, and includes React Router 8/RSC examples plus framework integration +coverage. diff --git a/.changeset/stream-server-first-route-css.md b/.changeset/stream-server-first-route-css.md new file mode 100644 index 00000000..4d88ed65 --- /dev/null +++ b/.changeset/stream-server-first-route-css.md @@ -0,0 +1,11 @@ +--- +'rsbuild-plugin-react-router': patch +--- + +Stream server-first route CSS during RSC render. CSS imported in the RSC +layer never flows through the client manifest's ``, so it was +previously dropped. Modules exporting server components are now marked with +the `'use server-entry'` directive so rspack's RSC runtime records +`entryCssFiles`, which the server route entry wrapper streams as +precedence-tagged stylesheet links, fixing missing styles and +flash-of-unstyled-content for server-component routes. diff --git a/.codex/skills/react-router-upstream-tests/SKILL.md b/.codex/skills/react-router-upstream-tests/SKILL.md new file mode 100644 index 00000000..f3441319 --- /dev/null +++ b/.codex/skills/react-router-upstream-tests/SKILL.md @@ -0,0 +1,70 @@ +--- +name: react-router-upstream-tests +description: Audit upstream React Router framework test changes and manually select tests to adapt into this repository without overwriting the checked-in Rsbuild corpus. +--- + +# React Router Upstream Tests + +Use this skill when checking for new or changed upstream React Router framework +tests, reviewing the corpus checkpoint, or manually importing test coverage. + +## Source of truth + +- `tests/react-router-framework/UPSTREAM.json` records the upstream repository, + last reviewed commit, review time, and relevant source directories. +- `tests/react-router-framework/` is repository-owned and authoritative. It is + intentionally adapted for Rsbuild and must never be overwritten wholesale. +- `scripts/check-react-router-framework-upstream.mjs` is a read-only audit. It + reports upstream changes but never copies files or updates the checkpoint. + +## Workflow + +1. Inspect the current checkpoint and confirm the local React Router clone: + + ```sh + cat tests/react-router-framework/UPSTREAM.json + git -C /path/to/react-router status --short --branch + ``` + +2. Compare the reviewed commit with the desired upstream target: + + ```sh + pnpm check:react-router-framework-upstream -- \ + --source=/path/to/react-router \ + --target=HEAD + ``` + +3. Review every reported directory containing added files and every added, + modified, deleted, or renamed file. Select only tests that add relevant + compatibility coverage. + +4. Copy selected tests manually and adapt them to the existing Rsbuild harness: + + - use `rsbuild.config.ts`, not Vite configuration; + - reuse helpers under `tests/react-router-framework/integration/helpers`; + - preserve intentional Rsbuild/Rspack divergences; + - do not add inert Vite dependencies, fixtures, or skipped suites; + - keep resource-guard and process-cleanup behavior intact. + +5. Run the narrow imported tests first, then the relevant framework gate: + + ```sh + pnpm build + RR_FRAMEWORK_MAX_WORKERS=2 pnpm exec playwright test \ + --config tests/react-router-framework/integration/playwright.config.ts \ + path/to/imported-test.ts --workers=2 --max-failures=1 --retries=0 + pnpm test:react-router-framework:failfast + ``` + +6. After review and validation, update `lastReviewedRef` and `reviewedAt` in + `UPSTREAM.json` in the same commit as the selected test adaptations. + +## Guardrails + +- Never add an automatic sync, overlay restoration, or corpus rewrite step. +- Never update the checkpoint merely because the audit command ran. +- Never claim the corpus matches upstream; record only the revision reviewed. +- Do not copy upstream package manifests without adapting workspace/catalog + dependencies to this repository deliberately. +- Keep the working tree reviewable and commit only selected tests, adaptations, + checkpoint metadata, and directly related documentation. diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index a05265c7..ccac6693 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -27,11 +27,13 @@ jobs: profile: ci-small build_order: base,head dev_order: head,base + parallel_route_transform: auto - shard: large label: large fixtures profile: ci-large build_order: head,base dev_order: base,head + parallel_route_transform: 1 env: BENCHMARK_PROFILE: ${{ matrix.profile }} @@ -41,6 +43,7 @@ jobs: BENCHMARK_ITERATIONS: '5' BENCHMARK_WARMUP: '1' BENCHMARK_DEV_ROUTES: auto + BENCHMARK_PARALLEL_ROUTE_TRANSFORM: ${{ matrix.parallel_route_transform }} steps: - name: Checkout PR head @@ -120,6 +123,7 @@ jobs: --iterations "$BENCHMARK_BUILD_ITERATIONS" \ --large-iterations "$BENCHMARK_LARGE_ITERATIONS" \ --warmup "$BENCHMARK_WARMUP" \ + --parallel-route-transform "$BENCHMARK_PARALLEL_ROUTE_TRANSFORM" \ --clean build \ --format both \ --log-performance \ @@ -150,6 +154,7 @@ jobs: --large-iterations "$BENCHMARK_LARGE_ITERATIONS" \ --warmup "$BENCHMARK_WARMUP" \ --dev-routes "$BENCHMARK_DEV_ROUTES" \ + --parallel-route-transform "$BENCHMARK_PARALLEL_ROUTE_TRANSFORM" \ --clean build \ --format both \ --log-performance \ diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 3498a655..bf20c0bd 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -6,6 +6,9 @@ on: pull_request: branches: [main] +permissions: + contents: read + jobs: build-test: runs-on: ubuntu-latest @@ -52,6 +55,13 @@ jobs: - name: Install Playwright browsers run: pnpm exec playwright install --with-deps chromium + env: + PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/node_modules/.cache/ms-playwright + + - name: Run React Router ecosystem tests + run: pnpm test:react-router-framework:failfast:no-build - name: Run E2E tests - run: pnpm e2e + run: pnpm e2e:no-build + env: + PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/node_modules/.cache/ms-playwright diff --git a/.gitignore b/.gitignore index a029637d..8617c12b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ tests/react-router-framework/.tmp/ playwright-report/ test-results/ -# Nested Playwright artifacts (examples, fixtures) +# Playwright fixture scratch and reports (framework test corpus) +tests/react-router-framework/.tmp/ +tests/.tmp-dev-runtime/ **/test-results/ **/playwright-report/ diff --git a/CHANGELOG.md b/CHANGELOG.md index a2b8b261..38cbde8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,7 +91,7 @@ ### Minor Changes -- 3c6d368: Bring Rsbuild plugin behavior closer to React Router's official Vite plugin. +- 3c6d368: Bring Rsbuild plugin behavior closer to React Router framework behavior. - Add React Router config resolution + validations/warnings for closer framework parity - Add split route modules (route chunk entrypoints) including enforce mode validation - Improve `.client` module stubbing on the server (including `export *` re-exports) diff --git a/README.md b/README.md index 51f43430..cb90e84b 100644 --- a/README.md +++ b/README.md @@ -111,10 +111,11 @@ export default { ``` Use `ReactRouterRsbuildConfig` for Rsbuild projects so plugin-supported -configuration such as `splitRouteModules` stays typed. The underlying route -and config types come from `@react-router/dev`, which framework-mode apps -already install for `routes.ts` helpers and typegen; it is declared as an -optional peer dependency. +configuration such as `splitRouteModules` stays typed across React Router 7 +and 8 releases. The underlying route and config types come from +`@react-router/dev`, which framework-mode apps already install for +`routes.ts` helpers and typegen; it is declared as an optional peer +dependency. Commonly used options: @@ -168,7 +169,7 @@ a better fit. ### SPA Mode (`ssr: false`) -React Router's SPA Mode still requires a build-time server render of the root route to generate a hydratable `index.html` (this is how the official React Router Vite plugin works). +React Router's SPA Mode still requires a build-time server render of the root route to generate a hydratable `index.html` (this is how this Rsbuild integration mirrors React Router framework behavior). When `ssr: false`: @@ -688,14 +689,13 @@ inconclusive comparison before treating it as a performance result. ## React Router Framework Mode React Router "Framework Mode" wraps Data Mode using a Vite plugin. This Rsbuild -plugin aims to match the important behaviors without depending on Vite: +plugin aims to match the important framework behaviors on Rsbuild: - Typegen + Route Module API types (`./+types/*`) - Route module splitting (`splitRouteModules`) - SPA mode (`ssr: false`), SSR mode, and static prerendering (`prerender`) -Some Vite-specific integrations (for example Vite's environment API + critical -CSS endpoint) are not supported 1:1. +Some upstream framework integrations are not supported 1:1. ## Examples @@ -709,6 +709,7 @@ The repository includes several examples demonstrating different use cases: | [custom-node-server](./examples/custom-node-server) | Custom Express server with SSR | 3003 | `pnpm dev` | | [cloudflare](./examples/cloudflare) | Cloudflare Workers deployment | 3004 | `pnpm dev` | | [client-only](./examples/client-only) | `.client` modules with SSR hydration | 3010 | `pnpm dev` | +| [react-router-8](./examples/react-router-8) | React Router 8 framework-mode SSR | 3020 | `pnpm dev` | | [epic-stack](./examples/epic-stack) | Full-featured Epic Stack example | 3005 | `pnpm dev` | | [federation/epic-stack](./examples/federation/epic-stack) | Module Federation host | 3006 | `pnpm dev` | | [federation/epic-stack-remote](./examples/federation/epic-stack-remote) | Module Federation remote | 3007 | `pnpm dev` | diff --git a/benchmarks/synthetic-web-bundler-benchmark/app/root.tsx b/benchmarks/synthetic-web-bundler-benchmark/app/root.tsx index 740d5a4c..b56ca0eb 100644 --- a/benchmarks/synthetic-web-bundler-benchmark/app/root.tsx +++ b/benchmarks/synthetic-web-bundler-benchmark/app/root.tsx @@ -1,24 +1,18 @@ -import type { LinksFunction, MetaFunction } from "react-router"; -import { - Links, - Meta, - Outlet, - Scripts, - ScrollRestoration, -} from "react-router"; -import { IntlProvider } from "react-intl"; -import globalStyles from "./styles/global.css?url"; +import type { LinksFunction, MetaFunction } from 'react-router'; +import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router'; +import { IntlProvider } from 'react-intl'; +import globalStyles from './styles/global.css?url'; export const meta: MetaFunction = () => [ - { title: "Synthetic Web Bundler Benchmark" }, + { title: 'Synthetic Web Bundler Benchmark' }, { - name: "description", - content: "A deterministic, non-product Vite and Rsbuild benchmark.", + name: 'description', + content: 'A deterministic, non-product Vite and Rsbuild benchmark.', }, ]; export const links: LinksFunction = () => [ - { rel: "stylesheet", href: globalStyles }, + { rel: 'stylesheet', href: globalStyles }, ]; export function Layout({ children }: { children: React.ReactNode }) { diff --git a/docs/coherent-dev-generations.md b/docs/coherent-dev-generations.md deleted file mode 100644 index 5acc592c..00000000 --- a/docs/coherent-dev-generations.md +++ /dev/null @@ -1,70 +0,0 @@ -# Coherent React Router development generations - -The plugin builds React Router applications with separate `web` and `node` -compilers. During development, those compilers can finish at different times. -Publishing each result independently can pair a new browser manifest with an -older server entry object. - -## Contract - -The plugin exposes only a committed React Router development generation: - -- every configured React Router server entry was evaluated successfully; -- each entry's embedded manifest came from the selected web compilation; -- failed, incomplete, and superseded candidates cannot replace the last-good - generation; and -- built-in middleware and `loadReactRouterServerBuild(devServer)` read the same - committed generation. - -Requests capture one committed server entry object for their lifetime. Calling -`loadReactRouterServerBuild(devServer, entryName)` selects a configured server -bundle by its exact Rsbuild entry name; omitting `entryName` selects the full -default build. All entries switch generations together. The public helper is -the supported build provider for custom development servers; calling -`devServer.environments.node.loadBundle()` directly bypasses this contract. - -## Lifecycle model - -A candidate records the exact web compilation used to produce its manifests and -the corresponding evaluated node builds. It becomes visible only after -Rsbuild's aggregate development callback supplies a complete, error-free pair. -One-sided callbacks are accepted only when their known changed files do not -intersect the unchanged compiler's dependencies. - -Each node compilation also records the latest completed web compilation when it -starts. If rapid edits produce a callback containing a node result paired with -a different web compilation, that mixed candidate is discarded. Fatal compiler -failures reject initial waiters promptly; later failures preserve last-good -output. - -The committed generation remains available while a later candidate builds. -Initial compilation failures are reported to requests; failures after a commit -leave the last-good generation available. Starting a replacement dev server -creates a new lifecycle session so callbacks from the old session cannot -publish into the new one. - -Programmatic replacement requires callers to await the active server's -`close()` before calling `createDevServer()` again. The plugin rejects an -overlapping or out-of-order replacement rather than closing one server from -inside another server's global startup-hook transaction. Callers must serialize -`createDevServer()` calls; concurrent server creation is outside this contract. -If startup fails before returning a server, or if closing the active server -rejects, restart the process before retrying unless the caller can externally -prove and force complete teardown. A fresh Rsbuild instance alone is not -sufficient because the prior compiler or watchers may still be active. - -## Deliberate limit - -This is an eagerly evaluated server-entry-set and manifest-pairing guarantee, not -byte-level output atomicity. Development outputs use stable paths and mutable -storage, so an old server-build object does not preserve older client assets or -server chunks that are imported lazily after entry evaluation. - -Rsbuild publishes compiler-derived WebSocket success before its supported -`onAfterDevCompile` plugin callback. The plugin therefore cannot promise that -browser success notification waits for framework publication. A supported -graph-settled, pre-success hook is required to close that gap. - -Strict old-or-new asset serving would additionally require immutable -generation filenames, staged output promotion, or request-pinned asset -snapshots with garbage collection. That is outside this contract. diff --git a/docs/superpowers/plans/2026-07-17-effect-server-architecture.md b/docs/superpowers/plans/2026-07-17-effect-server-architecture.md new file mode 100644 index 00000000..3e5f9023 --- /dev/null +++ b/docs/superpowers/plans/2026-07-17-effect-server-architecture.md @@ -0,0 +1,1232 @@ +# Effect Server Architecture Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make Effect the orchestration and lifecycle spine for Node-side plugin and server resources where it materially simplifies ownership and composition, while preserving build, dev-startup, worker, and runtime performance. + +**Architecture:** Create one `ManagedRuntime` per plugin instance. Rsbuild hooks and exported Promise APIs are runtime boundaries; effectful internal workflows compose as `Effect`, while synchronous callback-driven state transitions remain ordinary TypeScript. The runtime's native `Scope` owns dynamically created workers, watchers, delayed work, and background fibers. Pure transforms, generated/browser runtime code, and worker entrypoints remain ordinary TypeScript. + +**Tech Stack:** TypeScript 5.9, Effect 3.22, Rsbuild/Rspack, Rstest, Playwright, Node `worker_threads`, existing benchmark harness. + +## Global Constraints + +> **Execution refinement:** Early task examples name a custom `PluginScope` service and propose Effect-based dev-session state. The accepted implementation uses the runtime-provided native `Scope` plus a supervised `FiberSet`, and keeps Rsbuild's synchronous dev-session transitions plain. These choices remove a forwarding abstraction and avoid runtime crossings in synchronous callbacks while preserving the ownership model described here. + +- Scope is Node-side plugin/server code only. Do not add Effect to browser runtime, generated browser modules, templates shipped to applications, or worker entrypoints. +- `src/parallel-route-transform-worker.ts` and its transitive worker-only hot path remain Effect-free unless a separate benchmark-backed design is approved. +- Effect may own `RouteTransformExecutor` acquisition, supervision, and shutdown in the parent process; `postMessage`, worker task execution, serialization, and worker caches remain plain TypeScript. +- Use direct Effect subpath imports such as `effect/Effect` and `effect/ManagedRuntime`; do not import the `effect` barrel from production source. +- Create one `ManagedRuntime` per `pluginReactRouter(...).setup(api)` invocation. Do not create a runtime per hook, route, transform, request, compiler, or worker. +- Keep pure synchronous helpers pure. Do not wrap string transforms, AST transforms, manifest shaping, cache access, predicates, or object construction in `Effect.sync` unless they cross a lifecycle/error boundary. +- Use `Effect.fn` for reusable orchestration boundaries when the named span materially improves ownership or diagnostics. Do not add it to per-route, per-module, per-request, or other measured hot paths without benchmark evidence. +- Keep synchronous Rsbuild callback state machines in plain TypeScript when converting them would introduce repeated Promise/Effect crossings or runtime calls. Effect should remove manual asynchronous ownership, not wrap every mutation. +- Add tagged errors only where callers recover differently. Continue using `Error` for opaque third-party failures when a new error class would only add ceremony. +- Preserve public exports and Promise-returning APIs unless a separate breaking-change decision is approved. +- Production behavior and error messages remain unchanged unless a task explicitly names the intended change. +- Treat LOC as a diagnostic, not an acceptance gate. Prefer less production code when clarity is equal, but accept additional tests, benchmarks, or narrowly justified runtime code when the result is easier to understand and maintain. +- Performance acceptance: no benchmark median may regress by more than 2%; no p95/tail metric, dev readiness, HMR update, worker-enabled build, CPU time, or maximum RSS metric may regress by more than 5%. +- If a tranche misses the performance gate or adds abstraction without removing equivalent manual coordination, revert or redesign it before continuing. +- Keep commits tranche-local and independently green. Never mix browser-runtime changes, dependency upgrades, formatting sweeps, or unrelated refactors into this branch. + +--- + +## Target File Structure + +Keep the repository's flat `src/` layout. Do not create an `effect/` directory or split every service into its own file. + +- `src/effect-runtime.ts`: error normalization, foreign Promise constructors, native scoped resource/fiber ownership, and the single plugin `ManagedRuntime` factory. +- `src/index.ts`: constructs the plugin runtime, adapts Rsbuild hooks to `runtime.runPromise`, and disposes the runtime from close hooks. +- `src/dev-background-resources.ts`: scoped acquisition of route watchers, lazy-compilation prewarm, and the parent-side transform executor. +- `src/route-watch.ts`: Effect-native watcher lifecycle and serialized rescan workflow. +- `src/dev-runtime-session.ts`: synchronous session state and close observation required by Rsbuild's callback API. +- `src/dev-runtime-controller.ts`: compiler/runtime orchestration; asynchronous delayed work and plugin-owned cleanup use the plugin runtime where they simplify ownership. +- `src/dev-generation.ts`: readiness and generation state transitions using `Deferred` plus an Effect workflow boundary. +- `src/lazy-compilation-prewarm.ts`: interruptible prewarm scheduling owned by the plugin scope. +- `src/parallel-route-transforms.ts`: Promise-based worker dispatch remains; acquisition/close becomes a parent-side scoped resource. +- `src/parallel-route-transform-worker.ts`: unchanged Effect-free worker entrypoint. +- `src/parallel-route-transform-protocol.ts`: unchanged protocol unless benchmark instrumentation requires an additive ready/timing message; default plan does not add one. +- `src/{react-router-config,build-manifest,manifest,prerender-build,rsc-prerender,server-build-resolution,server-utils,dev-runtime-artifacts,typegen}.ts`: internal Effect workflows with Promise adapters only at exported/public edges. +- `tests/effect-runtime.test.ts`: runtime ownership, finalization, interruption, and idempotent disposal. +- Existing subsystem tests: behavior-preserving migration coverage; avoid a parallel Effect-only test suite. + +## Acceptance Measurements + +Record all artifacts under ignored `.benchmark/effect-server-architecture/`. + +```bash +git rev-parse HEAD > .benchmark/effect-server-architecture/base-commit.txt +git status --short > .benchmark/effect-server-architecture/base-status.txt +git ls-files -z '*.ts' '*.tsx' '*.js' '*.mjs' '*.mts' \ + | xargs -0 wc -l \ + > .benchmark/effect-server-architecture/base-total-loc.txt +find src -type f \( -name '*.ts' -o -name '*.tsx' \) -print0 \ + | xargs -0 wc -l \ + > .benchmark/effect-server-architecture/base-src-loc.txt +``` + +Benchmark commands used before the first code change and after every performance-sensitive tranche: + +```bash +pnpm build +node scripts/bench-builds.mts --profile=full --mode=build --iterations=5 --warmup=1 --clean=build --format=both --out=.benchmark/effect-server-architecture/base-full-build +node scripts/bench-builds.mts --profile=large --mode=dev --iterations=5 --warmup=1 --parallel-route-transform=false --format=both --out=.benchmark/effect-server-architecture/base-large-dev-inline +node scripts/bench-builds.mts --profile=large --mode=dev --iterations=5 --warmup=1 --parallel-route-transform=true --format=both --out=.benchmark/effect-server-architecture/base-large-dev-workers +node scripts/bench-builds.mts --profile=full --filter=synthetic-1024-ssr-esm --mode=build --iterations=5 --warmup=1 --parallel-route-transform=false --format=both --out=.benchmark/effect-server-architecture/base-1024-inline +node scripts/bench-builds.mts --profile=full --filter=synthetic-1024-ssr-esm --mode=build --iterations=5 --warmup=1 --parallel-route-transform=true --format=both --out=.benchmark/effect-server-architecture/base-1024-workers +``` + +The worker-enabled dev run is especially important: current code documents roughly 0.5 seconds of startup/IPC cost on constrained machines. The migration must not load Effect in each worker or increase that cost. + +--- + +### Task 1: Freeze Baselines and Add Architectural Boundary Tests + +**Files:** + +- Modify: `tests/effect-runtime.test.ts` +- Modify: `tests/parallel-route-transforms.test.ts` +- Test: `tests/effect-runtime.test.ts` +- Test: `tests/parallel-route-transforms.test.ts` + +**Interfaces:** + +- Consumes: current `runPluginEffect`, `tryPluginPromise`, `createDelayedPluginTask`, `createRouteTransformExecutor`. +- Produces: regression tests that constrain runtime disposal and prohibit Effect imports from the worker entrypoint. + +- [ ] **Step 1: Capture the clean baseline** + +Run the commands in `Acceptance Measurements` before modifying source. Confirm `base-status.txt` is empty and all five benchmark result sets exist. + +Expected: build succeeds; each benchmark directory contains `baseline.json` and `baseline.md`. + +- [ ] **Step 2: Add the worker-boundary test** + +Add this test to `tests/parallel-route-transforms.test.ts`: + +```ts +import { readFile } from 'node:fs/promises'; + +it('keeps the worker entrypoint Effect-free', async () => { + const source = await readFile( + new URL('../src/parallel-route-transform-worker.ts', import.meta.url), + 'utf8' + ); + + expect(source).not.toMatch(/from ['"]effect(?:\/|['"])/); + expect(source).not.toContain("import('effect"); +}); +``` + +This is intentionally a source-boundary test. Bundle verification in Task 9 checks the emitted worker chunk and its transitive imports. + +- [ ] **Step 3: Run the boundary test** + +Run: + +```bash +pnpm rstest run tests/parallel-route-transforms.test.ts +``` + +Expected: PASS. + +- [ ] **Step 4: Commit the guardrail** + +```bash +git add tests/parallel-route-transforms.test.ts +git commit -m "test: protect worker runtime boundary" +``` + +--- + +### Task 2: Introduce the Single Plugin Runtime and Runtime-Owned Scope + +**Files:** + +- Modify: `src/effect-runtime.ts` +- Modify: `tests/effect-runtime.test.ts` + +**Interfaces:** + +- Produces: `PluginScope`, `PluginEffectRuntime`, and `createPluginEffectRuntime()`. +- Preserves temporarily: `runPluginEffect`, `tryPluginPromise`, `createDelayedPluginTask` while callers migrate. +- Later tasks consume: `runtime.runPromise`, `runtime.runFork`, `runtime.dispose`, and `PluginScope.acquire`. + +- [ ] **Step 1: Write failing lifecycle tests** + +Add these tests to `tests/effect-runtime.test.ts`: + +```ts +import * as Effect from 'effect/Effect'; +import { createPluginEffectRuntime, PluginScope } from '../src/effect-runtime'; + +it('releases dynamically acquired resources when the runtime is disposed', async () => { + const events: string[] = []; + const runtime = createPluginEffectRuntime(); + + await runtime.runPromise( + Effect.gen(function* () { + const scope = yield* PluginScope; + return yield* scope.acquire( + Effect.sync(() => { + events.push('acquire'); + return 'resource'; + }), + resource => + Effect.sync(() => { + events.push(`release:${resource}`); + }) + ); + }) + ); + + await runtime.dispose(); + expect(events).toEqual(['acquire', 'release:resource']); +}); + +it('interrupts supervised fibers and disposes idempotently', async () => { + let finalized = 0; + const runtime = createPluginEffectRuntime(); + + runtime.runFork( + Effect.never.pipe( + Effect.ensuring( + Effect.sync(() => { + finalized += 1; + }) + ) + ) + ); + + await Promise.all([runtime.dispose(), runtime.dispose()]); + expect(finalized).toBe(1); +}); +``` + +- [ ] **Step 2: Verify the tests fail** + +```bash +pnpm rstest run tests/effect-runtime.test.ts +``` + +Expected: FAIL because `PluginScope` and `createPluginEffectRuntime` do not exist. + +- [ ] **Step 3: Implement the minimal runtime** + +Add direct subpath imports and this runtime model to `src/effect-runtime.ts`: + +```ts +import * as Context from 'effect/Context'; +import * as Layer from 'effect/Layer'; +import * as ManagedRuntime from 'effect/ManagedRuntime'; +import * as Scope from 'effect/Scope'; + +type Acquire = ( + acquire: Effect.Effect, + release: (resource: A) => Effect.Effect +) => Effect.Effect; + +export class PluginScope extends Context.Tag( + 'rsbuild-plugin-react-router/PluginScope' +)() {} + +const PluginScopeLive = Layer.scoped( + PluginScope, + Effect.gen(function* () { + const scope = yield* Effect.scope; + return { + acquire: (acquire, release) => + Effect.acquireRelease(acquire, release).pipe( + Effect.provideService(Scope.Scope, scope) + ), + }; + }) +); + +export const createPluginEffectRuntime = () => { + const runtime = ManagedRuntime.make(PluginScopeLive); + let disposePromise: Promise | undefined; + + return { + runPromise: runtime.runPromise, + runFork: runtime.runFork, + dispose: (): Promise => (disposePromise ??= runtime.dispose()), + }; +}; + +export type PluginEffectRuntime = ReturnType; +``` + +- [ ] **Step 4: Run the runtime tests** + +```bash +pnpm rstest run tests/effect-runtime.test.ts +pnpm typecheck +``` + +Expected: PASS. + +- [ ] **Step 5: Review tranche size and maintainability** + +```bash +git diff --numstat -- src/effect-runtime.ts tests/effect-runtime.test.ts +``` + +This foundation may be positive. Review Tasks 2–3 together and confirm the added runtime code replaces duplicated lifecycle ownership; LOC alone does not determine acceptance. + +- [ ] **Step 6: Commit the runtime foundation** + +```bash +git add src/effect-runtime.ts tests/effect-runtime.test.ts +git commit -m "refactor: add managed plugin effect runtime" +``` + +--- + +### Task 3: Make Rsbuild Hooks the Runtime Boundary + +**Files:** + +- Modify: `src/index.ts` +- Modify: `src/effect-runtime.ts` +- Modify: `tests/index.test.ts` +- Test: `tests/index.test.ts` +- Test: `tests/effect-runtime.test.ts` + +**Interfaces:** + +- Consumes: `createPluginEffectRuntime()`. +- Produces: exactly one runtime per plugin setup; idempotent disposal from `onCloseBuild`, `onCloseDevServer`, and `onExit`. +- Constraint: no `ManagedRuntime.make`, `Effect.runPromise`, or `Effect.runFork` outside `src/effect-runtime.ts`. + +- [ ] **Step 1: Add a setup/close regression test** + +Extend the existing fake Rsbuild API in `tests/index.test.ts` to capture `onCloseBuild`, `onCloseDevServer`, and `onExit`. Add a test with this behavior: + +```ts +it('shares one Effect runtime and closes it from every Rsbuild shutdown path', async () => { + const api = createFakeRsbuildApi(); + await pluginReactRouter().setup(api); + + await Promise.all([ + api.runHook('onCloseBuild'), + api.runHook('onCloseDevServer'), + api.runHook('onExit'), + ]); + + expect(api.errors).toEqual([]); +}); +``` + +Use the test file's existing fake API helpers and naming; do not add a second fake framework. + +- [ ] **Step 2: Create the runtime once in `setup(api)`** + +At the start of `pluginReactRouter(...).setup(api)`, add: + +```ts +const effectRuntime = createPluginEffectRuntime(); +const disposeEffectRuntime = (): Promise => effectRuntime.dispose(); + +api.onCloseBuild(disposeEffectRuntime); +api.onCloseDevServer(disposeEffectRuntime); +api.onExit(disposeEffectRuntime); +``` + +Register these hooks before any resource-producing hook. Because disposal is idempotent, multiple shutdown paths may race safely. + +- [ ] **Step 3: Replace hook-local `runPluginEffect` calls** + +Change the `onAfterBuild` callbacks at the current classic and RSC branches from: + +```ts +runPluginEffect(tryPluginPromise(() => runBuildWorkflow(...))) +``` + +to: + +```ts +effectRuntime.runPromise( + tryPluginPromise(() => runBuildWorkflow(...)) +) +``` + +Apply the same rule to every Effect launched directly by `src/index.ts`. Do not create nested runtimes. + +- [ ] **Step 4: Add an import-boundary assertion** + +Add this assertion to `tests/effect-runtime.test.ts` using `readFile` for the exact production files: + +```ts +it('centralizes raw Effect runners in effect-runtime.ts', async () => { + const files = ['index.ts', 'manifest.ts', 'route-watch.ts']; + const sources = await Promise.all( + files.map(file => + readFile(new URL(`../src/${file}`, import.meta.url), 'utf8') + ) + ); + + for (const source of sources) { + expect(source).not.toMatch(/Effect\.run(?:Promise|Fork|Sync)/); + expect(source).not.toContain('ManagedRuntime.make'); + } +}); +``` + +- [ ] **Step 5: Run focused tests and typecheck** + +```bash +pnpm rstest run tests/index.test.ts tests/effect-runtime.test.ts +pnpm typecheck +``` + +Expected: PASS. + +- [ ] **Step 6: Review the first runtime tranche** + +```bash +git diff HEAD~1 --numstat -- src/effect-runtime.ts src/index.ts +``` + +Tasks 2–3 together must centralize runtime creation, disposal, and failure normalization without adding a second ownership path. Use the line diff to identify accidental duplication, not as a hard gate. + +- [ ] **Step 7: Commit the boundary** + +```bash +git add src/index.ts src/effect-runtime.ts tests/index.test.ts tests/effect-runtime.test.ts +git commit -m "refactor: centralize plugin effect runtime boundary" +``` + +--- + +### Task 4: Move Parent-Side Worker Ownership into `PluginScope` + +**Files:** + +- Modify: `src/parallel-route-transforms.ts` +- Modify: `src/dev-background-resources.ts` +- Modify: `src/index.ts` +- Preserve unchanged: `src/parallel-route-transform-worker.ts` +- Preserve unchanged: `src/parallel-route-transform-protocol.ts` +- Modify: `tests/parallel-route-transforms.test.ts` +- Modify: `tests/index.test.ts` + +**Interfaces:** + +- Preserves: `RouteTransformExecutor.run(task): Promise`, `prewarm(): void`, `close(): Promise`. +- Produces: `acquireRouteTransformExecutor(options): Effect.Effect`. +- Worker boundary: no Effect import, runtime, fiber, `Deferred`, `Queue`, `Layer`, or schema decoding inside the worker. + +- [ ] **Step 1: Add a scoped executor test** + +In `tests/parallel-route-transforms.test.ts`, add: + +```ts +it('terminates parent-owned workers when the plugin runtime is disposed', async () => { + const worker = new FakeRouteTransformWorker(); + const runtime = createPluginEffectRuntime(); + + const executor = await runtime.runPromise( + acquireRouteTransformExecutor({ + createWorker: () => worker, + parallelRouteTransform: true, + }) + ); + + executor.prewarm(); + await runtime.dispose(); + + expect(worker.terminateCalls).toBe(1); +}); +``` + +- [ ] **Step 2: Add the parent-side acquisition function** + +In `src/parallel-route-transforms.ts`, keep the worker executor implementation Promise-based and add only the resource boundary: + +```ts +export const acquireRouteTransformExecutor = Effect.fn( + 'RouteTransformExecutor.acquire' +)(function* (options: RouteTransformExecutorOptions) { + const pluginScope = yield* PluginScope; + return yield* pluginScope.acquire( + Effect.sync(() => createRouteTransformExecutor(options)), + executor => + tryPluginPromise(() => executor.close()).pipe( + Effect.catchAll(() => Effect.void) + ) + ); +}); +``` + +Do not rewrite `ParallelRouteTransformExecutor.run`, `#createWorkerState`, pending task maps, message handlers, or worker code to Effect in this task. + +- [ ] **Step 3: Remove duplicate worker close registration** + +In `src/dev-background-resources.ts`, delete `closeRouteTransformExecutor`, remove it from `closeAll`, and remove `api.onCloseBuild(closeRouteTransformExecutor)`. The executor is now released by `PluginScope` when the runtime closes. + +Keep explicit dev-server cleanup only for resources not yet migrated in later tasks. + +- [ ] **Step 4: Run worker and index tests** + +```bash +pnpm rstest run tests/parallel-route-transforms.test.ts tests/index.test.ts +pnpm typecheck +``` + +Expected: PASS, including all existing lazy-worker, fallback, cache-affinity, idempotent-close, and in-flight rejection cases. + +- [ ] **Step 5: Verify worker output stays Effect-free** + +```bash +pnpm build +! rg -n "effect/(Effect|Fiber|Layer|ManagedRuntime|Scope)|from ['\"]effect['\"]" src/parallel-route-transform-worker.ts +! rg -n "effect/(Effect|Fiber|Layer|ManagedRuntime|Scope)" dist | rg "parallel-route-transform-worker" +``` + +Expected: both negated searches exit successfully with no matches. + +- [ ] **Step 6: Run worker-sensitive benchmarks** + +Repeat the `large-dev-workers`, `1024-inline`, and `1024-workers` commands with `after-task-4` output paths. Compare using: + +```bash +node scripts/compare-benchmarks.mts --before=.benchmark/effect-server-architecture/base-1024-workers/baseline.json --after=.benchmark/effect-server-architecture/after-task-4-1024-workers/baseline.json --benchmark=synthetic-1024-ssr-esm +``` + +Expected: worker-enabled production build remains faster than inline; all acceptance thresholds pass. + +- [ ] **Step 7: Review maintainability and commit** + +```bash +git diff --numstat -- src/parallel-route-transforms.ts src/dev-background-resources.ts src/index.ts +git add src/parallel-route-transforms.ts src/dev-background-resources.ts src/index.ts tests/parallel-route-transforms.test.ts tests/index.test.ts +git commit -m "refactor: scope parent route transform workers" +``` + +--- + +### Task 5: Replace Manual Dev Background Cleanup with Scoped Effects + +**Files:** + +- Modify: `src/dev-background-resources.ts` +- Modify: `src/route-watch.ts` +- Modify: `src/lazy-compilation-prewarm.ts` +- Modify: `src/effect-runtime.ts` +- Modify: `tests/route-watch.test.ts` +- Modify: `tests/lazy-compilation-prewarm.test.ts` +- Modify: `tests/effect-runtime.test.ts` + +**Interfaces:** + +- Produces: `acquireRouteTopologyWatcher(...)` and `acquireLazyCompilationPrewarm(...)` requiring `PluginScope`. +- Preserves: returned `setManifest(manifest)` controller method. +- Deletes: `closeAll`, `routeTopologyWatcherClosed`, `closeActiveRouteTopologyWatcher`, manual cancel/close aggregation, and duplicate watcher-close loops where interruption/finalizers make them unnecessary. + +- [ ] **Step 1: Strengthen the existing cancellation and finalization tests** + +In `tests/route-watch.test.ts`, extend `does not recreate watchers or touch the marker after close`. After its first `await closePromise`, call the returned close function again and tighten the existing assertion: + +```ts +await close(); +expect(closeWatcher).toHaveBeenCalledTimes(1); +``` + +In `tests/lazy-compilation-prewarm.test.ts`, migrate `reschedules in-flight prewarm work with the latest manifest` to acquire the controller from `createPluginEffectRuntime()`. Replace its finalizer: + +```ts +await runPluginEffect(controller.cancelEffect()); +``` + +with: + +```ts +await runtime.dispose(); +``` + +Add this test beside it to prove a pending delay is interrupted: + +```ts +it('does not fetch after runtime disposal cancels a scheduled prewarm', async () => { + const config = normalizeLazyCompilationPrewarmOptions(true); + if (!config) throw new Error('Expected prewarm config.'); + const runtime = createPluginEffectRuntime(); + const fetchSpy = rstest.spyOn(globalThis, 'fetch'); + const controller = await runtime.runPromise( + acquireLazyCompilationPrewarm({ + config, + onError: error => { + throw error; + }, + }) + ); + + controller.setServerOrigin('http://localhost:3000'); + controller.setManifest(createManifest()); + controller.schedule(); + await runtime.dispose(); + + expect(fetchSpy).not.toHaveBeenCalled(); + fetchSpy.mockRestore(); +}); +``` + +- [ ] **Step 2: Convert watcher creation to a scoped acquisition** + +Express watcher ownership as one acquisition and one release: + +```ts +export const acquireRouteTopologyWatcher = Effect.fn( + 'RouteTopologyWatcher.acquire' +)(function* (options: CreateRouteTopologyWatcherOptions) { + const pluginScope = yield* PluginScope; + return yield* pluginScope.acquire( + tryPluginPromise(() => createRouteTopologyWatcher(options)), + close => + tryPluginPromise(close).pipe( + Effect.catchAll(error => Effect.sync(() => options.onError(error))) + ) + ); +}); +``` + +Then simplify `createRouteTopologyWatcher` itself: retain its serialized `rescanQueue` semantics until tests demonstrate `Semaphore.withPermits(1)` or a single consumer fiber can replace it with fewer lines. + +- [ ] **Step 3: Replace delayed-task state with an interruptible fiber** + +In `src/effect-runtime.ts`, replace `createDelayedPluginTask`'s `version`, `activeFiber`, Promise chaining, and explicit cancellation effect with a runtime-scoped controller: + +```ts +export type DelayedPluginTask = { + readonly schedule: () => void; + readonly reschedule: () => void; + readonly cancel: () => Promise; +}; + +export const createDelayedPluginTask = ({ + runtime, + delayMs, + run, + onError, +}: { + runtime: PluginEffectRuntime; + delayMs: number; + run: () => Effect.Effect; + onError: (error: Error) => void; +}): DelayedPluginTask => { + let fiber: ReturnType | undefined; + + const cancel = async (): Promise => { + const active = fiber; + fiber = undefined; + if (active) await runtime.runPromise(Fiber.interrupt(active)); + }; + + const start = (): void => { + fiber = runtime.runFork( + Effect.sleep(Duration.millis(delayMs)).pipe( + Effect.zipRight(Effect.suspend(run)), + Effect.catchAll(error => Effect.sync(() => onError(error))), + Effect.ensuring(Effect.sync(() => (fiber = undefined))) + ) + ); + }; + + return { + schedule: () => { + if (!fiber) start(); + }, + reschedule: () => void cancel().then(start, onError), + cancel, + }; +}; +``` + +Run the existing reschedule race test immediately after this edit. If it fails, discard this controller implementation and implement a single `Ref>` controller instead; the accepted implementation must use either fiber interruption or the old version protocol, never both. + +- [ ] **Step 4: Remove background close aggregation** + +Refactor `registerReactRouterDevBackgroundResources` so it acquires watcher/prewarm/executor resources through `PluginScope`, registers Rsbuild start/compile hooks, and returns only: + +```ts +return { + setManifest(manifest) { + lazyCompilationPrewarmController?.setManifest(manifest); + }, +}; +``` + +Delete `closeAll`, all local close functions, `api.onCloseDevServer` cleanup from this module, and `api.onCloseBuild` cleanup. Runtime disposal is the single owner. + +- [ ] **Step 5: Run affected tests** + +```bash +pnpm rstest run tests/effect-runtime.test.ts tests/route-watch.test.ts tests/lazy-compilation-prewarm.test.ts tests/index.test.ts +pnpm typecheck +``` + +Expected: PASS. Explicitly verify double close, close during rescan, failed watcher startup, reschedule, and callback-triggered close cases. + +- [ ] **Step 6: Run dev startup/HMR benchmarks and review maintainability** + +Repeat both large dev benchmark commands with `after-task-5` output paths. Reject the tranche if readiness or update time exceeds the global thresholds. + +```bash +git diff --numstat -- src/effect-runtime.ts src/dev-background-resources.ts src/route-watch.ts src/lazy-compilation-prewarm.ts +``` + +Expected: production deletions exceed additions across the four files. + +- [ ] **Step 7: Commit** + +```bash +git add src/effect-runtime.ts src/dev-background-resources.ts src/route-watch.ts src/lazy-compilation-prewarm.ts tests/effect-runtime.test.ts tests/route-watch.test.ts tests/lazy-compilation-prewarm.test.ts tests/index.test.ts +git commit -m "refactor: scope dev background resources" +``` + +--- + +### Task 6: Collapse Dev Session, Controller, and Generation State Machines + +**Files:** + +- Modify: `src/dev-runtime-session.ts` +- Modify: `src/dev-runtime-controller.ts` +- Modify: `src/dev-generation.ts` +- Modify: `src/dev-runtime-compilation.ts` +- Modify: `src/dev-runtime-artifacts.ts` +- Modify: `tests/dev-runtime-controller.test.ts` +- Modify: `tests/dev-generation.test.ts` +- Modify: `tests/dev-generation-multi-entry.test.ts` +- Modify: `tests/dev-runtime.integration.test.ts` + +**Interfaces:** + +- Preserves: `ReactRouterDevRuntimeController`, `RuntimeBinding`, `createReactRouterDevRuntime`, `loadReactRouterServerBuild` public behavior. +- Produces: Effect-native `open`, `finishAttempt`, `close`, and readiness workflows. +- Deletes: Promise observation objects, duplicate close outcome unions, timer handles, runner calls inside state transitions, and callback error normalization duplicated from `effect-runtime.ts`. + +- [ ] **Step 1: Freeze the existing state-transition coverage before refactoring** + +Run the existing public-behavior tests that cover the three state families: + +```bash +pnpm rstest run tests/dev-runtime-controller.test.ts -t "rejects a fatal child failure and recovers on the next compile" +pnpm rstest run tests/dev-runtime-controller.test.ts -t "requires the active server to close before replacement" +pnpm rstest run tests/dev-runtime-controller.test.ts -t "observes one close promise and rejects replacement until it settles" +pnpm rstest run tests/dev-runtime-controller.test.ts -t "publishes a same-attempt rebuild when node starts before web completes" +``` + +Expected: PASS. These exercise failure recovery, active-to-closing exclusion, close-promise observation, and ready-to-rebuilding-to-ready behavior through the public controller harness. + +- [ ] **Step 2: Make session operations return Effect** + +Replace Promise-producing session internals with: + +```ts +export type DevRuntimeSessionManager = { + readonly open: Effect.Effect; + readonly close: Effect.Effect; +}; +``` + +Use one `Deferred` for close completion and one `Ref` for session state. Callers that still require Promises adapt through the plugin runtime at the Rsbuild/API edge. + +- [ ] **Step 3: Replace CSS reload timers with fibers** + +In `createReactRouterDevRuntimeController`, replace `scheduledCssAssetOwnershipReload`, `setTimeout`, and both `clearTimeout` branches with a delayed fiber supervised by the plugin runtime. Interruption must be the only cancellation mechanism. + +The scheduling body should be structurally equivalent to: + +```ts +const scheduleCssReload = Effect.fn(function* () { + yield* Effect.sleep(Duration.millis(CSS_SOURCE_RELOAD_DELAY_MS)); + yield* reloadCssAssetOwnership; +}); +``` + +- [ ] **Step 4: Keep generation readiness on one `Deferred` abstraction** + +`src/dev-generation.ts` already uses `EffectDeferred`. Remove surrounding `runPluginEffect` calls and expose readiness as an Effect inside Node-side workflows: + +```ts +const awaitGeneration = (entryName: string) => + EffectDeferred.await(state.readiness).pipe( + Effect.map(generation => selectBuild(generation, entryName)) + ); +``` + +Only `loadReactRouterServerBuild`, the exported Promise API, should call `runtime.runPromise` or an explicit standalone adapter. + +- [ ] **Step 5: Convert reusable orchestration functions to `Effect.fn`** + +Convert `finishRuntimeAttemptEffect`, `evaluateServerBuildsEffect`, and compilation orchestration functions that already return `Effect` from raw arrow functions/`Effect.gen` wrappers to named `Effect.fn` definitions. Do not convert pure selectors, cache lookups, or manifest shaping. + +- [ ] **Step 6: Run the graph-derived affected tests** + +```bash +pnpm rstest run tests/dev-runtime-controller.test.ts tests/dev-generation.test.ts tests/dev-generation-multi-entry.test.ts tests/dev-runtime.integration.test.ts tests/index.test.ts +pnpm typecheck +``` + +- [ ] **Step 7: Run dev performance and maintainability checks** + +Run large dev inline and worker benchmarks plus the existing HMR/HDR fail-fast suite: + +```bash +pnpm test:react-router-framework:failfast +git diff --numstat -- src/dev-runtime-session.ts src/dev-runtime-controller.ts src/dev-generation.ts src/dev-runtime-compilation.ts src/dev-runtime-artifacts.ts +``` + +Expected: no performance regression; Effect removes manual ownership or state coordination wherever it is introduced. + +- [ ] **Step 8: Commit** + +```bash +git add src/dev-runtime-session.ts src/dev-runtime-controller.ts src/dev-generation.ts src/dev-runtime-compilation.ts src/dev-runtime-artifacts.ts tests/dev-runtime-controller.test.ts tests/dev-generation.test.ts tests/dev-generation-multi-entry.test.ts tests/dev-runtime.integration.test.ts +git commit -m "refactor: simplify dev runtime with effect" +``` + +--- + +### Task 7: Normalize Config, Manifest, Build, and Prerender Workflows + +**Files:** + +- Modify: `src/react-router-config.ts` +- Modify: `src/build-manifest.ts` +- Modify: `src/manifest.ts` +- Modify: `src/prerender-build.ts` +- Modify: `src/rsc-prerender.ts` +- Modify: `src/server-build-resolution.ts` +- Modify: `src/server-utils.ts` +- Modify: `src/typegen.ts` +- Modify: `tests/react-router-config.test.ts` +- Modify: `tests/build-manifest.test.ts` +- Modify: `tests/manifest.test.ts` +- Modify: `tests/manifest-split-route-modules.test.ts` +- Modify: `tests/prerender.test.ts` +- Modify: `tests/rsc-prerender.test.ts` +- Modify: `tests/typegen.test.ts` + +**Interfaces:** + +- Internal functions return `Effect` and compose without crossing to Promise. +- Existing exported Promise functions remain adapters for compatibility. +- Concurrency remains capped with `Effect.forEach(..., { concurrency })`. +- Pure route/manifest transforms remain pure functions. + +- [ ] **Step 1: Establish naming and boundary rules** + +For each exported pair such as: + +```ts +getBuildManifestEffect(...) +getBuildManifest(...): Promise<...> +``` + +keep the Promise name stable and make the Effect workflow the internal implementation. Remove `Effect` suffixes from private functions where the return type already makes the abstraction obvious; preserve exported names if tests or consumers import them. + +- [ ] **Step 2: Convert reusable workflows to `Effect.fn`** + +Replace the current wrapper beginning at `src/build-manifest.ts:93`: + +```ts +export const getBuildManifestEffect = ({ + reactRouterConfig, + routes, + rootDirectory, +}: GetBuildManifestOptions): Effect.Effect< + BuildManifest | undefined, + Error, + never +> => + Effect.gen(function* () { +``` + +with this named wrapper while leaving the statements currently inside the generator unchanged: + +```ts +export const getBuildManifestEffect = Effect.fn('BuildManifest.get')( + function* ({ + reactRouterConfig, + routes, + rootDirectory, + }: GetBuildManifestOptions) { +``` + +Change the final `});` for the old `Effect.gen` to `});` for `Effect.fn`; no second helper or wrapper is introduced. Apply the same wrapper-only transformation to reusable workflows in the other listed files. + +Do not wrap `getAddressableRoutes`, `getRouteBranch`, `createRouteManifestItem`, `normalizeSubResourceIntegrity`, or other pure helpers. + +- [ ] **Step 3: Remove nested Promise crossings** + +Replace all internal forms of: + +```ts +yield* tryPluginPromise(() => promiseAdapter(...)); +``` + +with direct Effect composition: + +```ts +yield* effectImplementation(...); +``` + +The final source search must show `runPluginEffect(` only in explicit exported Promise adapters pending Task 8. + +- [ ] **Step 4: Preserve bounded parallelism exactly** + +Keep these existing concurrency behaviors: + +- preset resolution uses `getCappedPluginConcurrency()`; +- manifest route analysis uses `ROUTE_ANALYSIS_CONCURRENCY` capped by route count; +- server bundle resolution uses the existing capped concurrency; +- prerender paths use the configured prerender concurrency; +- type generation and route analysis do not become unbounded. + +Use `Effect.forEach` rather than adding a custom pool abstraction. + +- [ ] **Step 5: Preserve request cancellation** + +Keep `createBuildRequestEffect` based on `Effect.acquireUseRelease` so interruption aborts its `AbortController`: + +```ts +export const createBuildRequestEffect = ( + input: string | URL, + init: RequestInit | undefined, + handle: (request: Request) => Promise +): Effect.Effect => + Effect.acquireUseRelease( + Effect.sync(() => new AbortController()), + controller => + tryPluginPromise(() => + handle(new Request(input, { ...init, signal: controller.signal })) + ), + controller => Effect.sync(() => controller.abort()) + ); +``` + +- [ ] **Step 6: Run the graph-derived build tests** + +```bash +pnpm rstest run tests/react-router-config.test.ts tests/build-manifest.test.ts tests/manifest.test.ts tests/manifest-split-route-modules.test.ts tests/manifest-version.test.ts tests/prerender.test.ts tests/rsc-prerender.test.ts tests/typegen.test.ts +pnpm typecheck +``` + +Expected: PASS. + +- [ ] **Step 7: Run full build benchmarks and review maintainability** + +Repeat `full-build`, `1024-inline`, and `1024-workers` benchmarks with `after-task-7` output paths. Verify route analysis and prerender concurrency did not regress. + +```bash +git diff --numstat -- src/react-router-config.ts src/build-manifest.ts src/manifest.ts src/prerender-build.ts src/rsc-prerender.ts src/server-build-resolution.ts src/server-utils.ts src/typegen.ts +``` + +Expected: no performance regression and no redundant Promise/Effect boundary remains. + +- [ ] **Step 8: Commit** + +```bash +git add src/react-router-config.ts src/build-manifest.ts src/manifest.ts src/prerender-build.ts src/rsc-prerender.ts src/server-build-resolution.ts src/server-utils.ts src/typegen.ts tests/react-router-config.test.ts tests/build-manifest.test.ts tests/manifest.test.ts tests/manifest-split-route-modules.test.ts tests/prerender.test.ts tests/rsc-prerender.test.ts tests/typegen.test.ts +git commit -m "refactor: compose server workflows with effect" +``` + +--- + +### Task 8: Remove Transitional Runners and Consolidate Error Policy + +**Files:** + +- Modify: `src/effect-runtime.ts` +- Modify: all remaining Node-side callers reported by `rg "runPluginEffect|Effect\.run" src` +- Modify: `tests/effect-runtime.test.ts` +- Modify: affected subsystem tests from Tasks 5–7 + +**Interfaces:** + +- Deletes: `runPluginEffect` if no public compatibility adapter requires it. +- Retains: `tryPluginPromise`, `normalizeEffectError` only if they eliminate repeated boundary code. +- Produces: one error policy at Promise/Rsbuild boundaries and typed recovery only where behavior differs. + +- [ ] **Step 1: Inventory remaining runtime crossings** + +```bash +rg -n "runPluginEffect|Effect\.run(?:Promise|PromiseExit|Fork|Sync)|ManagedRuntime\.make" src --glob '*.ts' +``` + +Expected target after this task: + +- `ManagedRuntime.make`, raw `Effect.run*`, and `Cause`/`Exit` normalization appear only in `src/effect-runtime.ts`; +- `runtime.runPromise` appears only in `src/index.ts` and explicit exported Promise adapters; +- no runtime calls appear in worker or browser/generated code. + +- [ ] **Step 2: Delete redundant Promise adapters** + +Where both Promise and Effect variants are private, delete the Promise variant. Where the Promise function is exported, keep exactly this adapter shape: + +```ts +export const publicPromiseApi = (...args: Args): Promise => + runStandaloneEffect(internalEffectApi(...args)); +``` + +Define `runStandaloneEffect` once in `effect-runtime.ts` only if exported non-plugin APIs cannot receive the plugin runtime. Document it as a compatibility boundary, not a second application runtime. + +- [ ] **Step 3: Consolidate failure normalization** + +Use `runPromiseExit` only inside `effect-runtime.ts` to preserve the current behavior of surfacing typed failures as their original `Error` and squashing defects. Do not convert cancellation into a normal `Error` before cleanup completes. + +Keep the existing behavior: + +```ts +const normalizeEffectCause = (cause: Cause.Cause): Error => { + const failure = Cause.failureOption(cause); + return normalizeEffectError( + Option.isSome(failure) ? failure.value : Cause.squash(cause) + ); +}; +``` + +- [ ] **Step 4: Remove ceremonial Effect wrappers** + +Search for `Effect.sync` bodies that only call pure helpers and inline them outside Effect workflows. Keep `Effect.sync` for mutation, logging, clock reads, controller creation, and foreign synchronous APIs that may throw. + +- [ ] **Step 5: Run affected tests and typecheck** + +```bash +pnpm rstest run tests/effect-runtime.test.ts tests/index.test.ts tests/dev-runtime-controller.test.ts tests/dev-generation.test.ts tests/route-watch.test.ts tests/parallel-route-transforms.test.ts tests/manifest.test.ts tests/prerender.test.ts +pnpm typecheck +``` + +Expected: PASS. + +- [ ] **Step 6: Enforce crossing policy and review source size** + +```bash +rg -n "Effect\.run(?:Promise|PromiseExit|Fork|Sync)|ManagedRuntime\.make" src --glob '*.ts' +git diff --numstat $(cat .benchmark/effect-server-architecture/base-commit.txt)..HEAD -- src +``` + +Expected: runner search is limited to `src/effect-runtime.ts`; source-size changes are understood and justified by simpler ownership or stronger coverage. + +- [ ] **Step 7: Commit** + +```bash +git add src tests +git commit -m "refactor: remove transitional effect boundaries" +``` + +--- + +### Task 9: Verify Browser and Worker Packaging Boundaries + +**Files:** + +- Modify: `scripts/test-package-interop.mts` +- Preserve: `src/parallel-route-transform-worker.ts` +- Preserve: browser templates and generated HMR/runtime source + +**Interfaces:** + +- Production package may bundle or externalize Effect for Node-side plugin execution according to existing Rslib behavior. +- Browser/template/worker outputs must not gain Effect imports or runtime code. + +- [ ] **Step 1: Build and inventory emitted Effect references** + +```bash +pnpm build +rg -n "effect/(Effect|Fiber|Layer|ManagedRuntime|Scope)|from ['\"]effect['\"]" dist +``` + +Classify every hit by emitted entry/chunk. Accept hits only in Node-side plugin chunks. Any hit reachable from `templates/entry.client`, generated HMR code, RSC client entry, or `parallel-route-transform-worker` fails the task. + +- [ ] **Step 2: Add package-boundary assertions** + +Extend `scripts/test-package-interop.mts` to discover emitted browser/worker files and assert: + +```ts +const emittedFiles = await readdir(distDir, { + recursive: true, + withFileTypes: true, +}); +const browserAndWorkerOutputs = emittedFiles + .filter(entry => entry.isFile()) + .map(entry => join(entry.parentPath, entry.name)) + .filter(file => + /templates[\\/]entry\.(?:client|rsc\.client)|parallel-route-transform-worker/.test( + file + ) + ); + +for (const relativePath of browserAndWorkerOutputs) { + const source = await readFile(resolve(distDir, relativePath), 'utf8'); + expect(source).not.toMatch( + /effect\/(?:Effect|Fiber|Layer|ManagedRuntime|Scope)/ + ); + expect(source).not.toMatch(/from ['"]effect['"]/); +} +``` + +Use Node's `assert.doesNotMatch` instead of `expect` if this script uses `node:assert`; follow its existing assertion style. Import `readdir`, `readFile`, `join`, and `resolve` from the same Node modules already used by the script. + +- [ ] **Step 3: Run package and browser smoke tests** + +```bash +pnpm test:package-interop +pnpm test:react-router-framework:smoke +``` + +Expected: PASS. + +- [ ] **Step 4: Measure package output size** + +```bash +du -sk dist > .benchmark/effect-server-architecture/after-dist-size.txt +find dist -type f -print0 | sort -z | xargs -0 wc -c > .benchmark/effect-server-architecture/after-dist-files.txt +``` + +Compare against a baseline build from the recorded base commit. Reject unexplained worker/browser chunk growth; Node-side plugin growth must be justified by total source/maintenance savings. + +- [ ] **Step 5: Commit boundary tests or config fix** + +```bash +git add scripts/test-package-interop.mts +git commit -m "test: verify effect packaging boundaries" +``` + +Skip unchanged paths in `git add`; do not create an empty commit. + +--- + +### Task 10: Final Performance, Health, Maintainability, and Compatibility Gate + +**Files:** + +- Verify: all source and tests changed above +- Preserve unchanged: `README.md` +- Do not create: `.changeset/effect-server-architecture.md` because this plan is behavior-compatible + +**Interfaces:** + +- Delivers: behavior-compatible Node-side architecture with one managed runtime, scoped resources, Effect-free workers/browser code, clearer ownership, and accepted performance. + +- [ ] **Step 1: Run final source-policy searches** + +```bash +rg -n "from ['\"]effect['\"]" src --glob '*.ts' +rg -n "Effect\.run(?:Promise|PromiseExit|Fork|Sync)|ManagedRuntime\.make" src --glob '*.ts' +rg -n "runPluginEffect" src --glob '*.ts' +! rg -n "effect/|ManagedRuntime|Effect\." src/parallel-route-transform-worker.ts src/templates --glob '*.{ts,tsx}' +``` + +Expected: no production barrel imports; raw runners are centralized; worker/templates are Effect-free. + +- [ ] **Step 2: Run the complete test matrix** + +```bash +pnpm typecheck +pnpm test:core +pnpm build +pnpm test:package-interop +pnpm test:react-router-framework:failfast +``` + +Expected: PASS. + +- [ ] **Step 3: Run the complete benchmark matrix** + +Repeat every command in `Acceptance Measurements` with `final-` output paths. Compare each matching `baseline.json` with `scripts/compare-benchmarks.mts`. + +Also run: + +```bash +pnpm bench:synthetic-app -- --plugin-root "$PWD" --profile=all --runs=5 +``` + +Expected: every metric satisfies the 2% median/5% tail gates; worker-enabled 1024-route build retains its benefit; constrained dev startup does not absorb additional Effect worker cost. + +- [ ] **Step 4: Record final LOC as a diagnostic** + +```bash +git ls-files -z '*.ts' '*.tsx' '*.js' '*.mjs' '*.mts' \ + | xargs -0 wc -l \ + > .benchmark/effect-server-architecture/final-total-loc.txt +find src -type f \( -name '*.ts' -o -name '*.tsx' \) -print0 \ + | xargs -0 wc -l \ + > .benchmark/effect-server-architecture/final-src-loc.txt +diff -u .benchmark/effect-server-architecture/base-total-loc.txt .benchmark/effect-server-architecture/final-total-loc.txt || true +diff -u .benchmark/effect-server-architecture/base-src-loc.txt .benchmark/effect-server-architecture/final-src-loc.txt || true +``` + +Manually compare the final `total` rows. Explain material growth and confirm it comes from useful tests, benchmark support, or code that replaces more complex ownership. Test deletion is allowed only when it removes obsolete adapter-specific coverage and the same behavior remains covered through public APIs. + +- [ ] **Step 5: Re-run TraceDecay health and affected-test analysis** + +Use `tracedecay_health(details: true, path: "src")`, `tracedecay_diff_context` for every changed source file, and `tracedecay_affected` to confirm no graph-derived tests were omitted. + +Acceptance relative to the recorded audit: + +- overall source health must not fall below 8,637/10,000; +- acyclicity remains 1.0; +- depth remains 1.0; +- modularity remains at least 0.9565; +- complexity equality must improve or remain within measurement noise; +- no new dependency cycle may involve `effect-runtime.ts`. + +- [ ] **Step 6: Review maintainability outcomes** + +Confirm all of these are true in the final diff: + +- one plugin runtime creation site; +- one runtime disposal policy; +- no duplicated worker/watcher/prewarm cleanup registration; +- no Effect runtime inside worker/browser code; +- no Promise-to-Effect-to-Promise bounce inside internal workflows; +- public Promise APIs preserved; +- fewer manual timer handles, `closed` flags, close promises, and `Promise.allSettled` cleanup aggregators; +- production ownership and boundary code is easier to follow even where line count is unchanged. + +- [ ] **Step 7: Confirm release documentation remains unchanged** + +```bash +git diff --exit-code -- README.md .changeset +``` + +Expected: exit 0. A behavior or packaging change is outside this plan and requires a separate decision before adding a changeset. + +- [ ] **Step 8: Commit final cleanup** + +```bash +git add src tests scripts +git commit -m "refactor: complete effect server architecture" +``` + +Skip unchanged paths. Do not commit `.benchmark/` artifacts. + +## Stop Conditions + +Stop and redesign the current tranche when any condition occurs: + +- Effect is imported by `src/parallel-route-transform-worker.ts` or an emitted worker chunk. +- A runtime is created inside a hook callback, route loop, request handler, worker, or transform. +- Worker-enabled dev readiness, worker-enabled 1024-route build, HMR update latency, CPU, or RSS exceeds the performance thresholds. +- A tranche adds another abstraction or ownership path without simplifying the superseded implementation. +- Resource cleanup requires both a runtime finalizer and the old manual close-hook path. +- An internal workflow repeatedly crosses Promise/Effect boundaries. +- Browser/template output contains Effect runtime code. +- A public Promise API changes without explicit approval. +- The refactor creates a dependency cycle through `effect-runtime.ts`. + +## Commit Sequence + +1. `test: protect worker runtime boundary` +2. `refactor: add managed plugin effect runtime` +3. `refactor: centralize plugin effect runtime boundary` +4. `refactor: scope parent route transform workers` +5. `refactor: scope dev background resources` +6. `refactor: simplify dev runtime with effect` +7. `refactor: compose server workflows with effect` +8. `refactor: remove transitional effect boundaries` +9. `test: verify effect packaging boundaries` when files changed +10. `refactor: complete effect server architecture` when final cleanup changed files diff --git a/examples/client-only/package.json b/examples/client-only/package.json index 8a0712df..3648c037 100644 --- a/examples/client-only/package.json +++ b/examples/client-only/package.json @@ -26,7 +26,6 @@ "@types/react": "^19.2.10", "@types/react-dom": "^19.2.3", "rsbuild-plugin-react-router": "workspace:*", - "typescript": "^5.9.3", - "vite": "^7.3.1" + "typescript": "^5.9.3" } } diff --git a/examples/client-only/tsconfig.json b/examples/client-only/tsconfig.json index dc391a45..0fb4dbe4 100644 --- a/examples/client-only/tsconfig.json +++ b/examples/client-only/tsconfig.json @@ -7,7 +7,7 @@ ], "compilerOptions": { "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["node", "vite/client"], + "types": ["node"], "target": "ES2022", "module": "ES2022", "moduleResolution": "bundler", diff --git a/examples/cloudflare/tsconfig.node.json b/examples/cloudflare/tsconfig.node.json index 8e3f1d34..2c95f5d5 100644 --- a/examples/cloudflare/tsconfig.node.json +++ b/examples/cloudflare/tsconfig.node.json @@ -1,6 +1,6 @@ { "extends": "./tsconfig.json", - "include": ["tailwind.config.ts", "vite.config.ts"], + "include": ["tailwind.config.ts", "rsbuild.config.ts"], "compilerOptions": { "composite": true, "strict": true, diff --git a/examples/custom-node-server/tsconfig.json b/examples/custom-node-server/tsconfig.json index c50dc993..189cf2ee 100644 --- a/examples/custom-node-server/tsconfig.json +++ b/examples/custom-node-server/tsconfig.json @@ -10,7 +10,7 @@ "strict": true, "checkJs": true, "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["@rsbuild/core/types", "node"], + "types": ["node"], "target": "ES2022", "module": "ES2022", "moduleResolution": "bundler", diff --git a/examples/default-template/package.json b/examples/default-template/package.json index dafe66f5..5c1819ff 100644 --- a/examples/default-template/package.json +++ b/examples/default-template/package.json @@ -37,8 +37,6 @@ "string-replace-loader": "^3.3.0", "tailwindcss": "^4.1.18", "text-encoder-lite": "^2.0.0", - "typescript": "^5.9.3", - "vite": "^7.3.1", - "vite-tsconfig-paths": "^6.0.5" + "typescript": "^5.9.3" } } diff --git a/examples/default-template/tests/e2e/dev-route-watch.test.ts b/examples/default-template/tests/e2e/dev-route-watch.test.ts index cc0e3ed2..fb14ca74 100644 --- a/examples/default-template/tests/e2e/dev-route-watch.test.ts +++ b/examples/default-template/tests/e2e/dev-route-watch.test.ts @@ -11,10 +11,7 @@ import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const appDirectory = join(__dirname, '../../app'); -const restartMarkerPath = join( - __dirname, - '../../build/client/.react-router/route-watch' -); +const restartMarkerPath = join(appDirectory, '..', '.react-router/route-watch'); const devRoutesConfigPath = join(appDirectory, 'dev-routes.ts'); const addedRoutePath = join(appDirectory, 'routes/dev-added-route.tsx'); const addedRouteUrl = '/dev-added-route'; diff --git a/examples/default-template/tsconfig.json b/examples/default-template/tsconfig.json index dc391a45..7c424432 100644 --- a/examples/default-template/tsconfig.json +++ b/examples/default-template/tsconfig.json @@ -6,16 +6,27 @@ ".react-router/types/**/*" ], "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["node", "vite/client"], + "lib": [ + "DOM", + "DOM.Iterable", + "ES2022" + ], + "types": [ + "node" + ], "target": "ES2022", "module": "ES2022", "moduleResolution": "bundler", "jsx": "react-jsx", - "rootDirs": [".", "./.react-router/types"], + "rootDirs": [ + ".", + "./.react-router/types" + ], "baseUrl": ".", "paths": { - "~/*": ["./app/*"] + "~/*": [ + "./app/*" + ] }, "esModuleInterop": true, "verbatimModuleSyntax": true, diff --git a/examples/epic-stack/.github/workflows/deploy.yml b/examples/epic-stack/.github/workflows/deploy.yml index e0eac2bf..1743b8b1 100644 --- a/examples/epic-stack/.github/workflows/deploy.yml +++ b/examples/epic-stack/.github/workflows/deploy.yml @@ -57,8 +57,8 @@ jobs: - name: 🔎 Type check run: npm run typecheck --if-present - vitest: - name: ⚡ Vitest + rstest: + name: ⚡ Rstest runs-on: ubuntu-22.04 steps: - name: ⬇️ Checkout repo @@ -78,7 +78,7 @@ jobs: - name: 🖼 Build icons run: npm run build:icons - - name: ⚡ Run vitest + - name: ⚡ Run rstest run: npm run test -- --coverage playwright: @@ -137,7 +137,7 @@ jobs: deploy: name: 🚀 Deploy runs-on: ubuntu-22.04 - needs: [lint, typecheck, vitest, playwright] + needs: [lint, typecheck, rstest, playwright] # only build/deploy branches on pushes if: ${{ github.event_name == 'push' }} diff --git a/examples/epic-stack/app/routes/_marketing+/logos/vitest.svg b/examples/epic-stack/app/routes/_marketing+/logos/vitest.svg deleted file mode 100644 index fd9daaf6..00000000 --- a/examples/epic-stack/app/routes/_marketing+/logos/vitest.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/examples/epic-stack/docs/decisions/036-rsbuild.md b/examples/epic-stack/docs/decisions/036-rsbuild.md new file mode 100644 index 00000000..f85aa55f --- /dev/null +++ b/examples/epic-stack/docs/decisions/036-rsbuild.md @@ -0,0 +1,24 @@ +# Adopting Rsbuild + +Date: 2026-07-10 + +Status: accepted + +## Context + +This example originated in the Epic Stack's React Router/Vite setup. This +repository exercises the same framework behavior through +`rsbuild-plugin-react-router` and Rsbuild/Rspack. React Router's upstream Vite +plugin remains the behavioral reference; React Router does not provide a native +Rsbuild plugin. + +## Decision + +Use Rsbuild with `rsbuild-plugin-react-router` for this example. + +## Consequences + +The example uses Rsbuild configuration and scripts while preserving React +Router route-module, SSR, and development semantics. Bundler-specific +differences are implemented by this plugin and covered by the repository's +framework integration tests. diff --git a/examples/epic-stack/docs/decisions/036-vite.md b/examples/epic-stack/docs/decisions/036-vite.md deleted file mode 100644 index 6af222e7..00000000 --- a/examples/epic-stack/docs/decisions/036-vite.md +++ /dev/null @@ -1,46 +0,0 @@ -# Adopting Vite - -Date: 2024-02-22 - -Status: accepted - -## Context - -[The Remix Team has created a Vite Plugin](https://remix.run/blog/remix-vite-stable) -and it is now stable. It can be used to replace the existing remix compiler. In -Remix v3 the plugin will be the only supported way to build remix applications. - -Using vite also means we get better hot module replacement, a thriving ecosystem -of tools, and shared efforts with other projects using vite. - -If we don't adopt vite, we'll be stuck on Remix v2 forever 🙃 Now that the vite -plugin is stable, adopting vite is really the only way forward. - -That said, we currently have a few route modules that mix server-only utilities -with server/client code. In vite, you cannot have any exported functions which -use server-only code, so those utilities will need to be moved. Luckily, the -vite plugin will fail the build if it finds any issues so if it builds, it -works. Additionally, this will help us make a cleaner separation between server -and server/client code which is a good thing. - -The simple rule is this: if it's a Remix export (like `loader`, or `action`) -then it can be in the route. If it's our own utility export (like -`requireRecentVerification` we had in the `/verify` route) then it needs to go -in a `.server` file. To be clear, if you don't export it, then it's fine. -Server-only utility functions are fine in routes. It just becomes a problem for -remix when they are exported. - -An interesting exception to this is sever-only code in the `handle` export like -the [`getSitemapEntries` function](https://github.com/nasa-gcn/remix-seo). For -this, you need to use -[`vite-env-only`](https://github.com/pcattori/vite-env-only). - -## Decision - -Adopt vite. - -## Consequences - -Almost everything is better. We have slightly more complicated rules around the -server/client code separation, but for the most part that's better and there are -fewer surprises. diff --git a/examples/epic-stack/docs/monitoring.md b/examples/epic-stack/docs/monitoring.md index ec354fc8..d43063b9 100644 --- a/examples/epic-stack/docs/monitoring.md +++ b/examples/epic-stack/docs/monitoring.md @@ -20,7 +20,7 @@ You can sign up for Sentry and create a Remix project from visiting [this url](https://sentry.io/signup/?project_platform=javascript-remix) and filling out the signup form. -## Setting up the sentry-vite plugin +## Setting up Sentry releases Once you see the onboarding page which has the DSN, copy that somewhere (this becomes `SENTRY_DSN`). Now, set the sentry dsn secret in Fly.io to be used as an @@ -49,7 +49,7 @@ for your project under `Organization > Projects > Project > Name` (`SENTRY_PROJECT`). In the 'build' section of the [Dockerfile](../other/Dockerfile), there is an -example of how to pass `SENTRY_AUTH_TOKEN` secret, so it is available to Vite +example of how to pass `SENTRY_AUTH_TOKEN` secret, so it is available to Rsbuild when `npm run build` is run. You may also uncomment and hard code your `SENTRY_ORG` and `SENTRY_PROJECT` values. Setup up your secrets in [GitHub Actions secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions). @@ -60,8 +60,4 @@ workflow. Note that these do not need to be added to the [`env.server`](../app/utils/env.server.ts) env vars schema, as they are only used during the build and not the runtime. -The Sentry Vite plugin in [`vite.config.ts`](../vite.config.ts) will create -sentry releases for you and automatically associate commits during the vite -build once the `SENTRY_AUTH_TOKEN` is set. In this setup we have utilized a -simple strategy for naming releases of using the commit sha, passed in as a -build arg via the GitHub action workflow. +Use your deployment workflow to create Sentry releases and associate commits once `SENTRY_AUTH_TOKEN` is set. A common release name is the commit sha passed through the GitHub action workflow. diff --git a/examples/epic-stack/docs/routing.md b/examples/epic-stack/docs/routing.md index 98022aac..7f12737a 100644 --- a/examples/epic-stack/docs/routing.md +++ b/examples/epic-stack/docs/routing.md @@ -4,7 +4,7 @@ The Epic Stack uses file-based routing with Remix. However, it's not using the built-in routing convention of Remix and instead is using [remix-flat-routes](https://github.com/kiliman/remix-flat-routes) which is a special implementation of the Remix convention that adds a few features. You'll -find it configured for the application in the `vite.config.js` file at the root +find it configured for the application in the `rsbuild.config.ts` file at the root of the app. Specifically "hybrid routes." We'll defer to the `remix-flat-routes` documentation for specifics, but an diff --git a/examples/epic-stack/docs/seo.md b/examples/epic-stack/docs/seo.md index b5de558c..5314f648 100644 --- a/examples/epic-stack/docs/seo.md +++ b/examples/epic-stack/docs/seo.md @@ -17,24 +17,18 @@ from the `@nasa-gcn/remix-seo` docs: ```tsx // routes/blog/_layout.tsx import { type SEOHandle } from '@nasa-gcn/remix-seo' -import { serverOnly$ } from 'vite-env-only/macros' export const handle: SEOHandle = { - getSitemapEntries: serverOnly$(async (request) => { + async getSitemapEntries(request) { const blogs = await db.blog.findMany() return blogs.map((blog) => { return { route: `/blog/${blog.slug}`, priority: 0.7 } }) - }), + }, } ``` -Note the use of -[`vite-env-only/macros`](https://github.com/pcattori/vite-env-only). This is -because `handle` is a route export object that goes in both the client as well -as the server, but our sitemap function should only be run on the server. So we -use `vite-env-only/macros` to make sure the function is removed for the client -build. Support for this is pre-configured in the `vite.config.ts` file. +Keep sitemap data server-only by loading it from server route modules or other server-only files. ```tsx // in your routes/url-that-doesnt-need-sitemap diff --git a/examples/epic-stack/package.json b/examples/epic-stack/package.json index 270e31a1..909173ed 100644 --- a/examples/epic-stack/package.json +++ b/examples/epic-stack/package.json @@ -113,7 +113,6 @@ "tailwindcss": "4.1.18", "tailwindcss-animate": "1.0.7", "tailwindcss-radix": "4.0.2", - "vite-env-only": "3.0.3", "zod": "3.25.76" }, "devDependencies": { @@ -121,7 +120,6 @@ "@faker-js/faker": "10.2.0", "@playwright/test": "^1.61.1", "@react-router/dev": "^7.13.0", - "@sentry/vite-plugin": "4.8.0", "@sly-cli/sly": "2.1.1", "@tailwindcss/nesting": "0.0.0-insiders.565cd3e", "@tailwindcss/postcss": "^4.1.18", @@ -146,7 +144,6 @@ "@types/source-map-support": "0.5.10", "@rstest/core": "0.8.1", "@rstest/coverage-istanbul": "0.2.0", - "@vitejs/plugin-react": "5.1.2", "autoprefixer": "10.4.23", "enforce-unique": "1.3.0", "esbuild": "0.27.2", @@ -161,8 +158,7 @@ "prettier-plugin-tailwindcss": "0.7.2", "remix-flat-routes": "0.8.5", "tsx": "4.21.0", - "typescript": "5.9.3", - "vite": "7.3.1" + "typescript": "5.9.3" }, "engines": { "node": "22" diff --git a/examples/epic-stack/tsconfig.json b/examples/epic-stack/tsconfig.json index 60aec3ad..fc83be04 100644 --- a/examples/epic-stack/tsconfig.json +++ b/examples/epic-stack/tsconfig.json @@ -3,7 +3,7 @@ "extends": ["@epic-web/config/typescript"], "compilerOptions": { // TODO: Probably should move this into epic-web/config - "types": ["@react-router/node", "vite/client"], + "types": ["-router/node", "node"], "rootDirs": [".", "./.react-router/types"], "paths": { "#app/*": ["./app/*"], diff --git a/examples/epic-stack/types/env.env.d.ts b/examples/epic-stack/types/env.env.d.ts index 8d2f9518..6b1c00ce 100644 --- a/examples/epic-stack/types/env.env.d.ts +++ b/examples/epic-stack/types/env.env.d.ts @@ -1,2 +1,2 @@ -/// +/// /// diff --git a/examples/federation/epic-stack-remote/.github/workflows/deploy.yml b/examples/federation/epic-stack-remote/.github/workflows/deploy.yml index e0eac2bf..1743b8b1 100644 --- a/examples/federation/epic-stack-remote/.github/workflows/deploy.yml +++ b/examples/federation/epic-stack-remote/.github/workflows/deploy.yml @@ -57,8 +57,8 @@ jobs: - name: 🔎 Type check run: npm run typecheck --if-present - vitest: - name: ⚡ Vitest + rstest: + name: ⚡ Rstest runs-on: ubuntu-22.04 steps: - name: ⬇️ Checkout repo @@ -78,7 +78,7 @@ jobs: - name: 🖼 Build icons run: npm run build:icons - - name: ⚡ Run vitest + - name: ⚡ Run rstest run: npm run test -- --coverage playwright: @@ -137,7 +137,7 @@ jobs: deploy: name: 🚀 Deploy runs-on: ubuntu-22.04 - needs: [lint, typecheck, vitest, playwright] + needs: [lint, typecheck, rstest, playwright] # only build/deploy branches on pushes if: ${{ github.event_name == 'push' }} diff --git a/examples/federation/epic-stack-remote/app/routes/_marketing+/logos/vitest.svg b/examples/federation/epic-stack-remote/app/routes/_marketing+/logos/vitest.svg deleted file mode 100644 index fd9daaf6..00000000 --- a/examples/federation/epic-stack-remote/app/routes/_marketing+/logos/vitest.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/examples/federation/epic-stack-remote/docs/decisions/036-rsbuild.md b/examples/federation/epic-stack-remote/docs/decisions/036-rsbuild.md new file mode 100644 index 00000000..f85aa55f --- /dev/null +++ b/examples/federation/epic-stack-remote/docs/decisions/036-rsbuild.md @@ -0,0 +1,24 @@ +# Adopting Rsbuild + +Date: 2026-07-10 + +Status: accepted + +## Context + +This example originated in the Epic Stack's React Router/Vite setup. This +repository exercises the same framework behavior through +`rsbuild-plugin-react-router` and Rsbuild/Rspack. React Router's upstream Vite +plugin remains the behavioral reference; React Router does not provide a native +Rsbuild plugin. + +## Decision + +Use Rsbuild with `rsbuild-plugin-react-router` for this example. + +## Consequences + +The example uses Rsbuild configuration and scripts while preserving React +Router route-module, SSR, and development semantics. Bundler-specific +differences are implemented by this plugin and covered by the repository's +framework integration tests. diff --git a/examples/federation/epic-stack-remote/docs/decisions/036-vite.md b/examples/federation/epic-stack-remote/docs/decisions/036-vite.md deleted file mode 100644 index 6af222e7..00000000 --- a/examples/federation/epic-stack-remote/docs/decisions/036-vite.md +++ /dev/null @@ -1,46 +0,0 @@ -# Adopting Vite - -Date: 2024-02-22 - -Status: accepted - -## Context - -[The Remix Team has created a Vite Plugin](https://remix.run/blog/remix-vite-stable) -and it is now stable. It can be used to replace the existing remix compiler. In -Remix v3 the plugin will be the only supported way to build remix applications. - -Using vite also means we get better hot module replacement, a thriving ecosystem -of tools, and shared efforts with other projects using vite. - -If we don't adopt vite, we'll be stuck on Remix v2 forever 🙃 Now that the vite -plugin is stable, adopting vite is really the only way forward. - -That said, we currently have a few route modules that mix server-only utilities -with server/client code. In vite, you cannot have any exported functions which -use server-only code, so those utilities will need to be moved. Luckily, the -vite plugin will fail the build if it finds any issues so if it builds, it -works. Additionally, this will help us make a cleaner separation between server -and server/client code which is a good thing. - -The simple rule is this: if it's a Remix export (like `loader`, or `action`) -then it can be in the route. If it's our own utility export (like -`requireRecentVerification` we had in the `/verify` route) then it needs to go -in a `.server` file. To be clear, if you don't export it, then it's fine. -Server-only utility functions are fine in routes. It just becomes a problem for -remix when they are exported. - -An interesting exception to this is sever-only code in the `handle` export like -the [`getSitemapEntries` function](https://github.com/nasa-gcn/remix-seo). For -this, you need to use -[`vite-env-only`](https://github.com/pcattori/vite-env-only). - -## Decision - -Adopt vite. - -## Consequences - -Almost everything is better. We have slightly more complicated rules around the -server/client code separation, but for the most part that's better and there are -fewer surprises. diff --git a/examples/federation/epic-stack-remote/docs/monitoring.md b/examples/federation/epic-stack-remote/docs/monitoring.md index ec354fc8..d43063b9 100644 --- a/examples/federation/epic-stack-remote/docs/monitoring.md +++ b/examples/federation/epic-stack-remote/docs/monitoring.md @@ -20,7 +20,7 @@ You can sign up for Sentry and create a Remix project from visiting [this url](https://sentry.io/signup/?project_platform=javascript-remix) and filling out the signup form. -## Setting up the sentry-vite plugin +## Setting up Sentry releases Once you see the onboarding page which has the DSN, copy that somewhere (this becomes `SENTRY_DSN`). Now, set the sentry dsn secret in Fly.io to be used as an @@ -49,7 +49,7 @@ for your project under `Organization > Projects > Project > Name` (`SENTRY_PROJECT`). In the 'build' section of the [Dockerfile](../other/Dockerfile), there is an -example of how to pass `SENTRY_AUTH_TOKEN` secret, so it is available to Vite +example of how to pass `SENTRY_AUTH_TOKEN` secret, so it is available to Rsbuild when `npm run build` is run. You may also uncomment and hard code your `SENTRY_ORG` and `SENTRY_PROJECT` values. Setup up your secrets in [GitHub Actions secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions). @@ -60,8 +60,4 @@ workflow. Note that these do not need to be added to the [`env.server`](../app/utils/env.server.ts) env vars schema, as they are only used during the build and not the runtime. -The Sentry Vite plugin in [`vite.config.ts`](../vite.config.ts) will create -sentry releases for you and automatically associate commits during the vite -build once the `SENTRY_AUTH_TOKEN` is set. In this setup we have utilized a -simple strategy for naming releases of using the commit sha, passed in as a -build arg via the GitHub action workflow. +Use your deployment workflow to create Sentry releases and associate commits once `SENTRY_AUTH_TOKEN` is set. A common release name is the commit sha passed through the GitHub action workflow. diff --git a/examples/federation/epic-stack-remote/docs/routing.md b/examples/federation/epic-stack-remote/docs/routing.md index 98022aac..7f12737a 100644 --- a/examples/federation/epic-stack-remote/docs/routing.md +++ b/examples/federation/epic-stack-remote/docs/routing.md @@ -4,7 +4,7 @@ The Epic Stack uses file-based routing with Remix. However, it's not using the built-in routing convention of Remix and instead is using [remix-flat-routes](https://github.com/kiliman/remix-flat-routes) which is a special implementation of the Remix convention that adds a few features. You'll -find it configured for the application in the `vite.config.js` file at the root +find it configured for the application in the `rsbuild.config.ts` file at the root of the app. Specifically "hybrid routes." We'll defer to the `remix-flat-routes` documentation for specifics, but an diff --git a/examples/federation/epic-stack-remote/docs/seo.md b/examples/federation/epic-stack-remote/docs/seo.md index b5de558c..5314f648 100644 --- a/examples/federation/epic-stack-remote/docs/seo.md +++ b/examples/federation/epic-stack-remote/docs/seo.md @@ -17,24 +17,18 @@ from the `@nasa-gcn/remix-seo` docs: ```tsx // routes/blog/_layout.tsx import { type SEOHandle } from '@nasa-gcn/remix-seo' -import { serverOnly$ } from 'vite-env-only/macros' export const handle: SEOHandle = { - getSitemapEntries: serverOnly$(async (request) => { + async getSitemapEntries(request) { const blogs = await db.blog.findMany() return blogs.map((blog) => { return { route: `/blog/${blog.slug}`, priority: 0.7 } }) - }), + }, } ``` -Note the use of -[`vite-env-only/macros`](https://github.com/pcattori/vite-env-only). This is -because `handle` is a route export object that goes in both the client as well -as the server, but our sitemap function should only be run on the server. So we -use `vite-env-only/macros` to make sure the function is removed for the client -build. Support for this is pre-configured in the `vite.config.ts` file. +Keep sitemap data server-only by loading it from server route modules or other server-only files. ```tsx // in your routes/url-that-doesnt-need-sitemap diff --git a/examples/federation/epic-stack/.github/workflows/deploy.yml b/examples/federation/epic-stack/.github/workflows/deploy.yml index e0eac2bf..1743b8b1 100644 --- a/examples/federation/epic-stack/.github/workflows/deploy.yml +++ b/examples/federation/epic-stack/.github/workflows/deploy.yml @@ -57,8 +57,8 @@ jobs: - name: 🔎 Type check run: npm run typecheck --if-present - vitest: - name: ⚡ Vitest + rstest: + name: ⚡ Rstest runs-on: ubuntu-22.04 steps: - name: ⬇️ Checkout repo @@ -78,7 +78,7 @@ jobs: - name: 🖼 Build icons run: npm run build:icons - - name: ⚡ Run vitest + - name: ⚡ Run rstest run: npm run test -- --coverage playwright: @@ -137,7 +137,7 @@ jobs: deploy: name: 🚀 Deploy runs-on: ubuntu-22.04 - needs: [lint, typecheck, vitest, playwright] + needs: [lint, typecheck, rstest, playwright] # only build/deploy branches on pushes if: ${{ github.event_name == 'push' }} diff --git a/examples/federation/epic-stack/app/routes/_marketing+/logos/vitest.svg b/examples/federation/epic-stack/app/routes/_marketing+/logos/vitest.svg deleted file mode 100644 index fd9daaf6..00000000 --- a/examples/federation/epic-stack/app/routes/_marketing+/logos/vitest.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/examples/federation/epic-stack/docs/decisions/036-rsbuild.md b/examples/federation/epic-stack/docs/decisions/036-rsbuild.md new file mode 100644 index 00000000..f85aa55f --- /dev/null +++ b/examples/federation/epic-stack/docs/decisions/036-rsbuild.md @@ -0,0 +1,24 @@ +# Adopting Rsbuild + +Date: 2026-07-10 + +Status: accepted + +## Context + +This example originated in the Epic Stack's React Router/Vite setup. This +repository exercises the same framework behavior through +`rsbuild-plugin-react-router` and Rsbuild/Rspack. React Router's upstream Vite +plugin remains the behavioral reference; React Router does not provide a native +Rsbuild plugin. + +## Decision + +Use Rsbuild with `rsbuild-plugin-react-router` for this example. + +## Consequences + +The example uses Rsbuild configuration and scripts while preserving React +Router route-module, SSR, and development semantics. Bundler-specific +differences are implemented by this plugin and covered by the repository's +framework integration tests. diff --git a/examples/federation/epic-stack/docs/decisions/036-vite.md b/examples/federation/epic-stack/docs/decisions/036-vite.md deleted file mode 100644 index 6af222e7..00000000 --- a/examples/federation/epic-stack/docs/decisions/036-vite.md +++ /dev/null @@ -1,46 +0,0 @@ -# Adopting Vite - -Date: 2024-02-22 - -Status: accepted - -## Context - -[The Remix Team has created a Vite Plugin](https://remix.run/blog/remix-vite-stable) -and it is now stable. It can be used to replace the existing remix compiler. In -Remix v3 the plugin will be the only supported way to build remix applications. - -Using vite also means we get better hot module replacement, a thriving ecosystem -of tools, and shared efforts with other projects using vite. - -If we don't adopt vite, we'll be stuck on Remix v2 forever 🙃 Now that the vite -plugin is stable, adopting vite is really the only way forward. - -That said, we currently have a few route modules that mix server-only utilities -with server/client code. In vite, you cannot have any exported functions which -use server-only code, so those utilities will need to be moved. Luckily, the -vite plugin will fail the build if it finds any issues so if it builds, it -works. Additionally, this will help us make a cleaner separation between server -and server/client code which is a good thing. - -The simple rule is this: if it's a Remix export (like `loader`, or `action`) -then it can be in the route. If it's our own utility export (like -`requireRecentVerification` we had in the `/verify` route) then it needs to go -in a `.server` file. To be clear, if you don't export it, then it's fine. -Server-only utility functions are fine in routes. It just becomes a problem for -remix when they are exported. - -An interesting exception to this is sever-only code in the `handle` export like -the [`getSitemapEntries` function](https://github.com/nasa-gcn/remix-seo). For -this, you need to use -[`vite-env-only`](https://github.com/pcattori/vite-env-only). - -## Decision - -Adopt vite. - -## Consequences - -Almost everything is better. We have slightly more complicated rules around the -server/client code separation, but for the most part that's better and there are -fewer surprises. diff --git a/examples/federation/epic-stack/docs/monitoring.md b/examples/federation/epic-stack/docs/monitoring.md index ec354fc8..d43063b9 100644 --- a/examples/federation/epic-stack/docs/monitoring.md +++ b/examples/federation/epic-stack/docs/monitoring.md @@ -20,7 +20,7 @@ You can sign up for Sentry and create a Remix project from visiting [this url](https://sentry.io/signup/?project_platform=javascript-remix) and filling out the signup form. -## Setting up the sentry-vite plugin +## Setting up Sentry releases Once you see the onboarding page which has the DSN, copy that somewhere (this becomes `SENTRY_DSN`). Now, set the sentry dsn secret in Fly.io to be used as an @@ -49,7 +49,7 @@ for your project under `Organization > Projects > Project > Name` (`SENTRY_PROJECT`). In the 'build' section of the [Dockerfile](../other/Dockerfile), there is an -example of how to pass `SENTRY_AUTH_TOKEN` secret, so it is available to Vite +example of how to pass `SENTRY_AUTH_TOKEN` secret, so it is available to Rsbuild when `npm run build` is run. You may also uncomment and hard code your `SENTRY_ORG` and `SENTRY_PROJECT` values. Setup up your secrets in [GitHub Actions secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions). @@ -60,8 +60,4 @@ workflow. Note that these do not need to be added to the [`env.server`](../app/utils/env.server.ts) env vars schema, as they are only used during the build and not the runtime. -The Sentry Vite plugin in [`vite.config.ts`](../vite.config.ts) will create -sentry releases for you and automatically associate commits during the vite -build once the `SENTRY_AUTH_TOKEN` is set. In this setup we have utilized a -simple strategy for naming releases of using the commit sha, passed in as a -build arg via the GitHub action workflow. +Use your deployment workflow to create Sentry releases and associate commits once `SENTRY_AUTH_TOKEN` is set. A common release name is the commit sha passed through the GitHub action workflow. diff --git a/examples/federation/epic-stack/docs/routing.md b/examples/federation/epic-stack/docs/routing.md index 98022aac..7f12737a 100644 --- a/examples/federation/epic-stack/docs/routing.md +++ b/examples/federation/epic-stack/docs/routing.md @@ -4,7 +4,7 @@ The Epic Stack uses file-based routing with Remix. However, it's not using the built-in routing convention of Remix and instead is using [remix-flat-routes](https://github.com/kiliman/remix-flat-routes) which is a special implementation of the Remix convention that adds a few features. You'll -find it configured for the application in the `vite.config.js` file at the root +find it configured for the application in the `rsbuild.config.ts` file at the root of the app. Specifically "hybrid routes." We'll defer to the `remix-flat-routes` documentation for specifics, but an diff --git a/examples/federation/epic-stack/docs/seo.md b/examples/federation/epic-stack/docs/seo.md index b5de558c..5314f648 100644 --- a/examples/federation/epic-stack/docs/seo.md +++ b/examples/federation/epic-stack/docs/seo.md @@ -17,24 +17,18 @@ from the `@nasa-gcn/remix-seo` docs: ```tsx // routes/blog/_layout.tsx import { type SEOHandle } from '@nasa-gcn/remix-seo' -import { serverOnly$ } from 'vite-env-only/macros' export const handle: SEOHandle = { - getSitemapEntries: serverOnly$(async (request) => { + async getSitemapEntries(request) { const blogs = await db.blog.findMany() return blogs.map((blog) => { return { route: `/blog/${blog.slug}`, priority: 0.7 } }) - }), + }, } ``` -Note the use of -[`vite-env-only/macros`](https://github.com/pcattori/vite-env-only). This is -because `handle` is a route export object that goes in both the client as well -as the server, but our sitemap function should only be run on the server. So we -use `vite-env-only/macros` to make sure the function is removed for the client -build. Support for this is pre-configured in the `vite.config.ts` file. +Keep sitemap data server-only by loading it from server route modules or other server-only files. ```tsx // in your routes/url-that-doesnt-need-sitemap diff --git a/examples/prerender/package.json b/examples/prerender/package.json index e9347b8b..06143ab6 100644 --- a/examples/prerender/package.json +++ b/examples/prerender/package.json @@ -34,8 +34,6 @@ "string-replace-loader": "^3.3.0", "tailwindcss": "^4.1.18", "text-encoder-lite": "^2.0.0", - "typescript": "^5.9.3", - "vite": "^7.3.1", - "vite-tsconfig-paths": "^6.0.5" + "typescript": "^5.9.3" } } diff --git a/examples/prerender/tsconfig.json b/examples/prerender/tsconfig.json index dc391a45..0fb4dbe4 100644 --- a/examples/prerender/tsconfig.json +++ b/examples/prerender/tsconfig.json @@ -7,7 +7,7 @@ ], "compilerOptions": { "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["node", "vite/client"], + "types": ["node"], "target": "ES2022", "module": "ES2022", "moduleResolution": "bundler", diff --git a/examples/react-router-8/.gitignore b/examples/react-router-8/.gitignore new file mode 100644 index 00000000..c08251ce --- /dev/null +++ b/examples/react-router-8/.gitignore @@ -0,0 +1,6 @@ +node_modules + +/.cache +/build +.env +.react-router diff --git a/examples/react-router-8/README.md b/examples/react-router-8/README.md new file mode 100644 index 00000000..5dedefcd --- /dev/null +++ b/examples/react-router-8/README.md @@ -0,0 +1,5 @@ +# React Router 8 Default Template + +This example starts as a direct copy of upstream React Router's `integration/helpers/vite-8-template` (collapsed into this repo's corpus as `helpers/rsbuild-template`) and swaps the Vite config/scripts for `rsbuild-plugin-react-router`. + +Run `pnpm --filter react-router-8-default-template test:e2e` to exercise development and production browser flows. diff --git a/examples/react-router-8/app/root.tsx b/examples/react-router-8/app/root.tsx new file mode 100644 index 00000000..b36392b4 --- /dev/null +++ b/examples/react-router-8/app/root.tsx @@ -0,0 +1,19 @@ +import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router"; + +export default function App() { + return ( + + + + + + + + + + + + + + ); +} diff --git a/examples/react-router-8/app/routes.ts b/examples/react-router-8/app/routes.ts new file mode 100644 index 00000000..4c05936c --- /dev/null +++ b/examples/react-router-8/app/routes.ts @@ -0,0 +1,4 @@ +import { type RouteConfig } from "@react-router/dev/routes"; +import { flatRoutes } from "@react-router/fs-routes"; + +export default flatRoutes() satisfies RouteConfig; diff --git a/examples/react-router-8/app/routes/_index.tsx b/examples/react-router-8/app/routes/_index.tsx new file mode 100644 index 00000000..ecfc25c6 --- /dev/null +++ b/examples/react-router-8/app/routes/_index.tsx @@ -0,0 +1,16 @@ +import type { MetaFunction } from "react-router"; + +export const meta: MetaFunction = () => { + return [ + { title: "New React Router App" }, + { name: "description", content: "Welcome to React Router!" }, + ]; +}; + +export default function Index() { + return ( +
+

Welcome to React Router

+
+ ); +} diff --git a/examples/react-router-8/env.d.ts b/examples/react-router-8/env.d.ts new file mode 100644 index 00000000..4db3c0f8 --- /dev/null +++ b/examples/react-router-8/env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/examples/react-router-8/package.json b/examples/react-router-8/package.json new file mode 100644 index 00000000..ee0d34b1 --- /dev/null +++ b/examples/react-router-8/package.json @@ -0,0 +1,40 @@ +{ + "name": "react-router-8-default-template", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "dev": "NODE_OPTIONS=\"--experimental-vm-modules --experimental-global-webcrypto\" rsbuild dev --port 3020 --host 127.0.0.1", + "build": "rsbuild build", + "start": "HOST=127.0.0.1 PORT=3020 react-router-serve ./build/server/static/js/app.js", + "test:e2e": "pnpm run test:e2e:dev && pnpm run build && pnpm run test:e2e:prod", + "test:e2e:dev": "playwright test", + "test:e2e:prod": "cross-env RR8_E2E_MODE=production playwright test", + "typecheck": "react-router typegen && tsc" + }, + "dependencies": { + "@react-router/fs-routes": "^8.0.1", + "@react-router/node": "^8.0.1", + "@react-router/serve": "^8.0.1", + "@vanilla-extract/css": "^1.20.1", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-router": "^8.0.1", + "serialize-javascript": "^6.0.1" + }, + "devDependencies": { + "@playwright/test": "^1.58.0", + "@react-router/dev": "^8.0.1", + "@rsbuild/core": "2.1.0", + "@rsbuild/plugin-react": "2.1.0", + "@types/node": "^25.0.10", + "@types/react": "^19.2.10", + "@types/react-dom": "^19.2.3", + "cross-env": "^10.1.0", + "rsbuild-plugin-react-router": "workspace:*", + "typescript": "^5.9.3" + }, + "engines": { + "node": ">=22.22.0" + } +} diff --git a/examples/react-router-8/playwright.config.ts b/examples/react-router-8/playwright.config.ts new file mode 100644 index 00000000..304b899b --- /dev/null +++ b/examples/react-router-8/playwright.config.ts @@ -0,0 +1,27 @@ +import { defineConfig, devices } from '@playwright/test'; + +const isProduction = process.env.RR8_E2E_MODE === 'production'; + +export default defineConfig({ + testDir: './tests/e2e', + timeout: 30_000, + expect: { + timeout: 10_000, + }, + use: { + baseURL: 'http://127.0.0.1:3020', + trace: 'on-first-retry', + }, + webServer: { + command: isProduction ? 'pnpm run start' : 'pnpm run dev', + url: 'http://127.0.0.1:3020', + reuseExistingServer: !process.env.CI, + timeout: 60_000, + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}); diff --git a/examples/react-router-8/public/favicon.ico b/examples/react-router-8/public/favicon.ico new file mode 100644 index 00000000..5dbdfcdd Binary files /dev/null and b/examples/react-router-8/public/favicon.ico differ diff --git a/examples/react-router-8/react-router.config.ts b/examples/react-router-8/react-router.config.ts new file mode 100644 index 00000000..f26e5657 --- /dev/null +++ b/examples/react-router-8/react-router.config.ts @@ -0,0 +1,6 @@ +export default { + ssr: true, + routeDiscovery: { mode: 'initial' }, + splitRouteModules: true, + subResourceIntegrity: true, +}; diff --git a/examples/react-router-8/rsbuild.config.ts b/examples/react-router-8/rsbuild.config.ts new file mode 100644 index 00000000..36cc0bc3 --- /dev/null +++ b/examples/react-router-8/rsbuild.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; +import { pluginReactRouter } from 'rsbuild-plugin-react-router'; + +export default defineConfig({ + plugins: [pluginReactRouter(), pluginReact()], +}); diff --git a/examples/react-router-8/tests/e2e/react-router-8.test.ts b/examples/react-router-8/tests/e2e/react-router-8.test.ts new file mode 100644 index 00000000..ea1187b1 --- /dev/null +++ b/examples/react-router-8/tests/e2e/react-router-8.test.ts @@ -0,0 +1,44 @@ +import { expect, test } from '@playwright/test'; + +test('renders the React Router 8 default template without browser errors', async ({ + page, +}) => { + const browserProblems: string[] = []; + page.on('console', message => { + if (message.type() === 'error') { + browserProblems.push(`console error: ${message.text()}`); + } + }); + page.on('pageerror', error => { + browserProblems.push(`page error: ${error.message}`); + }); + page.on('response', response => { + if (response.status() >= 500) { + browserProblems.push(`${response.status()} response: ${response.url()}`); + } + }); + page.on('requestfailed', request => { + if (request.resourceType() !== 'websocket') { + browserProblems.push( + `${request.method()} ${request.url()} failed: ${ + request.failure()?.errorText ?? 'unknown error' + }` + ); + } + }); + + const response = await page.goto('/'); + expect(response?.ok()).toBe(true); + await expect( + page.getByRole('heading', { name: 'Welcome to React Router' }) + ).toBeVisible(); + await expect(page).toHaveTitle('New React Router App'); + await page.waitForFunction( + () => + (window as Window & { __reactRouterRouteModules?: unknown }) + .__reactRouterRouteModules !== undefined + ); + await page.waitForTimeout(250); + + expect(browserProblems).toEqual([]); +}); diff --git a/examples/react-router-8/tsconfig.json b/examples/react-router-8/tsconfig.json new file mode 100644 index 00000000..da8e2e8a --- /dev/null +++ b/examples/react-router-8/tsconfig.json @@ -0,0 +1,38 @@ +{ + "include": [ + "env.d.ts", + "**/*.ts", + "**/*.tsx", + ".react-router/types/**/*" + ], + "compilerOptions": { + "lib": [ + "DOM", + "DOM.Iterable", + "ES2022" + ], + "types": [ + "node" + ], + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "esModuleInterop": true, + "jsx": "react-jsx", + "resolveJsonModule": true, + "allowJs": true, + "baseUrl": ".", + "paths": { + "~/*": [ + "./app/*" + ] + }, + "noEmit": true, + "rootDirs": [ + ".", + ".react-router/types/" + ], + "skipLibCheck": true, + "strict": true + } +} diff --git a/examples/rsc-mode/.gitignore b/examples/rsc-mode/.gitignore new file mode 100644 index 00000000..74818a80 --- /dev/null +++ b/examples/rsc-mode/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/node_modules/ + +# React Router +/.react-router/ +/build/ +test-results +.rspack-profile-* diff --git a/examples/rsc-mode/README.md b/examples/rsc-mode/README.md new file mode 100644 index 00000000..6ac02f3a --- /dev/null +++ b/examples/rsc-mode/README.md @@ -0,0 +1,25 @@ +# RSC Mode Example + +This example is a small React Router RSC Framework Mode app wired for `rsbuild-plugin-react-router`. + +It includes: + +- `pluginReactRouter({ rsc: true })` in `rsbuild.config.ts` +- RSC-safe React Router config without `splitRouteModules` or `subResourceIntegrity` +- A server-first index route using `ServerComponent` +- A loader that returns a React element rendered on the server +- A `"use client"` island mounted inside the server-first route +- A client-first route for soft navigation coverage +- A Playwright smoke test for dev and production mode + +## Commands + +```sh +pnpm run dev +pnpm run build +pnpm run test:e2e +``` + +## Notes + +This example uses `react-server-dom-rspack` and `rsbuild-plugin-rsc` for a Rsbuild/Rspack-native RSC runtime. diff --git a/examples/rsc-mode/app/client-counter.tsx b/examples/rsc-mode/app/client-counter.tsx new file mode 100644 index 00000000..d14946b6 --- /dev/null +++ b/examples/rsc-mode/app/client-counter.tsx @@ -0,0 +1,17 @@ +'use client'; + +import { useState } from 'react'; + +export function ClientCounter() { + const [count, setCount] = useState(0); + + return ( + + ); +} diff --git a/examples/rsc-mode/app/root.tsx b/examples/rsc-mode/app/root.tsx new file mode 100644 index 00000000..ac47312f --- /dev/null +++ b/examples/rsc-mode/app/root.tsx @@ -0,0 +1,64 @@ +import type { Route } from './+types/root'; +import { + isRouteErrorResponse, + Link, + Links, + Meta, + Outlet, +} from 'react-router'; + +import './styles.css'; + +export function meta() { + return [ + { title: 'Rsbuild RSC example' }, + { + name: 'description', + content: 'React Router RSC Framework Mode with Rsbuild', + }, + ]; +} + +export function ServerLayout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + +
+ + RSC Mode + + +
+
{children}
+ + + ); +} + +export function ServerComponent() { + return ; +} + +export function ServerErrorBoundary({ error }: Route.ServerErrorBoundaryProps) { + const message = isRouteErrorResponse(error) + ? `${error.status} ${error.statusText}` + : error instanceof Error + ? error.message + : 'Unknown error'; + + return ( +
+

Route error

+

{message}

+
+ ); +} diff --git a/examples/rsc-mode/app/routes.ts b/examples/rsc-mode/app/routes.ts new file mode 100644 index 00000000..9f976145 --- /dev/null +++ b/examples/rsc-mode/app/routes.ts @@ -0,0 +1,6 @@ +import { type RouteConfig, index, route } from '@react-router/dev/routes'; + +export default [ + index('routes/_index.tsx'), + route('client', 'routes/client.tsx'), +] satisfies RouteConfig; diff --git a/examples/rsc-mode/app/routes/_index.tsx b/examples/rsc-mode/app/routes/_index.tsx new file mode 100644 index 00000000..555f533c --- /dev/null +++ b/examples/rsc-mode/app/routes/_index.tsx @@ -0,0 +1,32 @@ +import type { Route } from './+types/_index'; +import { Link } from 'react-router'; + +import { ClientCounter } from '~/client-counter'; +import { getRscShowcase } from '~/rsc-data'; + +export async function loader() { + return getRscShowcase(); +} + +export function ServerComponent({ loaderData }: Route.ServerComponentProps) { + return ( +
+
+

Rsbuild React Router RSC

+

{loaderData.message}

+

{loaderData.element}

+
+ +
+

+ This route renders through React Router RSC Framework Mode and mounts a + small client island inside the server-first route. +

+ + + Visit the client-first route + +
+
+ ); +} diff --git a/examples/rsc-mode/app/routes/client.tsx b/examples/rsc-mode/app/routes/client.tsx new file mode 100644 index 00000000..a570d7ef --- /dev/null +++ b/examples/rsc-mode/app/routes/client.tsx @@ -0,0 +1,16 @@ +export function meta() { + return [{ title: 'Rsbuild RSC example' }]; +} + +export default function ClientRoute() { + return ( +
+

Client-first route

+

+ This route uses the conventional default route component export so the + example exercises navigation between server-first and client-first + routes. +

+
+ ); +} diff --git a/examples/rsc-mode/app/rsc-data.tsx b/examples/rsc-mode/app/rsc-data.tsx new file mode 100644 index 00000000..4cb9e0c8 --- /dev/null +++ b/examples/rsc-mode/app/rsc-data.tsx @@ -0,0 +1,10 @@ +export async function getRscShowcase() { + return { + message: 'Message rendered by an RSC loader', + element: ( + + React element returned from the server loader + + ), + }; +} diff --git a/examples/rsc-mode/app/styles.css b/examples/rsc-mode/app/styles.css new file mode 100644 index 00000000..66e37d6c --- /dev/null +++ b/examples/rsc-mode/app/styles.css @@ -0,0 +1,150 @@ +:root { + color: #172033; + background: #f7f9fc; + font-family: + Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", + sans-serif; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; + background: + radial-gradient(circle at 20% 10%, rgba(69, 125, 255, 0.16), transparent 28rem), + linear-gradient(135deg, #f7f9fc 0%, #ffffff 46%, #eef6f1 100%); +} + +a { + color: inherit; +} + +.site-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1.5rem; + padding: 1.25rem clamp(1rem, 4vw, 3rem); + border-bottom: 1px solid rgba(23, 32, 51, 0.12); + background: rgba(255, 255, 255, 0.82); +} + +.brand { + color: #0f5132; + font-size: 1.125rem; + font-weight: 800; + text-decoration: none; +} + +nav { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: 0.75rem; +} + +nav a, +.text-link { + color: #274c77; + font-weight: 700; + text-decoration: none; +} + +nav a:hover, +.text-link:hover { + text-decoration: underline; +} + +.page-shell { + width: min(100% - 2rem, 1040px); + margin: 0 auto; + padding: clamp(2rem, 8vw, 5rem) 0; +} + +.hero-panel { + display: grid; + grid-template-columns: minmax(0, 1.15fr) minmax(18rem, 0.85fr); + gap: clamp(1.5rem, 5vw, 4rem); + align-items: center; +} + +.hero-copy h1, +.client-route h1 { + max-width: 12ch; + margin: 0 0 1rem; + color: #172033; + font-size: clamp(2.5rem, 8vw, 5.25rem); + line-height: 0.96; +} + +.hero-copy p, +.client-route p, +.interaction-panel p { + color: #475569; + font-size: 1.05rem; + line-height: 1.7; +} + +.server-element { + margin-top: 1.5rem; +} + +.server-element strong { + display: inline-block; + border-left: 4px solid #0f766e; + padding: 0.5rem 0 0.5rem 1rem; + color: #0f5132; +} + +.interaction-panel { + display: grid; + gap: 1rem; + padding: 1.5rem; + border: 1px solid rgba(23, 32, 51, 0.12); + border-radius: 8px; + background: rgba(255, 255, 255, 0.88); + box-shadow: 0 24px 60px rgba(36, 54, 92, 0.12); +} + +.counter-button { + min-height: 2.75rem; + border: 0; + border-radius: 6px; + padding: 0.7rem 1rem; + background: #172033; + color: white; + font: inherit; + font-weight: 800; + cursor: pointer; +} + +.counter-button:hover { + background: #274c77; +} + +.client-route { + max-width: 720px; +} + +@media (max-width: 720px) { + .site-header, + .hero-panel { + align-items: flex-start; + } + + .hero-panel { + grid-template-columns: 1fr; + } + + .site-header { + flex-direction: column; + } + + nav { + justify-content: flex-start; + } +} diff --git a/examples/rsc-mode/package.json b/examples/rsc-mode/package.json new file mode 100644 index 00000000..77bec69d --- /dev/null +++ b/examples/rsc-mode/package.json @@ -0,0 +1,41 @@ +{ + "name": "rsc-mode-example", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "dev": "NODE_OPTIONS=\"--experimental-vm-modules --experimental-global-webcrypto\" rsbuild dev --port 3021 --host 127.0.0.1", + "build": "rsbuild build", + "start": "HOST=127.0.0.1 PORT=3021 node start.js", + "typecheck": "react-router typegen && tsc", + "test:e2e": "pnpm run test:e2e:dev && pnpm run build && pnpm run test:e2e:prod", + "test:e2e:dev": "playwright test", + "test:e2e:prod": "cross-env RSC_E2E_MODE=production playwright test" + }, + "dependencies": { + "@react-router/node": "^8.0.1", + "@react-router/serve": "^8.0.1", + "@remix-run/node-fetch-server": "^0.13.3", + "express": "^4.22.2", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-router": "^8.0.1", + "react-server-dom-rspack": "0.0.2" + }, + "devDependencies": { + "@playwright/test": "^1.58.0", + "@react-router/dev": "^8.0.1", + "@rsbuild/core": "2.1.0", + "@rsbuild/plugin-react": "2.1.0", + "@types/node": "^25.0.10", + "@types/react": "^19.2.10", + "@types/react-dom": "^19.2.3", + "cross-env": "^10.1.0", + "rsbuild-plugin-react-router": "workspace:*", + "rsbuild-plugin-rsc": "^0.1.1", + "typescript": "^5.9.3" + }, + "engines": { + "node": ">=22.22.0" + } +} diff --git a/examples/rsc-mode/playwright.config.ts b/examples/rsc-mode/playwright.config.ts new file mode 100644 index 00000000..923c5bc8 --- /dev/null +++ b/examples/rsc-mode/playwright.config.ts @@ -0,0 +1,27 @@ +import { defineConfig, devices } from '@playwright/test'; + +const isProduction = process.env.RSC_E2E_MODE === 'production'; + +export default defineConfig({ + testDir: './tests/e2e', + timeout: 30_000, + expect: { + timeout: 10_000, + }, + use: { + baseURL: 'http://127.0.0.1:3021', + trace: 'on-first-retry', + }, + webServer: { + command: isProduction ? 'pnpm run start' : 'pnpm run dev', + url: 'http://127.0.0.1:3021', + reuseExistingServer: !process.env.CI, + timeout: 120_000, + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], +}); diff --git a/examples/rsc-mode/react-router.config.ts b/examples/rsc-mode/react-router.config.ts new file mode 100644 index 00000000..564b76a0 --- /dev/null +++ b/examples/rsc-mode/react-router.config.ts @@ -0,0 +1,5 @@ +import type { Config } from '@react-router/dev/config'; + +export default { + routeDiscovery: { mode: 'initial' }, +} satisfies Config; diff --git a/examples/rsc-mode/rsbuild.config.ts b/examples/rsc-mode/rsbuild.config.ts new file mode 100644 index 00000000..ebb03455 --- /dev/null +++ b/examples/rsc-mode/rsbuild.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from '@rsbuild/core'; +import { pluginReact } from '@rsbuild/plugin-react'; +import { pluginReactRouter } from 'rsbuild-plugin-react-router'; + +type ReactRouterRscOptions = NonNullable< + Parameters[0] +> & { + rsc: true; +}; + +const reactRouterRscOptions = { + rsc: true, +} satisfies ReactRouterRscOptions; + +export default defineConfig({ + plugins: [pluginReact(), pluginReactRouter(reactRouterRscOptions)], +}); diff --git a/examples/rsc-mode/start.js b/examples/rsc-mode/start.js new file mode 100644 index 00000000..e1ef7adb --- /dev/null +++ b/examples/rsc-mode/start.js @@ -0,0 +1,23 @@ +import { createRequestListener } from '@remix-run/node-fetch-server'; +import express from 'express'; +import build from './build/server/index.js'; + +const app = express(); +const port = Number(process.env.PORT ?? 3021); +const host = process.env.HOST ?? '127.0.0.1'; + +app.use('/', express.static('build/client', { index: false })); + +app.get('/.well-known/appspecific/com.chrome.devtools.json', (_req, res) => { + res.status(404).send('Not Found'); +}); + +if (typeof build?.fetch !== 'function') { + throw new Error('Expected build/server/index.js to export default.fetch'); +} + +app.use(createRequestListener(build.fetch)); + +app.listen(port, host, () => { + console.log(`Server listening on http://${host}:${port}`); +}); diff --git a/examples/rsc-mode/tests/e2e/rsc-mode.test.ts b/examples/rsc-mode/tests/e2e/rsc-mode.test.ts new file mode 100644 index 00000000..1653a9f0 --- /dev/null +++ b/examples/rsc-mode/tests/e2e/rsc-mode.test.ts @@ -0,0 +1,59 @@ +import { expect, test } from '@playwright/test'; + +test('renders server-first RSC routes and hydrates the client island', async ({ + page, +}) => { + const browserProblems: string[] = []; + page.on('console', message => { + if (message.type() === 'error') { + browserProblems.push(`console error: ${message.text()}`); + } + }); + page.on('pageerror', error => { + browserProblems.push(`page error: ${error.message}`); + }); + page.on('response', response => { + if (response.status() >= 500) { + browserProblems.push(`${response.status()} response: ${response.url()}`); + } + }); + page.on('requestfailed', request => { + if (request.resourceType() !== 'websocket') { + browserProblems.push( + `${request.method()} ${request.url()} failed: ${ + request.failure()?.errorText ?? 'unknown error' + }` + ); + } + }); + + const response = await page.goto('/'); + expect(response?.ok()).toBe(true); + + await expect( + page.getByRole('heading', { name: 'Rsbuild React Router RSC' }) + ).toBeVisible(); + await expect(page.getByTestId('server-message')).toHaveText( + 'Message rendered by an RSC loader' + ); + await expect(page.getByTestId('server-element')).toHaveText( + 'React element returned from the server loader' + ); + expect(await page.content()).toMatch( + /\(self\.__FLIGHT_DATA\|\|=\[\]\)\.push\(/ + ); + + const counter = page.getByRole('button', { name: /Client island count:/ }); + await expect(counter).toHaveText('Client island count: 0'); + await counter.click(); + await expect(counter).toHaveText('Client island count: 1'); + + await page.getByRole('link', { name: 'Client route' }).click(); + await expect( + page.getByRole('heading', { name: 'Client-first route' }) + ).toBeVisible(); + await expect(page).toHaveTitle('Rsbuild RSC example'); + + await page.waitForTimeout(250); + expect(browserProblems).toEqual([]); +}); diff --git a/examples/rsc-mode/tsconfig.json b/examples/rsc-mode/tsconfig.json new file mode 100644 index 00000000..2e0923ff --- /dev/null +++ b/examples/rsc-mode/tsconfig.json @@ -0,0 +1,22 @@ +{ + "include": ["**/*.ts", "**/*.tsx", ".react-router/types/**/*"], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "types": ["node", "@react-router/dev/rsc-types"], + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "rootDirs": [".", "./.react-router/types"], + "baseUrl": ".", + "paths": { + "~/*": ["./app/*"] + }, + "esModuleInterop": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true + } +} diff --git a/examples/spa-mode/package.json b/examples/spa-mode/package.json index 38eb796a..6ce2fee9 100644 --- a/examples/spa-mode/package.json +++ b/examples/spa-mode/package.json @@ -34,8 +34,6 @@ "string-replace-loader": "^3.3.0", "tailwindcss": "^4.1.18", "text-encoder-lite": "^2.0.0", - "typescript": "^5.9.3", - "vite": "^7.3.1", - "vite-tsconfig-paths": "^6.0.5" + "typescript": "^5.9.3" } } diff --git a/examples/spa-mode/tsconfig.json b/examples/spa-mode/tsconfig.json index dc391a45..7c424432 100644 --- a/examples/spa-mode/tsconfig.json +++ b/examples/spa-mode/tsconfig.json @@ -6,16 +6,27 @@ ".react-router/types/**/*" ], "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["node", "vite/client"], + "lib": [ + "DOM", + "DOM.Iterable", + "ES2022" + ], + "types": [ + "node" + ], "target": "ES2022", "module": "ES2022", "moduleResolution": "bundler", "jsx": "react-jsx", - "rootDirs": [".", "./.react-router/types"], + "rootDirs": [ + ".", + "./.react-router/types" + ], "baseUrl": ".", "paths": { - "~/*": ["./app/*"] + "~/*": [ + "./app/*" + ] }, "esModuleInterop": true, "verbatimModuleSyntax": true, diff --git a/package.json b/package.json index bffd2486..0d6d48bb 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,18 @@ "types": "./dist/templates/entry.server.d.ts", "import": "./dist/templates/entry.server.js", "require": "./dist/templates/entry.server.cjs" + }, + "./templates/entry.rsc": { + "types": "./dist/templates/entry.rsc.d.ts", + "import": "./dist/templates/entry.rsc.js" + }, + "./templates/entry.rsc.client": { + "types": "./dist/templates/entry.rsc.client.d.ts", + "import": "./dist/templates/entry.rsc.client.js" + }, + "./templates/entry.rsc.ssr": { + "types": "./dist/templates/entry.rsc.ssr.d.ts", + "import": "./dist/templates/entry.rsc.ssr.js" } }, "main": "./dist/index.cjs", @@ -58,14 +70,21 @@ "bench:full": "node scripts/bench-builds.mts --profile full --iterations 5 --warmup 1 --clean build --format both --out .benchmark/results/full", "bench:large": "node scripts/bench-builds.mts --profile large --iterations 1 --warmup 0 --clean cold --format both --out .benchmark/results/large", "bench:synthetic-app": "node scripts/bench-synthetic-app.mjs", - "e2e": "pnpm build && pnpm test:package-interop && pnpm --filter './examples/{default-template,spa-mode,prerender,custom-node-server,cloudflare,client-only}' test:e2e", + "e2e": "pnpm build && pnpm e2e:no-build", + "e2e:no-build": "pnpm test:package-interop && pnpm --filter './examples/{default-template,spa-mode,prerender,custom-node-server,cloudflare,client-only,react-router-8,rsc-mode}' test:e2e", "dev": "rslib build --watch", - "test": "rstest run", + "test": "pnpm typecheck && rstest run", + "typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json && pnpm --filter integration typecheck && pnpm --filter integration-rsbuild-template typecheck && pnpm --filter integration-rsc-framework typecheck && pnpm --filter integration-rsc-preview typecheck", "test:watch": "rstest watch", "test:coverage": "rstest run --coverage", "test:core": "rstest run -c ./rstest.config.ts", "test:core:watch": "rstest watch -c ./rstest.config.ts", "test:package-interop": "node scripts/test-package-interop.mts", + "test:react-router-framework": "pnpm build && playwright test --config tests/react-router-framework/integration/playwright.config.ts", + "test:react-router-framework:failfast": "pnpm build && pnpm test:react-router-framework:failfast:no-build", + "test:react-router-framework:failfast:no-build": "RR_FRAMEWORK_MAX_WORKERS=2 playwright test --config tests/react-router-framework/integration/playwright.config.ts tests/react-router-framework/integration/hmr-hdr-test.ts --workers=2 --max-failures=1 --retries=0 --reporter=line && RR_FRAMEWORK_MAX_WORKERS=2 playwright test --config tests/react-router-framework/integration/playwright.config.ts tests/react-router-framework/integration/hmr-hdr-rsc-test.ts --workers=2 --max-failures=1 --retries=0 --reporter=line && RR_FRAMEWORK_MAX_WORKERS=4 playwright test --config tests/react-router-framework/integration/playwright.config.ts --grep-invert 'HMR & HDR' --workers=4 --max-failures=1 --retries=0 --reporter=line", + "test:react-router-framework:smoke": "pnpm build && playwright test --config tests/react-router-framework/integration/playwright.config.ts use-route-test.ts loader-test.ts", + "check:react-router-framework-upstream": "node scripts/check-react-router-framework-upstream.mjs", "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"", "format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"", "changeset": "changeset", @@ -74,8 +93,8 @@ "release:local": "pnpm build && changeset version && changeset publish && git add . && git commit -m \"chore: version packages\" && git push && git push --tags" }, "dependencies": { - "@react-router/node": "^7.13.0", - "@remix-run/node-fetch-server": "^0.13.0", + "@react-router/node": "^7.13.0 || ^8.0.0", + "@remix-run/node-fetch-server": "^0.13.3", "@rspack/plugin-react-refresh": "^2.0.2", "execa": "^9.6.1", "fs-extra": "11.3.3", @@ -90,39 +109,75 @@ }, "devDependencies": { "@changesets/cli": "^2.29.8", + "@playwright/test": "^1.61.1", "@react-router/dev": "^8.0.1", + "@react-router/express": "^8.0.1", + "@react-router/fs-routes": "^8.0.1", + "@react-router/remix-routes-option-adapter": "^8.0.1", + "@react-router/serve": "^8.0.1", "@rsbuild/config": "workspace:*", "@rsbuild/core": "2.1.0", + "@rsbuild/plugin-less": "1.6.4", + "@rsbuild/plugin-mdx": "^1.1.3", "@rsbuild/plugin-react": "2.1.0", + "@rsbuild/plugin-sass": "1.5.3", + "@rsbuild/plugin-svgr": "2.0.4", + "@rsbuild/plugin-tailwindcss": "2.0.3", "@rslib/core": "^0.22.1", "@rspack/core": "2.1.0", - "@rstest/core": "^0.8.1", - "@rstest/coverage-istanbul": "^0.2.0", + "@rstest/core": "^0.11.4", + "@rstest/coverage-istanbul": "^0.11.4", "@swc/helpers": "^0.5.23", "@types/fs-extra": "11.0.4", "@types/jsesc": "^3.0.3", "@types/node": "^25.0.10", "@types/react": "^19.2.10", "@types/react-dom": "^19.2.3", - "effect": "^3.21.4", + "@types/webpack-env": "^1.18.8", + "@vanilla-extract/css": "^1.20.1", + "cheerio": "^1.2.0", + "cross-spawn": "^7.0.6", + "dedent": "^1.7.2", + "effect": "^3.22.0", "es-module-lexer": "1.7.0", + "express": "^4.22.2", + "fast-glob": "^3.3.3", + "get-port": "7.1.0", "kill-port": "^2.0.1", "pkg-pr-new": "^0.0.75", "playwright": "^1.61.1", "prettier": "3.8.1", "react": "^19.2.4", "react-dom": "^19.2.4", - "react-router": "^7.13.0", - "react-router-dom": "^7.13.0", - "typescript": "^5.9.3" + "react-router": "^7.18.0", + "react-router-dom": "^7.18.0", + "react-server-dom-rspack": "0.0.2", + "rsbuild-plugin-rsc": "^0.1.1", + "semver": "^7.8.5", + "shelljs": "^0.10.0", + "strip-ansi": "^7.2.0", + "strip-indent": "^4.1.1", + "tailwindcss": "4.3.2", + "type-fest": "^5.7.0", + "typescript": "^5.9.3", + "wait-on": "^9.0.10" }, "peerDependencies": { "@react-router/dev": "^7.13.0 || ^8.0.0", - "@rsbuild/core": "^2.0.0" + "@rsbuild/core": "^2.0.0", + "react-router": "^7.13.0 || ^8.0.0", + "react-server-dom-rspack": "0.0.2", + "rsbuild-plugin-rsc": "^0.1.1" }, "peerDependenciesMeta": { "@react-router/dev": { "optional": true + }, + "react-server-dom-rspack": { + "optional": true + }, + "rsbuild-plugin-rsc": { + "optional": true } }, "publishConfig": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8827fb90..e12f8dac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,10 +12,10 @@ importers: .: dependencies: '@react-router/node': - specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + specifier: ^7.13.0 || ^8.0.0 + version: 7.14.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@remix-run/node-fetch-server': - specifier: ^0.13.0 + specifier: ^0.13.3 version: 0.13.3 '@rspack/plugin-react-refresh': specifier: ^2.0.2 @@ -54,30 +54,60 @@ importers: '@changesets/cli': specifier: ^2.29.8 version: 2.31.0(@types/node@25.0.10) + '@playwright/test': + specifier: ^1.61.1 + version: 1.61.1 '@react-router/dev': specifier: ^8.0.1 - version: 8.0.1(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + version: 8.1.0(@react-router/serve@8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + '@react-router/express': + specifier: ^8.0.1 + version: 8.1.0(express@4.22.2)(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/fs-routes': + specifier: ^8.0.1 + version: 8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3) + '@react-router/remix-routes-option-adapter': + specifier: ^8.0.1 + version: 8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3) + '@react-router/serve': + specifier: ^8.0.1 + version: 8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@rsbuild/config': specifier: workspace:* version: link:config '@rsbuild/core': specifier: 2.1.0 version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/plugin-less': + specifier: 1.6.4 + version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsbuild/plugin-mdx': + specifier: ^1.1.3 + version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0)) '@rsbuild/plugin-react': specifier: 2.1.0 version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + '@rsbuild/plugin-sass': + specifier: 1.5.3 + version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)) + '@rsbuild/plugin-svgr': + specifier: 2.0.4 + version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3) + '@rsbuild/plugin-tailwindcss': + specifier: 2.0.3 + version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) '@rslib/core': specifier: ^0.22.1 - version: 0.22.1(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3) + version: 0.22.1(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(typescript@5.9.3) '@rspack/core': specifier: 2.1.0 version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) '@rstest/core': - specifier: ^0.8.1 - version: 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)) + specifier: ^0.11.4 + version: 0.11.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)) '@rstest/coverage-istanbul': - specifier: ^0.2.0 - version: 0.2.0(@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0))) + specifier: ^0.11.4 + version: 0.11.4(@rstest/core@0.11.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))) '@swc/helpers': specifier: ^0.5.23 version: 0.5.23 @@ -96,12 +126,36 @@ importers: '@types/react-dom': specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.10) + '@types/webpack-env': + specifier: ^1.18.8 + version: 1.18.8 + '@vanilla-extract/css': + specifier: ^1.20.1 + version: 1.21.1 + cheerio: + specifier: ^1.2.0 + version: 1.2.0 + cross-spawn: + specifier: ^7.0.6 + version: 7.0.6 + dedent: + specifier: ^1.7.2 + version: 1.7.2 effect: - specifier: ^3.21.4 - version: 3.21.4 + specifier: ^3.22.0 + version: 3.22.0 es-module-lexer: specifier: 1.7.0 version: 1.7.0 + express: + specifier: ^4.22.2 + version: 4.22.2 + fast-glob: + specifier: ^3.3.3 + version: 3.3.3 + get-port: + specifier: 7.1.0 + version: 7.1.0 kill-port: specifier: ^2.0.1 version: 2.0.1 @@ -121,14 +175,41 @@ importers: specifier: ^19.2.4 version: 19.2.7(react@19.2.7) react-router: - specifier: ^7.13.0 - version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^7.18.0 + version: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react-router-dom: - specifier: ^7.13.0 - version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: ^7.18.0 + version: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-server-dom-rspack: + specifier: 0.0.2 + version: 0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + rsbuild-plugin-rsc: + specifier: ^0.1.1 + version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)) + semver: + specifier: ^7.8.5 + version: 7.8.5 + shelljs: + specifier: ^0.10.0 + version: 0.10.0 + strip-ansi: + specifier: ^7.2.0 + version: 7.2.0 + strip-indent: + specifier: ^4.1.1 + version: 4.1.1 + tailwindcss: + specifier: 4.3.2 + version: 4.3.2 + type-fest: + specifier: ^5.7.0 + version: 5.7.0 typescript: specifier: ^5.9.3 version: 5.9.3 + wait-on: + specifier: ^9.0.10 + version: 9.0.10 benchmarks/synthetic-web-bundler-benchmark: dependencies: @@ -189,22 +270,22 @@ importers: version: 7.29.0 '@react-router/dev': specifier: 7.14.0 - version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc))(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@6.0.1-rc)(vite@7.3.1(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc))(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@6.0.1-rc)(vite@7.3.1(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) '@rsbuild/core': specifier: 2.1.0 version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-babel': specifier: 2.0.0 - version: 2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)) + version: 2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)) '@rsbuild/plugin-react': specifier: 2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@rsbuild/plugin-svgr': specifier: 2.0.4 - version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc) + version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@6.0.1-rc) '@rsbuild/plugin-tailwindcss': specifier: 2.0.3 - version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) '@types/babel__core': specifier: 7.20.5 version: 7.20.5 @@ -237,7 +318,7 @@ importers: version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rslib/core': specifier: 0.22.1 - version: 0.22.1(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3) + version: 0.22.1(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(typescript@5.9.3) '@types/node': specifier: ^25.0.10 version: 25.0.10 @@ -249,13 +330,13 @@ importers: dependencies: '@react-router/express': specifier: ^7.13.0 - version: 7.13.0(express@4.22.2)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.13.0(express@5.2.1)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/node': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/serve': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) isbot: specifier: ^5.1.34 version: 5.1.34 @@ -267,20 +348,20 @@ importers: version: 19.2.7(react@19.2.7) react-router: specifier: ^7.13.0 - version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) devDependencies: '@playwright/test': specifier: ^1.61.1 version: 1.61.1 '@react-router/dev': specifier: ^7.13.0 - version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) '@rsbuild/core': specifier: 2.1.0 version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@types/node': specifier: ^25.0.10 version: 25.0.10 @@ -296,18 +377,15 @@ importers: typescript: specifier: ^5.9.3 version: 5.9.3 - vite: - specifier: ^7.3.1 - version: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) examples/cloudflare: dependencies: '@react-router/node': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/serve': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) isbot: specifier: ^5.1.34 version: 5.1.34 @@ -319,29 +397,29 @@ importers: version: 19.2.7(react@19.2.7) react-router: specifier: ^7.13.0 - version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) devDependencies: '@cloudflare/workers-types': specifier: ^4.20260127.0 - version: 4.20260628.1 + version: 4.20260702.1 '@playwright/test': specifier: ^1.61.1 version: 1.61.1 '@react-router/cloudflare': specifier: ^7.13.0 - version: 7.18.0(@cloudflare/workers-types@4.20260628.1)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.18.1(@cloudflare/workers-types@4.20260702.1)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/dev': specifier: ^7.13.0 - version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) '@rsbuild/core': specifier: 2.1.0 version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.1.18 - version: 4.3.1 + version: 4.3.2 '@types/node': specifier: ^25.0.10 version: 25.0.10 @@ -356,22 +434,22 @@ importers: version: link:../.. tailwindcss: specifier: ^4.1.18 - version: 4.1.18 + version: 4.3.1 typescript: specifier: ^5.9.3 version: 5.9.3 wrangler: specifier: ^4.61.0 - version: 4.105.0(@cloudflare/workers-types@4.20260628.1) + version: 4.107.0(@cloudflare/workers-types@4.20260702.1) examples/custom-node-server: dependencies: '@react-router/express': specifier: ^7.13.0 - version: 7.13.0(express@5.2.1)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.13.0(express@5.2.1)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/node': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) express: specifier: ^5.2.1 version: 5.2.1 @@ -386,26 +464,26 @@ importers: version: 19.2.7(react@19.2.7) react-router: specifier: ^7.13.0 - version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) devDependencies: '@playwright/test': specifier: ^1.61.1 version: 1.61.1 '@react-router/dev': specifier: ^7.13.0 - version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) '@rsbuild/core': specifier: 2.1.0 version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@rsdoctor/rspack-plugin': specifier: ^1.5.13 - version: 1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + version: 1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) '@tailwindcss/postcss': specifier: ^4.1.18 - version: 4.3.1 + version: 4.3.2 '@types/express': specifier: ^5.0.0 version: 5.0.6 @@ -423,7 +501,7 @@ importers: version: link:../.. tailwindcss: specifier: ^4.1.18 - version: 4.1.18 + version: 4.3.1 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -432,13 +510,13 @@ importers: dependencies: '@react-router/express': specifier: ^7.13.0 - version: 7.13.0(express@5.2.1)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.13.0(express@5.2.1)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/node': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/serve': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) isbot: specifier: ^5.1.34 version: 5.1.34 @@ -450,29 +528,29 @@ importers: version: 19.2.7(react@19.2.7) react-router: specifier: ^7.13.0 - version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) devDependencies: '@playwright/test': specifier: ^1.61.1 version: 1.61.1 '@react-router/dev': specifier: ^7.13.0 - version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) '@rsbuild/core': specifier: 2.1.0 version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-less': specifier: ^1.6.4 - version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@rsbuild/plugin-sass': specifier: ^1.5.3 - version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)) + version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)) '@tailwindcss/postcss': specifier: ^4.1.18 - version: 4.3.1 + version: 4.3.2 '@types/node': specifier: ^25.0.10 version: 25.0.10 @@ -487,28 +565,22 @@ importers: version: 10.1.0 react-router-devtools: specifier: ^6.2.0 - version: 6.2.1(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(solid-js@1.9.13)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) + version: 6.2.1(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(solid-js@1.9.14)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) rsbuild-plugin-react-router: specifier: workspace:* version: link:../.. string-replace-loader: specifier: ^3.3.0 - version: 3.3.0(webpack@5.108.1(lightningcss@1.32.0)) + version: 3.3.0(webpack@5.108.3(lightningcss@1.32.0)) tailwindcss: specifier: ^4.1.18 - version: 4.1.18 + version: 4.3.1 text-encoder-lite: specifier: ^2.0.0 version: 2.0.0 typescript: specifier: ^5.9.3 version: 5.9.3 - vite: - specifier: ^7.3.1 - version: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) - vite-tsconfig-paths: - specifier: ^6.0.5 - version: 6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) examples/epic-stack: dependencies: @@ -577,13 +649,13 @@ importers: version: 1.0.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@react-router/express': specifier: 7.13.0 - version: 7.13.0(express@5.2.1)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.13.0(express@5.2.1)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/node': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/remix-routes-option-adapter': specifier: 7.13.0 - version: 7.13.0(@react-router/dev@7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)))(typescript@5.9.3) + version: 7.13.0(@react-router/dev@7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3) '@remix-run/server-runtime': specifier: 2.17.4 version: 2.17.4(typescript@5.9.3) @@ -592,7 +664,7 @@ importers: version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: 2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@sentry/node': specifier: 10.37.0 version: 10.37.0 @@ -691,7 +763,7 @@ importers: version: 19.2.7(react@19.2.7) react-router: specifier: ^7.13.0 - version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) remix-auth: specifier: 4.2.0 version: 4.2.0 @@ -700,7 +772,7 @@ importers: version: 3.0.2(remix-auth@4.2.0) remix-utils: specifier: 9.0.0 - version: 9.0.0(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(@standard-schema/spec@1.1.0)(intl-parse-accept-language@1.0.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) + version: 9.0.0(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(@standard-schema/spec@1.1.0)(intl-parse-accept-language@1.0.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) rsbuild-plugin-react-router: specifier: workspace:* version: link:../.. @@ -728,16 +800,13 @@ importers: tailwindcss-radix: specifier: 4.0.2 version: 4.0.2(tailwindcss@4.1.18) - vite-env-only: - specifier: 3.0.3 - version: 3.0.3(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) zod: specifier: 3.25.76 version: 3.25.76 devDependencies: '@epic-web/config': specifier: 1.21.3 - version: 1.21.3(@testing-library/dom@10.4.1)(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/utils@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(prettier@3.8.1)(typescript@5.9.3) + version: 1.21.3(@testing-library/dom@10.4.1)(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/utils@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(prettier@3.8.1)(typescript@5.9.3) '@faker-js/faker': specifier: 10.2.0 version: 10.2.0 @@ -746,16 +815,13 @@ importers: version: 1.61.1 '@react-router/dev': specifier: ^7.13.0 - version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) '@rstest/core': specifier: 0.8.1 version: 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)) '@rstest/coverage-istanbul': specifier: 0.2.0 version: 0.2.0(@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0))) - '@sentry/vite-plugin': - specifier: 4.8.0 - version: 4.8.0(encoding@0.1.13) '@sly-cli/sly': specifier: 2.1.1 version: 2.1.1(typescript@5.9.3) @@ -764,7 +830,7 @@ importers: version: 0.0.0-insiders.565cd3e(postcss@8.5.16) '@tailwindcss/postcss': specifier: ^4.1.18 - version: 4.3.1 + version: 4.3.2 '@testing-library/dom': specifier: 10.4.1 version: 10.4.1 @@ -822,9 +888,6 @@ importers: '@types/source-map-support': specifier: 0.5.10 version: 0.5.10 - '@vitejs/plugin-react': - specifier: 5.1.2 - version: 5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) autoprefixer: specifier: 10.4.23 version: 10.4.23(postcss@8.5.16) @@ -870,9 +933,6 @@ importers: typescript: specifier: 5.9.3 version: 5.9.3 - vite: - specifier: 7.3.1 - version: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) examples/federation: devDependencies: @@ -911,13 +971,13 @@ importers: version: 0.6.1 '@module-federation/enhanced': specifier: 2.5.1 - version: 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/node': specifier: 2.7.44 - version: 2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.7.44(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/rsbuild-plugin': specifier: 2.5.1 - version: 2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@nasa-gcn/remix-seo': specifier: 2.0.1 version: 2.0.1(@remix-run/react@2.17.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(@remix-run/server-runtime@2.17.4(typescript@5.9.3)) @@ -959,13 +1019,13 @@ importers: version: 1.0.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@react-router/express': specifier: 7.13.0 - version: 7.13.0(express@5.2.1)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.13.0(express@5.2.1)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/node': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/remix-routes-option-adapter': specifier: 7.13.0 - version: 7.13.0(@react-router/dev@7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)))(typescript@5.9.3) + version: 7.13.0(@react-router/dev@7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3) '@remix-run/server-runtime': specifier: 2.17.4 version: 2.17.4(typescript@5.9.3) @@ -974,7 +1034,7 @@ importers: version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: 2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@sentry/node': specifier: 10.37.0 version: 10.37.0 @@ -1073,7 +1133,7 @@ importers: version: 19.2.7(react@19.2.7) react-router: specifier: ^7.13.0 - version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) remix-auth: specifier: 4.2.0 version: 4.2.0 @@ -1082,7 +1142,7 @@ importers: version: 3.0.2(remix-auth@4.2.0) remix-utils: specifier: 9.0.0 - version: 9.0.0(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(@standard-schema/spec@1.1.0)(intl-parse-accept-language@1.0.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) + version: 9.0.0(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(@standard-schema/spec@1.1.0)(intl-parse-accept-language@1.0.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) rsbuild-plugin-react-router: specifier: workspace:* version: link:../../.. @@ -1116,7 +1176,7 @@ importers: devDependencies: '@epic-web/config': specifier: 1.21.3 - version: 1.21.3(@testing-library/dom@10.4.1)(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/utils@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(prettier@3.8.1)(typescript@5.9.3) + version: 1.21.3(@testing-library/dom@10.4.1)(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/utils@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(prettier@3.8.1)(typescript@5.9.3) '@faker-js/faker': specifier: 10.2.0 version: 10.2.0 @@ -1125,7 +1185,7 @@ importers: version: 1.61.1 '@react-router/dev': specifier: ^7.13.0 - version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) '@rstest/core': specifier: 0.8.1 version: 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)) @@ -1140,7 +1200,7 @@ importers: version: 0.0.0-insiders.565cd3e(postcss@8.5.16) '@tailwindcss/postcss': specifier: ^4.1.18 - version: 4.3.1 + version: 4.3.2 '@testing-library/dom': specifier: 10.4.1 version: 10.4.1 @@ -1275,13 +1335,13 @@ importers: version: 0.6.1 '@module-federation/enhanced': specifier: 2.5.1 - version: 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/node': specifier: 2.7.44 - version: 2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.7.44(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/rsbuild-plugin': specifier: 2.5.1 - version: 2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + version: 2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@nasa-gcn/remix-seo': specifier: 2.0.1 version: 2.0.1(@remix-run/react@2.17.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(@remix-run/server-runtime@2.17.4(typescript@5.9.3)) @@ -1323,13 +1383,13 @@ importers: version: 1.0.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@react-router/express': specifier: 7.13.0 - version: 7.13.0(express@5.2.1)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.13.0(express@5.2.1)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/node': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/remix-routes-option-adapter': specifier: 7.13.0 - version: 7.13.0(@react-router/dev@7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)))(typescript@5.9.3) + version: 7.13.0(@react-router/dev@7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3) '@remix-run/server-runtime': specifier: 2.17.4 version: 2.17.4(typescript@5.9.3) @@ -1338,7 +1398,7 @@ importers: version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: 2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@sentry/node': specifier: 10.37.0 version: 10.37.0 @@ -1437,7 +1497,7 @@ importers: version: 19.2.7(react@19.2.7) react-router: specifier: ^7.13.0 - version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) remix-auth: specifier: 4.2.0 version: 4.2.0 @@ -1446,7 +1506,7 @@ importers: version: 3.0.2(remix-auth@4.2.0) remix-utils: specifier: 9.0.0 - version: 9.0.0(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(@standard-schema/spec@1.1.0)(intl-parse-accept-language@1.0.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) + version: 9.0.0(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(@standard-schema/spec@1.1.0)(intl-parse-accept-language@1.0.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) rsbuild-plugin-react-router: specifier: workspace:* version: link:../../.. @@ -1480,7 +1540,7 @@ importers: devDependencies: '@epic-web/config': specifier: 1.21.3 - version: 1.21.3(@testing-library/dom@10.4.1)(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/utils@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(prettier@3.8.1)(typescript@5.9.3) + version: 1.21.3(@testing-library/dom@10.4.1)(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/utils@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(prettier@3.8.1)(typescript@5.9.3) '@faker-js/faker': specifier: 10.2.0 version: 10.2.0 @@ -1489,7 +1549,7 @@ importers: version: 1.61.1 '@react-router/dev': specifier: ^7.13.0 - version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) '@rstest/core': specifier: 0.8.1 version: 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)) @@ -1504,7 +1564,7 @@ importers: version: 0.0.0-insiders.565cd3e(postcss@8.5.16) '@tailwindcss/postcss': specifier: ^4.1.18 - version: 4.3.1 + version: 4.3.2 '@testing-library/dom': specifier: 10.4.1 version: 10.4.1 @@ -1612,13 +1672,13 @@ importers: dependencies: '@react-router/express': specifier: ^7.13.0 - version: 7.13.0(express@5.2.1)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.13.0(express@5.2.1)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/node': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/serve': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) isbot: specifier: ^5.1.34 version: 5.1.34 @@ -1630,23 +1690,23 @@ importers: version: 19.2.7(react@19.2.7) react-router: specifier: ^7.13.0 - version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) devDependencies: '@playwright/test': specifier: ^1.61.1 version: 1.61.1 '@react-router/dev': specifier: ^7.13.0 - version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) '@rsbuild/core': specifier: 2.1.0 version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.1.18 - version: 4.3.1 + version: 4.3.2 '@types/node': specifier: ^25.0.10 version: 25.0.10 @@ -1661,7 +1721,7 @@ importers: version: 10.1.0 react-router-devtools: specifier: ^6.2.0 - version: 6.2.1(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(solid-js@1.9.13)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) + version: 6.2.1(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(solid-js@1.9.14)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) rsbuild-plugin-react-router: specifier: workspace:* version: link:../.. @@ -1670,34 +1730,147 @@ importers: version: 14.2.6 string-replace-loader: specifier: ^3.3.0 - version: 3.3.0(webpack@5.108.1(lightningcss@1.32.0)) + version: 3.3.0(webpack@5.108.3(lightningcss@1.32.0)) tailwindcss: specifier: ^4.1.18 - version: 4.1.18 + version: 4.3.1 text-encoder-lite: specifier: ^2.0.0 version: 2.0.0 typescript: specifier: ^5.9.3 version: 5.9.3 - vite: - specifier: ^7.3.1 - version: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) - vite-tsconfig-paths: - specifier: ^6.0.5 - version: 6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) + + examples/react-router-8: + dependencies: + '@react-router/fs-routes': + specifier: ^8.0.1 + version: 8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3) + '@react-router/node': + specifier: ^8.0.1 + version: 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/serve': + specifier: ^8.0.1 + version: 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@vanilla-extract/css': + specifier: ^1.20.1 + version: 1.21.1 + react: + specifier: ^19.2.4 + version: 19.2.7 + react-dom: + specifier: ^19.2.4 + version: 19.2.7(react@19.2.7) + react-router: + specifier: ^8.0.1 + version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + serialize-javascript: + specifier: ^6.0.1 + version: 6.0.2 + devDependencies: + '@playwright/test': + specifier: ^1.58.0 + version: 1.61.1 + '@react-router/dev': + specifier: ^8.0.1 + version: 8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + '@rsbuild/core': + specifier: 2.1.0 + version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/plugin-react': + specifier: 2.1.0 + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + '@types/node': + specifier: ^25.0.10 + version: 25.0.10 + '@types/react': + specifier: ^19.2.10 + version: 19.2.10 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.10) + cross-env: + specifier: ^10.1.0 + version: 10.1.0 + rsbuild-plugin-react-router: + specifier: workspace:* + version: link:../.. + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + examples/rsc-mode: + dependencies: + '@react-router/node': + specifier: ^8.0.1 + version: 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/serve': + specifier: ^8.0.1 + version: 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@remix-run/node-fetch-server': + specifier: ^0.13.3 + version: 0.13.3 + express: + specifier: ^4.22.2 + version: 4.22.2 + react: + specifier: ^19.2.4 + version: 19.2.7 + react-dom: + specifier: ^19.2.4 + version: 19.2.7(react@19.2.7) + react-router: + specifier: ^8.0.1 + version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-server-dom-rspack: + specifier: 0.0.2 + version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + devDependencies: + '@playwright/test': + specifier: ^1.58.0 + version: 1.61.1 + '@react-router/dev': + specifier: ^8.0.1 + version: 8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + '@rsbuild/core': + specifier: 2.1.0 + version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/plugin-react': + specifier: 2.1.0 + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + '@types/node': + specifier: ^25.0.10 + version: 25.0.10 + '@types/react': + specifier: ^19.2.10 + version: 19.2.10 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.10) + cross-env: + specifier: ^10.1.0 + version: 10.1.0 + rsbuild-plugin-react-router: + specifier: workspace:* + version: link:../.. + rsbuild-plugin-rsc: + specifier: ^0.1.1 + version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)) + typescript: + specifier: ^5.9.3 + version: 5.9.3 examples/spa-mode: dependencies: '@react-router/express': specifier: ^7.13.0 - version: 7.13.0(express@5.2.1)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.13.0(express@5.2.1)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/node': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@react-router/serve': specifier: ^7.13.0 - version: 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + version: 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) isbot: specifier: ^5.1.34 version: 5.1.34 @@ -1709,23 +1882,23 @@ importers: version: 19.2.7(react@19.2.7) react-router: specifier: ^7.13.0 - version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) devDependencies: '@playwright/test': specifier: ^1.61.1 version: 1.61.1 '@react-router/dev': specifier: ^7.13.0 - version: 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + version: 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) '@rsbuild/core': specifier: 2.1.0 version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) '@rsbuild/plugin-react': specifier: ^2.1.0 - version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@tailwindcss/postcss': specifier: ^4.1.18 - version: 4.3.1 + version: 4.3.2 '@types/node': specifier: ^25.0.10 version: 25.0.10 @@ -1740,7 +1913,7 @@ importers: version: 10.1.0 react-router-devtools: specifier: ^6.2.0 - version: 6.2.1(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(solid-js@1.9.13)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) + version: 6.2.1(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(solid-js@1.9.14)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) rsbuild-plugin-react-router: specifier: workspace:* version: link:../.. @@ -1749,55 +1922,418 @@ importers: version: 14.2.6 string-replace-loader: specifier: ^3.3.0 - version: 3.3.0(webpack@5.108.1(lightningcss@1.32.0)) + version: 3.3.0(webpack@5.108.3(lightningcss@1.32.0)) tailwindcss: specifier: ^4.1.18 - version: 4.1.18 + version: 4.3.1 text-encoder-lite: specifier: ^2.0.0 version: 2.0.0 typescript: specifier: ^5.9.3 version: 5.9.3 - vite: - specifier: ^7.3.1 - version: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) - vite-tsconfig-paths: - specifier: ^6.0.5 - version: 6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) - -packages: - - '@acemir/cssom@0.9.31': - resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} - - '@adobe/css-tools@4.5.0': - resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} - - '@alloc/quick-lru@5.2.0': - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} - engines: {node: '>=10'} - - '@apm-js-collab/code-transformer@0.8.2': - resolution: {integrity: sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA==} - - '@apm-js-collab/tracing-hooks@0.3.1': - resolution: {integrity: sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==} - - '@asamuzakjp/css-color@4.1.2': - resolution: {integrity: sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==} - '@asamuzakjp/dom-selector@6.8.1': - resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} + tests/react-router-framework/integration: + dependencies: + '@mdx-js/rollup': + specifier: ^3.1.1 + version: 3.1.1(rollup@4.62.2) + '@playwright/test': + specifier: ^1.60.0 + version: 1.61.1 + '@react-router/dev': + specifier: ^8.0.1 + version: 8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + '@react-router/express': + specifier: ^8.0.1 + version: 8.1.0(express@4.22.2)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/node': + specifier: ^8.0.1 + version: 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@types/cross-spawn': + specifier: ^6.0.6 + version: 6.0.6 + '@types/dedent': + specifier: ^0.7.0 + version: 0.7.2 + '@types/express': + specifier: ^5.0.0 + version: 5.0.6 + '@types/node': + specifier: ^22.19.19 + version: 22.20.0 + '@types/semver': + specifier: ^7.7.1 + version: 7.7.1 + '@types/shelljs': + specifier: ^0.10.0 + version: 0.10.0 + '@types/wait-on': + specifier: ^5.3.4 + version: 5.3.4 + '@vanilla-extract/css': + specifier: ^1.20.1 + version: 1.21.1 + cheerio: + specifier: ^1.2.0 + version: 1.2.0 + cross-spawn: + specifier: ^7.0.3 + version: 7.0.6 + dedent: + specifier: ^1.7.2 + version: 1.7.2 + execa: + specifier: 9.6.1 + version: 9.6.1 + express: + specifier: ^4.22.2 + version: 4.22.2 + get-port: + specifier: 7.2.0 + version: 7.2.0 + isbot: + specifier: ^5.1.40 + version: 5.1.44 + pathe: + specifier: ^2.0.3 + version: 2.0.3 + postcss: + specifier: ^8.5.15 + version: 8.5.16 + postcss-import: + specifier: ^16.1.1 + version: 16.1.1(postcss@8.5.16) + prettier: + specifier: ^3.8.3 + version: 3.9.4 + react: + specifier: ^19.2.4 + version: 19.2.7 + react-dom: + specifier: ^19.2.4 + version: 19.2.7(react@19.2.7) + react-router: + specifier: ^8.0.1 + version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-server-dom-rspack: + specifier: 0.0.2 + version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + semver: + specifier: ^7.8.1 + version: 7.8.5 + serialize-javascript: + specifier: ^6.0.1 + version: 6.0.2 + shelljs: + specifier: ^0.10.0 + version: 0.10.0 + strip-ansi: + specifier: ^7.2.0 + version: 7.2.0 + strip-indent: + specifier: ^3.0.0 + version: 3.0.0 + type-fest: + specifier: ^4.0.0 + version: 4.41.0 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + wait-on: + specifier: ^7.0.1 + version: 7.2.0 - '@asamuzakjp/nwsapi@2.3.9': - resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + tests/react-router-framework/integration/helpers/rsbuild-template: + dependencies: + '@react-router/express': + specifier: ^8.0.1 + version: 8.1.0(express@4.22.2)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/node': + specifier: ^8.0.1 + version: 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/serve': + specifier: ^8.0.1 + version: 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@remix-run/node-fetch-server': + specifier: ^0.13.3 + version: 0.13.3 + '@vanilla-extract/css': + specifier: ^1.20.1 + version: 1.21.1 + express: + specifier: ^4.22.2 + version: 4.22.2 + isbot: + specifier: ^5.1.40 + version: 5.1.44 + react: + specifier: ^19.2.4 + version: 19.2.7 + react-dom: + specifier: ^19.2.4 + version: 19.2.7(react@19.2.7) + react-router: + specifier: ^8.0.1 + version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + serialize-javascript: + specifier: ^6.0.1 + version: 6.0.2 + devDependencies: + '@mdx-js/rollup': + specifier: ^3.1.1 + version: 3.1.1(rollup@4.62.2) + '@react-router/dev': + specifier: ^8.0.1 + version: 8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + '@react-router/fs-routes': + specifier: ^8.0.1 + version: 8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3) + '@react-router/remix-routes-option-adapter': + specifier: ^8.0.1 + version: 8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3) + '@rsbuild/core': + specifier: 2.1.0 + version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/plugin-less': + specifier: 1.6.4 + version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsbuild/plugin-mdx': + specifier: ^1.1.3 + version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsbuild/plugin-react': + specifier: 2.1.0 + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + '@rsbuild/plugin-sass': + specifier: 1.5.3 + version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)) + '@rsbuild/plugin-svgr': + specifier: 2.0.4 + version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3) + '@rsbuild/plugin-tailwindcss': + specifier: 2.0.3 + version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@types/node': + specifier: ^22.19.19 + version: 22.20.0 + '@types/react': + specifier: ^19.2.10 + version: 19.2.10 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.10) + '@vanilla-extract/webpack-plugin': + specifier: ^2.3.27 + version: 2.3.27(webpack@5.108.3(lightningcss@1.32.0)) + rsbuild: + specifier: npm:@rsbuild/core@2.1.0 + version: '@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)' + rsbuild-plugin-react-router: + specifier: workspace:* + version: link:../../../../.. + tailwindcss: + specifier: 4.3.2 + version: 4.3.2 + typescript: + specifier: ^5.9.3 + version: 5.9.3 - '@ast-grep/napi-darwin-arm64@0.37.0': - resolution: {integrity: sha512-QAiIiaAbLvMEg/yBbyKn+p1gX2/FuaC0SMf7D7capm/oG4xGMzdeaQIcSosF4TCxxV+hIH4Bz9e4/u7w6Bnk3Q==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] + tests/react-router-framework/integration/helpers/rsc-framework: + dependencies: + '@react-router/express': + specifier: ^8.0.1 + version: 8.1.0(express@4.22.2)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/node': + specifier: ^8.0.1 + version: 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/serve': + specifier: ^8.0.1 + version: 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@remix-run/node-fetch-server': + specifier: ^0.13.3 + version: 0.13.3 + '@vanilla-extract/css': + specifier: ^1.20.1 + version: 1.21.1 + compression: + specifier: ^1.8.1 + version: 1.8.1 + express: + specifier: ^4.22.2 + version: 4.22.2 + isbot: + specifier: ^5.1.40 + version: 5.1.44 + react: + specifier: ^19.2.4 + version: 19.2.7 + react-dom: + specifier: ^19.2.4 + version: 19.2.7(react@19.2.7) + react-router: + specifier: ^8.0.1 + version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-server-dom-rspack: + specifier: 0.0.2 + version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + devDependencies: + '@mdx-js/rollup': + specifier: ^3.1.1 + version: 3.1.1(rollup@4.62.2) + '@react-router/dev': + specifier: ^8.0.1 + version: 8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + '@react-router/fs-routes': + specifier: ^8.0.1 + version: 8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3) + '@react-router/remix-routes-option-adapter': + specifier: ^8.0.1 + version: 8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3) + '@rsbuild/core': + specifier: 2.1.0 + version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/plugin-less': + specifier: 1.6.4 + version: 1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsbuild/plugin-mdx': + specifier: ^1.1.3 + version: 1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsbuild/plugin-react': + specifier: 2.1.0 + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + '@rsbuild/plugin-sass': + specifier: 1.5.3 + version: 1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)) + '@rsbuild/plugin-svgr': + specifier: 2.0.4 + version: 2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3) + '@rsbuild/plugin-tailwindcss': + specifier: 2.0.3 + version: 2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@types/express': + specifier: ^5.0.0 + version: 5.0.6 + '@types/node': + specifier: ^22.19.19 + version: 22.20.0 + '@types/react': + specifier: ^19.2.10 + version: 19.2.10 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.10) + '@vanilla-extract/webpack-plugin': + specifier: ^2.3.27 + version: 2.3.27(webpack@5.108.3(lightningcss@1.32.0)) + cross-env: + specifier: ^10.1.0 + version: 10.1.0 + rsbuild: + specifier: npm:@rsbuild/core@2.1.0 + version: '@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)' + rsbuild-plugin-react-router: + specifier: workspace:* + version: link:../../../../.. + rsbuild-plugin-rsc: + specifier: ^0.1.1 + version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)) + tailwindcss: + specifier: 4.3.2 + version: 4.3.2 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + + tests/react-router-framework/integration/helpers/rsc-preview: + dependencies: + '@remix-run/node-fetch-server': + specifier: ^0.13.3 + version: 0.13.3 + compression: + specifier: ^1.8.1 + version: 1.8.1 + cross-env: + specifier: ^10.1.0 + version: 10.1.0 + express: + specifier: ^4.22.2 + version: 4.22.2 + react: + specifier: ^19.2.4 + version: 19.2.7 + react-dom: + specifier: ^19.2.4 + version: 19.2.7(react@19.2.7) + react-router: + specifier: ^8.0.1 + version: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-server-dom-rspack: + specifier: 0.0.2 + version: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + devDependencies: + '@rsbuild/core': + specifier: 2.1.0 + version: 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@rsbuild/plugin-react': + specifier: 2.1.0 + version: 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + '@types/express': + specifier: ^5.0.0 + version: 5.0.6 + '@types/node': + specifier: ^22.19.19 + version: 22.20.0 + '@types/react': + specifier: ^19.2.10 + version: 19.2.10 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.10) + rsbuild: + specifier: npm:@rsbuild/core@2.1.0 + version: '@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)' + rsbuild-plugin-react-router: + specifier: workspace:* + version: link:../../../../.. + rsbuild-plugin-rsc: + specifier: ^0.1.1 + version: 0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + +packages: + + '@acemir/cssom@0.9.31': + resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} + + '@adobe/css-tools@4.5.0': + resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@apm-js-collab/code-transformer@0.8.2': + resolution: {integrity: sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA==} + + '@apm-js-collab/tracing-hooks@0.3.1': + resolution: {integrity: sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==} + + '@asamuzakjp/css-color@4.1.2': + resolution: {integrity: sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==} + + '@asamuzakjp/dom-selector@6.8.1': + resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + + '@ast-grep/napi-darwin-arm64@0.37.0': + resolution: {integrity: sha512-QAiIiaAbLvMEg/yBbyKn+p1gX2/FuaC0SMf7D7capm/oG4xGMzdeaQIcSosF4TCxxV+hIH4Bz9e4/u7w6Bnk3Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] '@ast-grep/napi-darwin-x64@0.37.0': resolution: {integrity: sha512-zvcvdgekd4ySV3zUbUp8HF5nk5zqwiMXTuVzTUdl/w08O7JjM6XPOIVT+d2o/MqwM9rsXdzdergY5oY2RdhSPA==} @@ -1982,18 +2518,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.29.7': - resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-source@7.29.7': - resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.29.7': resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} engines: {node: '>=6.9.0'} @@ -2022,8 +2546,8 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} - '@biomejs/cli-darwin-arm64@2.5.1': - resolution: {integrity: sha512-npqDzvqv7vFaWRiNN1Te71siRgPaqS9MpqgYCdP/CrUbkJ7ApezaeaKjueKHRN/JH/6lRjJQAHi8acQDCAz22w==} + '@biomejs/cli-darwin-arm64@2.5.2': + resolution: {integrity: sha512-e7P3P7EkwFc/KiX2AHw4YDLIBOMfG9CPCAwy52k5Bp0dfhkozx9hf6wCmIr2QeXy2XeccJ3V/Sg+hDmzYEqxSg==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] @@ -2105,38 +2629,38 @@ packages: workerd: optional: true - '@cloudflare/workerd-darwin-64@1.20260625.1': - resolution: {integrity: sha512-naCfBv0WnnTQIQPTniqMoUlklOIFjrAcSn1X+IAOhY8aFLF/xGYtFjs1eEE8sFib3ZuChGGpU23FFORVczqr0A==} + '@cloudflare/workerd-darwin-64@1.20260701.1': + resolution: {integrity: sha512-Zd9Y1bah6DwwBN2RW8vJohffQrIUazb8UXnqSNecOxM+jJLhUuvv5IOG8dbHcV83TyZAubea6gsQXo2yH1lDdw==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260625.1': - resolution: {integrity: sha512-jmH6zjp6Wrux46+qtFwDwrj+vd7s5bdwEqeGvdnwE0a4IEeAhKs0L42HQOyID+g5lkrHq9m55+AbhtmRAm63Pw==} + '@cloudflare/workerd-darwin-arm64@1.20260701.1': + resolution: {integrity: sha512-yBLsjS1qCWqFyCY37qRUrYfzHHvMGvjh8zRKJ6MvUivYDhkZTzqduppK38FoqYvayLJ5KbcxH7zo5rkxGqbsaA==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260625.1': - resolution: {integrity: sha512-MiQkpA/dX8d83Zp64pzHUKfd6ca4cvwxnNobSP6CnXvfESvnNI9pfa+nfwnParla36sPmnYntNkjR7NjRuDeKQ==} + '@cloudflare/workerd-linux-64@1.20260701.1': + resolution: {integrity: sha512-vMfqSIMfoo4xmZXEuUVqLpSFS921YKjiR9q7kDXPi6Vld1PK74UHg9LZuBavT2KSyemHUCTpj9y/4JSYOEyQbQ==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260625.1': - resolution: {integrity: sha512-LxxW7Qv60Xvv37+w6gUSDpYZziyqMy+cZWd9IvSA5ehVgKAxmzEaYPMiSZlxk32nbIWL9u/tfjXYCOKJ4Lo+XQ==} + '@cloudflare/workerd-linux-arm64@1.20260701.1': + resolution: {integrity: sha512-HRfwbKU2pK44V2NhoM0+iH0JJSj7nQ9Wv13ifIiGYCmTtDL8/zKtEhX7kQ3D4Vy/Cpjhttl0FkfqXj1aqLDPPg==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260625.1': - resolution: {integrity: sha512-LH6iIX1HHaTwVKV5VokDxxUErXJzQoNZFRwVm7Vx/3fB/ApcTcRCUaMqcxI4as94jEUqg+pmX5czOndiveohow==} + '@cloudflare/workerd-windows-64@1.20260701.1': + resolution: {integrity: sha512-ngxCiIN9s/fM2o1IBMD0o1/mcXrv2NJVdyznh51UH8sQuvrTrXvV2nM0Uj/qU2wMwF6prgNBcdcd7AZeZGiBQA==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@4.20260628.1': - resolution: {integrity: sha512-fMy5zBnNl/PxGqDzSOQb1TdqyR1sRT1Z7T4F8/cqtxpZ1w8VkejWi0qUH7GZE0I2gJyapdP0oW4pNZfxUbekmw==} + '@cloudflare/workers-types@4.20260702.1': + resolution: {integrity: sha512-mOhf5TUEB1m2vPrxtqoIGfz0fUC9xyxRDx5gWHy5s+OCo6dcV+g7wI1R7gYCMFohhqF/2y2xeKVwMwCJjfn/WA==} '@conform-to/dom@1.16.0': resolution: {integrity: sha512-KkURoALYztq5kli8/Ojqe4PyTcAmc9pHOmIoU5RJzre8poWgmjCsHQt28xhEyDk5XBwo8bkvyKm8oKqwSAAucw==} @@ -2197,18 +2721,27 @@ packages: '@emnapi/core@1.11.1': resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + '@emnapi/core@1.11.2': + resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} + '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} '@emnapi/runtime@1.11.1': resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@emnapi/runtime@1.11.2': + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} '@emnapi/wasi-threads@1.2.2': resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + '@epic-web/cachified@5.6.1': resolution: {integrity: sha512-+VKwMhqM43l2s+gX28Telcf6bUJk1Zaj0Ix2i8K4R2QW8WgPE0q3THCnr0xZg5chw35/B4SkHS43an2fqKOFnQ==} @@ -2641,6 +3174,32 @@ packages: ts-jest: optional: true + '@hapi/address@5.1.1': + resolution: {integrity: sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==} + engines: {node: '>=14.0.0'} + + '@hapi/formula@3.0.2': + resolution: {integrity: sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==} + + '@hapi/hoek@11.0.7': + resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==} + + '@hapi/hoek@9.3.0': + resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} + + '@hapi/pinpoint@2.0.1': + resolution: {integrity: sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==} + + '@hapi/tlds@1.1.7': + resolution: {integrity: sha512-MgNjRwy9Ti92yVAixLmDc8dd1bJIKwO9qlWCfFQRwRmUEDPQHYn4G6hwPFvFGUTzAa0FsS+inMjLin7GnyBRhA==} + engines: {node: '>=14.0.0'} + + '@hapi/topo@5.1.0': + resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + + '@hapi/topo@6.0.2': + resolution: {integrity: sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==} + '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -2874,6 +3433,22 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@mdx-js/loader@3.1.1': + resolution: {integrity: sha512-0TTacJyZ9mDmY+VefuthVshaNIyCGZHJG2fMnGaDttCt8HmjUF7SizlHJpaCDoGnN635nK1wpzfpx/Xx5S4WnQ==} + peerDependencies: + webpack: '>=5' + peerDependenciesMeta: + webpack: + optional: true + + '@mdx-js/mdx@3.1.1': + resolution: {integrity: sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==} + + '@mdx-js/rollup@3.1.1': + resolution: {integrity: sha512-v8satFmBB+DqDzYohnm1u2JOvxx6Hl3pUvqzJvfs2Zk/ngZ1aRUhsWpXvwPkNeGN9c2NCm/38H29ZqXQUjf8dw==} + peerDependencies: + rollup: '>=2' + '@mjackson/form-data-parser@0.9.1': resolution: {integrity: sha512-GQqet5qTAm8LfUOsMdfdInqnOBpuDO5GK/y7tBgpXs+DQhJY9Rf1fxMuFXiXczoNMRu8UIG5/RFSBDeaF1bbrw==} @@ -3075,16 +3650,16 @@ packages: resolution: {integrity: sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==} engines: {node: '>=8.0.0'} - '@opentelemetry/api-logs@0.219.0': - resolution: {integrity: sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==} + '@opentelemetry/api-logs@0.220.0': + resolution: {integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==} engines: {node: '>=8.0.0'} '@opentelemetry/api@1.9.1': resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} engines: {node: '>=8.0.0'} - '@opentelemetry/context-async-hooks@2.8.0': - resolution: {integrity: sha512-/3FIraneMcng67SUJCxvyInk/oxzwsxyadufk0wwfOBLf5wqtAGX4MoQASwSbndBPeARzBryUM9Azr5kHIdWLw==} + '@opentelemetry/context-async-hooks@2.9.0': + resolution: {integrity: sha512-OQ0vzvbZBiUhjqLnUaoNfYmP8553Crr3aggB4y0ZUi815mZ7idpdJXQmoKdeBKJelYttoBlLSSHubmyw3wvX4w==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -3095,8 +3670,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.8.0': - resolution: {integrity: sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==} + '@opentelemetry/core@2.9.0': + resolution: {integrity: sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -3245,8 +3820,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.219.0': - resolution: {integrity: sha512-X5t7I8GyIO9rmGHwoedZLREpQqrF1WW2nxzNNym6HOKpFiE+rvqV3ngC0xcZVO2YwIGf3KKmRdWrYwdwz3H9RQ==} + '@opentelemetry/instrumentation@0.220.0': + resolution: {integrity: sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -3255,14 +3830,20 @@ packages: resolution: {integrity: sha512-VCghU1JYs/4gP6Gqf/xro9MEsZ7LrMv2uONVsaESKL38ZOB9BqnI98FfS23wjMnHlpuE+TTaWSoAVNpTwYXzjw==} engines: {node: ^18.19.0 || >=20.6.0} - '@opentelemetry/resources@2.8.0': - resolution: {integrity: sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==} + '@opentelemetry/resources@2.9.0': + resolution: {integrity: sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.9.0': + resolution: {integrity: sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace-base@2.8.0': - resolution: {integrity: sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==} + '@opentelemetry/sdk-trace@2.9.0': + resolution: {integrity: sha512-sGA19HvtrrSKYsseHphluH6j3p6Xa3fqc7c7y8f/7mYWejc1lyDFcpSdD1kYa50HCLUeEo4zA5bW0pniaPszuw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' @@ -3450,8 +4031,8 @@ packages: '@radix-ui/primitive@1.1.4': resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} - '@radix-ui/react-accordion@1.2.14': - resolution: {integrity: sha512-iE8YB9nmTBH8zd73ofBISZ8JCzgMoMkATJr7qDwa6u5F1+7mTM81V6fa71jgZ65rpjVpecDf1vSnwIFP9Ly1zw==} + '@radix-ui/react-accordion@1.2.15': + resolution: {integrity: sha512-24Zz/0SYx8F2bSVThBnQrdJs2VbKelyuJordcFRRdA0fRAhrq/wSegGCqaQz34VQoiWqSMGYCYXEhynLSlyQlg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3489,8 +4070,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collapsible@1.1.14': - resolution: {integrity: sha512-9bT+FvifX1FK2Mj6UEsTdyu0cN3JaA3KdfhaBao+ONrYFy/pyOy3TU1TNw7iOk1o+0hOEq67RojlUUmoFGwxyA==} + '@radix-ui/react-collapsible@1.1.15': + resolution: {integrity: sha512-8A1zibu5skAQ+UVbaeNH5hVMibiFCRJzgMuM14LTWGttnTZKQL9jwYnhAbHRuxrtCqPXa4JvvnVUq1pTNgyZYw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3502,8 +4083,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.10': - resolution: {integrity: sha512-IVVz4EvBcKjrzKgof714qDnz/SzQAkLA2Emh5edlHbgcE6fNd3Un6CJLlaYcnm8N4JmAtzQgse4dOKxcD2yc9g==} + '@radix-ui/react-collection@1.1.11': + resolution: {integrity: sha512-djW9+zeg137KQdlPtmE8xnaD+K2rcXXMWFrSg0hsmYZ6HRbdTA7tDHFgpaW9+huWVEu0RCabL+985T4TA0BE7g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3752,8 +4333,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.1.6': - resolution: {integrity: sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g==} + '@radix-ui/react-primitive@2.1.7': + resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -4125,12 +4706,12 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc - '@react-router/cloudflare@7.18.0': - resolution: {integrity: sha512-im4t/oeTyT/oIoU/YoNtn4g9XA3NjeZoBM1wQafZPhXmCu6LfPF5TFaPwenj3U+70cqeuwXeVY/NfBG6yxodmQ==} + '@react-router/cloudflare@7.18.1': + resolution: {integrity: sha512-fQpWtCQ2wvV7mSfM/YC3dGWyMZtGlRI6JpkB07MF38ZTcqQf5wmyfP2u7iO3+YdalOKW+3fVnCFS84QkPAKwlg==} engines: {node: '>=20.0.0'} peerDependencies: '@cloudflare/workers-types': ^4.0.0 - react-router: ^7.18.0 + react-router: ^7.18.1 typescript: ^5.1.0 || ^6.0.0 peerDependenciesMeta: typescript: @@ -4160,38 +4741,14 @@ packages: wrangler: optional: true - '@react-router/dev@7.18.0': - resolution: {integrity: sha512-GVTFvul0xlZHZyVXyRpiJv54Xfyj4eDOAlGYrzi7kDmN7n40rsrUqX+hvU0fy/41SCDMtckht59R3iGR94703g==} - engines: {node: '>=20.0.0'} - hasBin: true - peerDependencies: - '@react-router/serve': ^7.18.0 - '@vitejs/plugin-rsc': ~0.5.21 - react-router: ^7.18.0 - react-server-dom-webpack: ^19.2.3 - typescript: ^5.1.0 || ^6.0.0 - vite: ^5.1.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - wrangler: ^3.28.2 || ^4.0.0 - peerDependenciesMeta: - '@react-router/serve': - optional: true - '@vitejs/plugin-rsc': - optional: true - react-server-dom-webpack: - optional: true - typescript: - optional: true - wrangler: - optional: true - - '@react-router/dev@8.0.1': - resolution: {integrity: sha512-Xc1WfN4Ql9j271iJnvIJqxLvN5cyjUW/X4JsGw2j/0lET12UFnpNTpBLYXmLNci8vfpmfKI06KSixZ4pjIT4Bw==} + '@react-router/dev@8.1.0': + resolution: {integrity: sha512-0R7g3hPm+vXjfOzA6oInZ3KI/N9hxrtibtue3s1pdHcSPrfRFU2jgmC5I/W0gCJ9wtszLKqisHRet2vzY8USoA==} engines: {node: '>=22.22.0'} hasBin: true peerDependencies: - '@react-router/serve': ^8.0.1 + '@react-router/serve': ^8.1.0 '@vitejs/plugin-rsc': ~0.5.26 - react-router: ^8.0.1 + react-router: ^8.1.0 react-server-dom-webpack: ^19.2.7 typescript: ^5.1.0 || ^6.0.0 vite: ^7.0.0 || ^8.0.0 @@ -4230,12 +4787,22 @@ packages: typescript: optional: true - '@react-router/express@7.18.0': - resolution: {integrity: sha512-7+6oZJYdJ7ha7ASV0XkYZkmnTUYtRkzjzjpZh3W9tPdaUY1zJoDFiMlUJZoAg0mHw8BNTAwVWCCf8NFV1xlLZA==} - engines: {node: '>=20.0.0'} + '@react-router/express@8.1.0': + resolution: {integrity: sha512-SuyQNfxbfphmEjF0joUir5boACNRoD2HUPrIqp56fhG7zwOAR9NQpnu6ZQZ8iA2ox3U013BR8Flijb2tI1jFbg==} + engines: {node: '>=22.22.0'} peerDependencies: - express: ^4.17.1 || ^5 - react-router: 7.18.0 + express: ^4.22.2 || ^5 + react-router: 8.1.0 + typescript: ^5.1.0 || ^6.0.0 + peerDependenciesMeta: + typescript: + optional: true + + '@react-router/fs-routes@8.1.0': + resolution: {integrity: sha512-m2TcSWXY/XQL46kXbo/mCoTx9pqzU4/om0aF0C1x/BCdHq9NsXSorUhiL8Jg08RJ+ZVxMbNc0W0WH/HQsKb6yw==} + engines: {node: '>=22.22.0'} + peerDependencies: + '@react-router/dev': ^8.1.0 typescript: ^5.1.0 || ^6.0.0 peerDependenciesMeta: typescript: @@ -4261,21 +4828,11 @@ packages: typescript: optional: true - '@react-router/node@7.18.0': - resolution: {integrity: sha512-pRXJahLrdVfuVbaTpWsZ89mBuGiYH3Z4y+y1UidwxmJFKk6NjMyUvkJl3FjDWdD+nSlgFPSESUZS0hF560MUUQ==} - engines: {node: '>=20.0.0'} - peerDependencies: - react-router: 7.18.0 - typescript: ^5.1.0 || ^6.0.0 - peerDependenciesMeta: - typescript: - optional: true - - '@react-router/node@8.0.1': - resolution: {integrity: sha512-XUtOdjgOtFXe4XxkO28km51l++AYL7A3mk4Sozm7hr3ROY/9qE+9EoPHw0gEv4FQEgY7a/6XnzDL5dB+zNt7GA==} + '@react-router/node@8.1.0': + resolution: {integrity: sha512-KgdcDTp+rDFybx4qBaGxBpKEBCSjBT+bmRXRziD3A9TOlQ1AlaqK1sSttYtgA/t4HEgoDsKVa7OO9jaZAacLgg==} engines: {node: '>=22.22.0'} peerDependencies: - react-router: 8.0.1 + react-router: 8.1.0 typescript: ^5.1.0 || ^6.0.0 peerDependenciesMeta: typescript: @@ -4291,6 +4848,16 @@ packages: typescript: optional: true + '@react-router/remix-routes-option-adapter@8.1.0': + resolution: {integrity: sha512-9lh1lzuXk1l70ekNLZYWcdo2mczeEfL8/dUhz2uK9yLOGWogtKbELyskJeLBGoZ/mfHardRM/VsPL46qoCw3Zg==} + engines: {node: '>=22.22.0'} + peerDependencies: + '@react-router/dev': ^8.1.0 + typescript: ^5.1.0 || ^6.0.0 + peerDependenciesMeta: + typescript: + optional: true + '@react-router/serve@7.14.0': resolution: {integrity: sha512-setPBP5+ci0vwx+ufGZl0inOwsCoGU1ssOJcW4fHo+Pb6GbbMTrbCOVO6yQkDsTrQju+iStp3d7FTxLHphLhcA==} engines: {node: '>=20.0.0'} @@ -4298,12 +4865,12 @@ packages: peerDependencies: react-router: 7.14.0 - '@react-router/serve@7.18.0': - resolution: {integrity: sha512-IrF0cLcJNGBBavnRBm3HxaEGwRrLrLF8E4EzQFuCpkgP1sRli1x2xEOOTJl4zBgUbyIn0ey4TAD6ytg45MAUBQ==} - engines: {node: '>=20.0.0'} + '@react-router/serve@8.1.0': + resolution: {integrity: sha512-ZK6BK25axqfWMha773/+5ZPGyh0zSWiOhsRXECnrHImUhzBcVvEK7niApbimIW9zllHegUIqbwb2AC47A6MQ4w==} + engines: {node: '>=22.22.0'} hasBin: true peerDependencies: - react-router: 7.18.0 + react-router: 8.1.0 '@remix-run/node-fetch-server@0.13.3': resolution: {integrity: sha512-UfjOXed/DQteaM5VyTfqTeGpHwyL2J5aoRGY6cydip4tt1ehNNeSwuXCC7AEGE0RWBs/7bgKxYkL/B/+UDe4AA==} @@ -4345,8 +4912,14 @@ packages: typescript: optional: true - '@rolldown/pluginutils@1.0.0-beta.53': - resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} + '@rollup/pluginutils@5.4.0': + resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true '@rollup/rollup-android-arm-eabi@4.62.2': resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} @@ -4498,6 +5071,16 @@ packages: core-js: optional: true + '@rsbuild/core@2.1.6': + resolution: {integrity: sha512-w2WxblstOgHnDElkqJZVO/jM/EqPaEhg7zqQhON3Xu3Mj9FlVOJx+SgimOtghFzxLt8x7atX4oMUtMTNSZGf0Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + core-js: '>= 3.0.0' + peerDependenciesMeta: + core-js: + optional: true + '@rsbuild/plugin-babel@2.0.0': resolution: {integrity: sha512-JSiKJhsebDItVYPLY4dpZ0vw2Acvqv4jQtuAIDri+o6qBYzCzDXP936F6REiitVNTWh3H2rCyT8eM/WADTuFow==} peerDependencies: @@ -4522,6 +5105,14 @@ packages: '@rsbuild/core': optional: true + '@rsbuild/plugin-mdx@1.1.3': + resolution: {integrity: sha512-2o/Pg+VExsyfr6uzzXj+cBxzjiFbQ2yIUxzM5kQnrPC5J0D8ZhWyXuWN5rTtsolAS23QdfsycZ0iAtlRuZVwCw==} + peerDependencies: + '@rsbuild/core': ^1.0.0 || ^2.0.0 + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@rsbuild/plugin-react@2.1.0': resolution: {integrity: sha512-RQTIAWB/CwPjoWt9iAl+8HixeQVgZ7kEIBrWPCixfITyHdiD84h0YpUTpEUuz6kGHw1KXT9mHZ3Rwy6WG7aRDA==} peerDependencies: @@ -4554,28 +5145,28 @@ packages: '@rsbuild/core': optional: true - '@rsdoctor/client@1.5.16': - resolution: {integrity: sha512-SskR2c06w+QTCrrhlN5wIjpVzJgqscQlMlYT3e/rSc2lH09lA1VBkKlgh2jy0OkE7FTZIO+9n6hdXM4qCAFOOA==} + '@rsdoctor/client@1.5.17': + resolution: {integrity: sha512-3EqJtkfBfKd4hcr2+KxvwqpTyTxm11Ti//c/0BGvcr+RnH+U7XVkrpxVZNvTPDA9RTSIqMNJCUh4/89KRSZPQQ==} - '@rsdoctor/core@1.5.16': - resolution: {integrity: sha512-Sj9bJO3yRYvTn111pNFzADLrE9WJ8ZgOJertzeST4ueOSw4yeUkM9oMgj21p7NWYsM44C2vDuBIcerAHuxIkBA==} + '@rsdoctor/core@1.5.17': + resolution: {integrity: sha512-RVpCDEIw2jERG1Jfc8t/sWVqPh9KndcoJRYwPHxls3GsbvaJZtwV1/smipn7GXiuT8T/80XU2icTydcFEdZEdw==} - '@rsdoctor/graph@1.5.16': - resolution: {integrity: sha512-Uzq5n5Za30z2CW6V9j6Bl4x8Bbek1YHJyhKEXyF1P3UoQIQ2qpdYu1B//krlDV2YmwB7ENwqaV5D0D+LoB690g==} + '@rsdoctor/graph@1.5.17': + resolution: {integrity: sha512-fMT50FdXZEObmt9aMaKFiZ4YI1hzFS0sBKSPqTUqq+GvkhRaDEx7FD+TNAYGoCeVENFkNWagS4NUpYP7Tn4SVw==} - '@rsdoctor/rspack-plugin@1.5.16': - resolution: {integrity: sha512-CoFLPpJF+XU96sVZ8EWbbQh4ZjihmZAv3hlyQzrn+HO8VBa0BRI/k+oyRAPwg7fI0/Uc1OHDtgL7D8IST3vbTw==} + '@rsdoctor/rspack-plugin@1.5.17': + resolution: {integrity: sha512-n8G4lhY3wxVg/Prni9YQskT9gHsReZRxozcruIdoqV00Y9Af+rmq5AOdKd3sN1XJnEXQP1d7HwZtBopm2L6qGA==} peerDependencies: '@rspack/core': '*' peerDependenciesMeta: '@rspack/core': optional: true - '@rsdoctor/sdk@1.5.16': - resolution: {integrity: sha512-t2ghEUCUKFwP12QeeLX3VvI5b+Z+SI9rwUiE8VHa+gOFi8oHkceEi0rlBf1k9BexMpQf3zP3RNTZtzo09mxpCw==} + '@rsdoctor/sdk@1.5.17': + resolution: {integrity: sha512-sywxok2WlZmHU2xd+yhVZAgbxyRpJF0y13OpdldKXaC5fTtuNIefA3NL5gEybDKUPcEJnxRhTCwwY0RMmoI8rA==} - '@rsdoctor/types@1.5.16': - resolution: {integrity: sha512-J9yqQu1VNFgLJpkgzAucjA1AkVnrg6kRyMhAzj05uYyPM6FQgFmi3NlQFDxRPLAfTuwSXC72xhSi7LYd6MuVvA==} + '@rsdoctor/types@1.5.17': + resolution: {integrity: sha512-iJsAUybCyLBOcXgWuRAqMREL3TBx6/4HLnNMRPgzLv4K3eMWD2CErntkJsabZZjh5VklBn7nTpBifHMilLGPVA==} peerDependencies: '@rspack/core': '*' webpack: 5.x @@ -4585,8 +5176,8 @@ packages: webpack: optional: true - '@rsdoctor/utils@1.5.16': - resolution: {integrity: sha512-aFhv9gzDjJ5qyODO1Uno1NAfl/7WDhmJASJ5lQj1Qo8FV1VgtnYKFbbYdQYSpsowhxgklNISxVs905HdiBp6yg==} + '@rsdoctor/utils@1.5.17': + resolution: {integrity: sha512-d4r6E8CiI+cQi+FJX24eObIS/AYrp0+xWHFd8URDNrjvRtZyAY12kKllcDT3nCw9OPlKhMSh+p+SqOohfDNCvg==} '@rslib/core@0.22.1': resolution: {integrity: sha512-RaqTITHFkpMDJG9fmD7Hu6FLE64hwctCo46asHOD2DipzQJWawg6K0pFGimTAyutYEZysIUfYgCwSYkbctDudg==} @@ -4616,6 +5207,11 @@ packages: cpu: [arm64] os: [darwin] + '@rspack/binding-darwin-arm64@2.1.4': + resolution: {integrity: sha512-3Xcs01iw48F4WeE4SHga6bCNb/UEFvtQX4P4eMIaJfGPjTQuxfabGE8yCPm9e3tpLZ5uo+IBnJ6nh5r6tIDOXQ==} + cpu: [arm64] + os: [darwin] + '@rspack/binding-darwin-x64@1.7.12': resolution: {integrity: sha512-jnOp+/UXOJa9xqUb8KXH03sysoO2e4Ij6tw6MqDdmdj8n/A8PQENRPUbW9AwXpPtVDJPus9r4fi7b3+6e4B8Hg==} cpu: [x64] @@ -4631,6 +5227,11 @@ packages: cpu: [x64] os: [darwin] + '@rspack/binding-darwin-x64@2.1.4': + resolution: {integrity: sha512-bz/AsCplLs+3fXULPQU9d4r8H4PdeljgHFyItvVIrA/NKZqzQ8sX0topf/zJVZAOtPH7GNnrKjq0/F0U2DHikQ==} + cpu: [x64] + os: [darwin] + '@rspack/binding-linux-arm64-gnu@1.7.12': resolution: {integrity: sha512-C8owWG+yvo7X0oVLIXetkoJhIFBP1LYNcAQqtgLmJnQLQDklGuP83dKC+zISGQWpjawHfZ1ER96vLgoTrxKZdw==} cpu: [arm64] @@ -4646,6 +5247,11 @@ packages: cpu: [arm64] os: [linux] + '@rspack/binding-linux-arm64-gnu@2.1.4': + resolution: {integrity: sha512-x0HQTLU1MusCtNamuXxf3ayEPkvh9uuaq4wVyBqveRkn4FznSOoHUsxTAKMnjGARX+vdLV/y/SwWJRDp2RI4zw==} + cpu: [arm64] + os: [linux] + '@rspack/binding-linux-arm64-musl@1.7.12': resolution: {integrity: sha512-i51WWI64aRpsfSki6rN0aepPqXkVfS+vZM7+4bWDcmnhUmdMvhIPcYg0QRk3DtyJnu33jqNLM0WHY78k00NyfA==} cpu: [arm64] @@ -4661,16 +5267,31 @@ packages: cpu: [arm64] os: [linux] + '@rspack/binding-linux-arm64-musl@2.1.4': + resolution: {integrity: sha512-SEYCQD9UflKJMkYGnG5nt2gcsqdkgJsQmryBC/jxo+bOuY9gUSReU99FqCt7WRQrsHLbGeAFeTWs1QzItWG/Bw==} + cpu: [arm64] + os: [linux] + '@rspack/binding-linux-riscv64-gnu@2.1.0': resolution: {integrity: sha512-B3ENZHIBi5u1Apt6RJ62QSCabCijI5l86Sm2AEDYpQnqqBj3vIc+Br9HJHvNjK8PNWs1WfmD//UTUmQqZbYpKQ==} cpu: [riscv64] os: [linux] + '@rspack/binding-linux-riscv64-gnu@2.1.4': + resolution: {integrity: sha512-jYtQKtnDRaVfyasvTGY04Z7m+xDWZYVwAIEOB4hP7czM7FVLOMgHlMlvw/EgF0DNHrBthqbPfBIS2tP50CzpEA==} + cpu: [riscv64] + os: [linux] + '@rspack/binding-linux-riscv64-musl@2.1.0': resolution: {integrity: sha512-Qho1S8bW2BKRsJjl/f39GoyPRznF8ZarIgxZdVCIkn4k+3veggKWxqR1WWKoMj/LfykQd1uG3FF6n7zy5IfxWw==} cpu: [riscv64] os: [linux] + '@rspack/binding-linux-riscv64-musl@2.1.4': + resolution: {integrity: sha512-ZgxKjQAm9pidq2kChQO2PqKI9OQpLuGD7iPBuyT0gQg3m1+7vvbbkArLBPzJ12CQHVZvqua7n/QGx8pQjJI2Zw==} + cpu: [riscv64] + os: [linux] + '@rspack/binding-linux-x64-gnu@1.7.12': resolution: {integrity: sha512-MSos0FuPEefqo9V92ULd5hggKG29EkSNg1zDcypy0OkpsKh5pfjVxTLYFXgTcVyFoUQQbdG8zFBzYbwmJ8V4ew==} cpu: [x64] @@ -4686,6 +5307,11 @@ packages: cpu: [x64] os: [linux] + '@rspack/binding-linux-x64-gnu@2.1.4': + resolution: {integrity: sha512-C53B3e6M4yzlYn4hDxR9cHZV+HqkfFGR0zuhH8QCfdBfN5KyGsmXmujiFU85ANEvBgf9CF8VreBQeJ20lyto/g==} + cpu: [x64] + os: [linux] + '@rspack/binding-linux-x64-musl@1.7.12': resolution: {integrity: sha512-JcAMVKXOnjfpC3coWjCFPWD3Yl8RBw6a+IXQQ8mfRlHaHMIiOv8IfZqx15XRxMUn49CtP7Z0Na8iiAg2aKrcfw==} cpu: [x64] @@ -4701,6 +5327,11 @@ packages: cpu: [x64] os: [linux] + '@rspack/binding-linux-x64-musl@2.1.4': + resolution: {integrity: sha512-UaeG3FRo7e5RameQvWRNQ6KeXF29LEk67U/ohb9tF4U38mKH1OGqhmwCf5yLkqiOSgOi7Ff3ireOZD83Fso1iw==} + cpu: [x64] + os: [linux] + '@rspack/binding-wasm32-wasi@1.7.12': resolution: {integrity: sha512-n+ZqP6ZMc0nhOgvadg5VhEs9ojtbES80AcWeFnmGkbzIszvGSO63GKNiRkXtjJ9KFuRzytbbmsCqkUVH+Tywxg==} cpu: [wasm32] @@ -4713,6 +5344,10 @@ packages: resolution: {integrity: sha512-ZsDDduXaEF1SpyGz2OFuEU9Tzm0pKtbtCYviymiNtQS+3lx6rXyv+FaK0oIWn+gWL+gVNamplxKnNNR2jZsp5w==} cpu: [wasm32] + '@rspack/binding-wasm32-wasi@2.1.4': + resolution: {integrity: sha512-0P1WZEfu7JOPzD/jQfk9U/6gnRFc7RpvYCQaYZVVYZNJ2gU6O0/yLegRGKNK/2L0zjYwiD0ynhOIBVUUERf5Pw==} + cpu: [wasm32] + '@rspack/binding-win32-arm64-msvc@1.7.12': resolution: {integrity: sha512-8+h5fYDXYdmugbdfZ+D1y8IQ3rv2EhSfyGP7vBe+bjNyaMa4jWrpucmZbtxojUL1AzaeuHbvMdj9UO/gelk/+g==} cpu: [arm64] @@ -4728,6 +5363,11 @@ packages: cpu: [arm64] os: [win32] + '@rspack/binding-win32-arm64-msvc@2.1.4': + resolution: {integrity: sha512-+aStQipk1EakLRPfD+/aQbtmTXfxqSWetcRRDWV3cAsD4ebv1tF8FLKYY1PCaFpQbX1FxzCBGF0KHxkAsi9cxA==} + cpu: [arm64] + os: [win32] + '@rspack/binding-win32-ia32-msvc@1.7.12': resolution: {integrity: sha512-cDMGwTRSa2p9fNBVe1wTRkF2AEXZ9ARWW36QeC5CkLaI0Ezz8lvhF2+CSOPnhaQ1O1qtn0L0SF+lFnrY+I7xGQ==} cpu: [ia32] @@ -4743,6 +5383,11 @@ packages: cpu: [ia32] os: [win32] + '@rspack/binding-win32-ia32-msvc@2.1.4': + resolution: {integrity: sha512-7nyrLRQ07j6i6omZuwiwwTI6rpjdy/Su3niLDAG7WsLL21u3/UQQrw6Fvm8SH3XYteghoHLSM3dhgaisuUhdJg==} + cpu: [ia32] + os: [win32] + '@rspack/binding-win32-x64-msvc@1.7.12': resolution: {integrity: sha512-wIqFvlgFqrgUyj/6S/FJcvShnkZOmIeXTfqvheLY67MGq8qd8jb1YimQVKAIrmWB3yuJKUFACI3Ag1UBtEedEA==} cpu: [x64] @@ -4758,7 +5403,12 @@ packages: cpu: [x64] os: [win32] - '@rspack/binding@1.7.12': + '@rspack/binding-win32-x64-msvc@2.1.4': + resolution: {integrity: sha512-Z4je7JBaDpO9vvNMgCxlycycRJq+GQwwuXSXagW2xvvGM10Ij63YVtIehSMG9xaW8PED41oc9nWndoEsiD60pA==} + cpu: [x64] + os: [win32] + + '@rspack/binding@1.7.12': resolution: {integrity: sha512-f4HHuLbvuld8Ba4iB/4ibse5XrKxFrgmM3S4P2AOKnPlekAFlBjmltCuaTL/W2ggYvILaVY+YcFXrEH1rrKeQA==} '@rspack/binding@2.0.8': @@ -4767,6 +5417,9 @@ packages: '@rspack/binding@2.1.0': resolution: {integrity: sha512-LsXFIOOYDutHk44SAOcVQa5iA7lhYwEbD+nZhgmCiGJvKKh0UIpBj6EAsBsB6omEK5GEXvjDeLFieKgbYW08QQ==} + '@rspack/binding@2.1.4': + resolution: {integrity: sha512-iye4BaTYtTt0qa39avWwEsUobVxFNhQHr6B8reFeKU7sdaZsC9LUoDR8JAt5gOnbnzFMPdKgrdU/VzkC6rXbIg==} + '@rspack/core@1.7.12': resolution: {integrity: sha512-6CwFIHlhRmXfZoMj3v9MZ1SMTPBn+cHVXeMIeaGp5sufqinKsISbsqHu6ZMJu2wDSmZLdmQJX6zLxkhcAUlhkQ==} engines: {node: '>=18.12.0'} @@ -4800,6 +5453,18 @@ packages: '@swc/helpers': optional: true + '@rspack/core@2.1.4': + resolution: {integrity: sha512-lpJgtr+JAXuDAMBJfRJ1LHyWVuYJyhZu6L6aj9t4lipUU03qwakHnzn3vwSCr68PsVvVPzR6NbJE1gJienSV0g==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@module-federation/runtime-tools': ^0.24.1 || ^2.0.0 + '@swc/helpers': ^0.5.23 + peerDependenciesMeta: + '@module-federation/runtime-tools': + optional: true + '@swc/helpers': + optional: true + '@rspack/lite-tapable@1.1.0': resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} @@ -4868,6 +5533,19 @@ packages: '@rspack/resolver@0.2.8': resolution: {integrity: sha512-FBWqdHhzS8mcf/WN4Ktzr7EaeaN+hsxbN98EweegX3924beZuY6H70CSFWCv1fIHAieCUv/9XCjKggHvhCsLwA==} + '@rstest/core@0.11.4': + resolution: {integrity: sha512-HfBP4mmvLVq2vpyRvHHaeu1+K2e9XSjDCHbj15s+NExnniS2b+y/ABXyjJldpRSoiirKnfIqY7BVGOaO+G+W0w==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + happy-dom: ^20.8.3 + jsdom: '*' + peerDependenciesMeta: + happy-dom: + optional: true + jsdom: + optional: true + '@rstest/core@0.8.1': resolution: {integrity: sha512-7d/2fm2V91pVx/rRtZ2gl6Zh4hVMivtDl4RgHFhBOrxi//UwhKISeF5gS/CSwpCgfOf10TzJRXqdI17ueUBNMQ==} engines: {node: '>=18.12.0'} @@ -4881,6 +5559,11 @@ packages: jsdom: optional: true + '@rstest/coverage-istanbul@0.11.4': + resolution: {integrity: sha512-InsiiS3koVjBoiIlBDLcpGT8njCtmqWzr5NyY8abRlOux8MCaEyodialg6GZXy1PNhvR2FyCiy2kFFRxxNDjgw==} + peerDependencies: + '@rstest/core': ^0.11.4 + '@rstest/coverage-istanbul@0.2.0': resolution: {integrity: sha512-ghFY2XkjL2PFarLaG+BxFTJYbVXtAtcqW5rjOqheiOYV6PqlOHFf/s+fLkKeOTc1oVoL3A/9vjMBc8fOgjAF/A==} peerDependencies: @@ -4912,70 +5595,10 @@ packages: resolution: {integrity: sha512-snuk12ZaDerxesSnetNIwKoth/51R0y/h3eXD/bGtXp+hnSkeXN5HanI/RJl297llRjn4zJYRShW9Nx86Ay0Dw==} engines: {node: '>=18'} - '@sentry/babel-plugin-component-annotate@4.8.0': - resolution: {integrity: sha512-cy/9Eipkv23MsEJ4IuB4dNlVwS9UqOzI3Eu+QPake5BVFgPYCX0uP0Tr3Z43Ime6Rb+BiDnWC51AJK9i9afHYw==} - engines: {node: '>= 14'} - '@sentry/browser@10.37.0': resolution: {integrity: sha512-kheqJNqGZP5TSBCPv4Vienv1sfZwXKHQDYR+xrdHHYdZqwWuZMJJW/cLO9XjYAe+B9NnJ4UwJOoY4fPvU+HQ1Q==} engines: {node: '>=18'} - '@sentry/bundler-plugin-core@4.8.0': - resolution: {integrity: sha512-QaXd/NzaZ2vmiA2FNu2nBkgQU+17N3fE+zVOTzG0YK54QDSJMd4n3AeJIEyPhSzkOob+GqtO22nbYf6AATFMAw==} - engines: {node: '>= 14'} - - '@sentry/cli-darwin@2.58.6': - resolution: {integrity: sha512-udAVvcyfNa0R+95GvPz/+43/N3TC0TYKdkQ7D7jhPSzbcMc7l2fxRNN5yB3UpCA5fWFnW4toeaqwDBhb/Wh3LA==} - engines: {node: '>=10'} - os: [darwin] - - '@sentry/cli-linux-arm64@2.58.6': - resolution: {integrity: sha512-q8mEcNNmeXMy5i+jWT30TVpH7LcP4HD21CD5XRSPAd/a912HF6EpK0ybf/1USO14WOhoXbAGi9txwaWabSe33g==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux, freebsd, android] - - '@sentry/cli-linux-arm@2.58.6': - resolution: {integrity: sha512-pD0LAt5PcUzAinBwvDqc66x9+2CabHEv486yP0gRjWO7SakbaxmfVq/EXd8VLq/Tzi39LAu422UYK1lpW3MILw==} - engines: {node: '>=10'} - cpu: [arm] - os: [linux, freebsd, android] - - '@sentry/cli-linux-i686@2.58.6': - resolution: {integrity: sha512-q8vNJi1eOV/4vxAFWBsEwLHoSYapaZHIf4j76KJGJXFKTkEbsjCOOsKbwUIBTQQhRgV4DFWh3ryfsPS/que4Kg==} - engines: {node: '>=10'} - cpu: [x86, ia32] - os: [linux, freebsd, android] - - '@sentry/cli-linux-x64@2.58.6': - resolution: {integrity: sha512-DZu956Mhi3ZRjTBe1WdbGV46ldVbA8d2rgp/fh51GsI25zjBHah4wZnPTSzpc+YqxU6pJpg579B/r3jrIK530Q==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux, freebsd, android] - - '@sentry/cli-win32-arm64@2.58.6': - resolution: {integrity: sha512-nj0Ff/kmAB73EPDhR8B4O9r+NUHK5GkPCkGWC+kXVemqAJWL5jcJ5KdxG0l/S0z6RoEoltID8/43/B+TaMlT7A==} - engines: {node: '>=10'} - cpu: [arm64] - os: [win32] - - '@sentry/cli-win32-i686@2.58.6': - resolution: {integrity: sha512-WNZiDzPbgsEMQWq4avsQ391v/xWKJDIWWWo9GYl+N/w5qcYKkoDW7wQG7T9FasI6ENn68phChTOAPXXxbfAdOg==} - engines: {node: '>=10'} - cpu: [x86, ia32] - os: [win32] - - '@sentry/cli-win32-x64@2.58.6': - resolution: {integrity: sha512-R35WJ17oF4D2eqI1DR2sQQqr0fjRTt5xoP16WrTu91XM2lndRMFsnjh+/GttbxapLCBNlrjzia99MJ0PZHZpgA==} - engines: {node: '>=10'} - cpu: [x64] - os: [win32] - - '@sentry/cli@2.58.6': - resolution: {integrity: sha512-baBcNPLLfUi9WuL+Tpri9BFaAdvugZIKelC5X0tt0Zdy+K0K+PCVSrnNmwMWU/HyaF/SEv6b6UHnXIdqanBlcg==} - engines: {node: '>= 10'} - hasBin: true - '@sentry/core@10.37.0': resolution: {integrity: sha512-hkRz7S4gkKLgPf+p3XgVjVm7tAfvcEPZxeACCC6jmoeKhGkzN44nXwLiqqshJ25RMcSrhfFvJa/FlBg6zupz7g==} engines: {node: '>=18'} @@ -5017,9 +5640,14 @@ packages: peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x - '@sentry/vite-plugin@4.8.0': - resolution: {integrity: sha512-/YZJitGsx/o72FFQYy3tucUfs4w3COvSI1d2NYyAhIzay4tjLLRjpM5PdwFnoBT7Uj/7jSbuHkg87PAliLiu2g==} - engines: {node: '>= 14'} + '@sideway/address@4.1.5': + resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} + + '@sideway/formula@3.0.1': + resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} + + '@sideway/pinpoint@2.0.0': + resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} '@sindresorhus/is@7.2.0': resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} @@ -5154,132 +5782,63 @@ packages: peerDependencies: postcss: ^8.2.15 - '@tailwindcss/node@4.3.1': - resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} - '@tailwindcss/node@4.3.2': resolution: {integrity: sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==} - '@tailwindcss/oxide-android-arm64@4.3.1': - resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [android] - '@tailwindcss/oxide-android-arm64@4.3.2': resolution: {integrity: sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==} engines: {node: '>= 20'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.3.1': - resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [darwin] - '@tailwindcss/oxide-darwin-arm64@4.3.2': resolution: {integrity: sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.3.1': - resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==} - engines: {node: '>= 20'} - cpu: [x64] - os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.3.2': resolution: {integrity: sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.3.1': - resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==} - engines: {node: '>= 20'} - cpu: [x64] - os: [freebsd] - '@tailwindcss/oxide-freebsd-x64@4.3.2': resolution: {integrity: sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': - resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==} - engines: {node: '>= 20'} - cpu: [arm] - os: [linux] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': resolution: {integrity: sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==} engines: {node: '>= 20'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': - resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': resolution: {integrity: sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.3.1': - resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.3.2': resolution: {integrity: sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.3.1': - resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==} - engines: {node: '>= 20'} - cpu: [x64] - os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.3.2': resolution: {integrity: sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==} engines: {node: '>= 20'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.3.1': - resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==} - engines: {node: '>= 20'} - cpu: [x64] - os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.3.2': resolution: {integrity: sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==} engines: {node: '>= 20'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.3.1': - resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] - bundledDependencies: - - '@napi-rs/wasm-runtime' - - '@emnapi/core' - - '@emnapi/runtime' - - '@tybys/wasm-util' - - '@emnapi/wasi-threads' - - tslib - '@tailwindcss/oxide-wasm32-wasi@4.3.2': resolution: {integrity: sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==} engines: {node: '>=14.0.0'} @@ -5292,40 +5851,24 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': - resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==} - engines: {node: '>= 20'} - cpu: [arm64] - os: [win32] - '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': resolution: {integrity: sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.3.1': - resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==} - engines: {node: '>= 20'} - cpu: [x64] - os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.3.2': resolution: {integrity: sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==} engines: {node: '>= 20'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.3.1': - resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==} - engines: {node: '>= 20'} - '@tailwindcss/oxide@4.3.2': resolution: {integrity: sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==} engines: {node: '>= 20'} - '@tailwindcss/postcss@4.3.1': - resolution: {integrity: sha512-dNJuNbdEJT/SWRuXTYP1WSamelsz3ztkUsdtWQPjrexysrTpaEPM40P/71knXiXLYEojqPOEGitVLLpPMS5T6A==} + '@tailwindcss/postcss@4.3.2': + resolution: {integrity: sha512-rjVWYCa7Ngbi5AarT6k8TkxUG3Wl1QKzHdIZVsjZSzf36Jmo2IKZt/NHRAwly8oDkbBOH0YTu+CHuf9jPxMc+g==} '@tailwindcss/webpack@4.3.2': resolution: {integrity: sha512-aI9G1d0QAZu1AlctpsIkpo/y5rWV2d1PZ7m/OOKlbgWZnxN4ZUa2lseu65QAbrp/eeDXOFHdTk6HDnSy3Yy5cA==} @@ -5468,15 +6011,27 @@ packages: '@types/cors@2.8.19': resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + '@types/cross-spawn@6.0.6': + resolution: {integrity: sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==} + '@types/d3-hierarchy@1.1.11': resolution: {integrity: sha512-lnQiU7jV+Gyk9oQYk0GGYccuexmQPTp08E0+4BidgFdiJivjEvf+esPSdZqCZ2C7UwTWejWpqetVaU8A+eX3FA==} + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/dedent@0.7.2': + resolution: {integrity: sha512-kRiitIeUg1mPV9yH4VUJ/1uk2XjyANfeL8/7rH1tsjvHeO9PJLBHJIYsFWmAvmGj5u8rj+1TZx7PZzW2qLw3Lw==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} '@types/eslint@9.6.1': resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} @@ -5496,6 +6051,9 @@ packages: resolution: {integrity: sha512-00UxlRaIUvYm4R4W9WYkN8/J+kV8fmOQ7okeH6YFtGWFMt3odD45tpG5yA5wnL7HE6lLgjaTW5n14ju2hl2NNA==} deprecated: This is a stub types definition. glob provides its own type definitions, so you do not need this installed. + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/http-errors@2.0.5': resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} @@ -5508,9 +6066,18 @@ packages: '@types/jsonfile@6.1.4': resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdx@2.0.14': + resolution: {integrity: sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==} + '@types/morgan@1.9.10': resolution: {integrity: sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==} + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/mysql@2.15.27': resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} @@ -5520,6 +6087,9 @@ packages: '@types/node@22.15.31': resolution: {integrity: sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==} + '@types/node@22.20.0': + resolution: {integrity: sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==} + '@types/node@25.0.10': resolution: {integrity: sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==} @@ -5563,6 +6133,9 @@ packages: '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + '@types/send@1.2.1': resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} @@ -5572,6 +6145,9 @@ packages: '@types/set-cookie-parser@2.4.10': resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==} + '@types/shelljs@0.10.0': + resolution: {integrity: sha512-OEfyhE5Ox+FeoHbhrEDwm0kXxntO6nsyMRCFvNsIBHPZu5rV1w2OjPcLclaC/IZ1TlzZPgbeMfwAZEi5N238yQ==} + '@types/source-map-support@0.5.10': resolution: {integrity: sha512-tgVP2H469x9zq34Z0m/fgPewGhg/MLClalNOiPIzQlXrSS2YrKu/xCdSCKnEDwkFha51VKEKB6A9wW26/ZNwzA==} @@ -5587,68 +6163,83 @@ packages: '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/wait-on@5.3.4': + resolution: {integrity: sha512-EBsPjFMrFlMbbUFf9D1Fp+PAB2TwmUn7a3YtHyD9RLuTIk1jDd8SxXVAoez2Ciy+8Jsceo2MYEYZzJ/DvorOKw==} + + '@types/webpack-env@1.18.8': + resolution: {integrity: sha512-G9eAoJRMLjcvN4I08wB5I7YofOb/kaJNd5uoCMX+LbKXTPCF+ZIHuqTnFaK9Jz1rgs035f9JUPUhNFtqgucy/A==} + '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/eslint-plugin@8.62.0': - resolution: {integrity: sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==} + '@typescript-eslint/eslint-plugin@8.62.1': + resolution: {integrity: sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.62.0 + '@typescript-eslint/parser': ^8.62.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.62.0': - resolution: {integrity: sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==} + '@typescript-eslint/parser@8.62.1': + resolution: {integrity: sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.62.0': - resolution: {integrity: sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==} + '@typescript-eslint/project-service@8.62.1': + resolution: {integrity: sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.62.0': - resolution: {integrity: sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==} + '@typescript-eslint/scope-manager@8.62.1': + resolution: {integrity: sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.62.0': - resolution: {integrity: sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==} + '@typescript-eslint/tsconfig-utils@8.62.1': + resolution: {integrity: sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.62.0': - resolution: {integrity: sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==} + '@typescript-eslint/type-utils@8.62.1': + resolution: {integrity: sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.62.0': - resolution: {integrity: sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==} + '@typescript-eslint/types@8.62.1': + resolution: {integrity: sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.62.0': - resolution: {integrity: sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==} + '@typescript-eslint/typescript-estree@8.62.1': + resolution: {integrity: sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.62.0': - resolution: {integrity: sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==} + '@typescript-eslint/utils@8.62.1': + resolution: {integrity: sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.62.0': - resolution: {integrity: sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==} + '@typescript-eslint/visitor-keys@8.62.1': + resolution: {integrity: sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.2': + resolution: {integrity: sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA==} + '@unrs/resolver-binding-android-arm-eabi@1.12.2': resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} cpu: [arm] @@ -5759,11 +6350,22 @@ packages: cpu: [x64] os: [win32] - '@vitejs/plugin-react@5.1.2': - resolution: {integrity: sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==} - engines: {node: ^20.19.0 || >=22.12.0} + '@vanilla-extract/babel-plugin-debug-ids@1.2.2': + resolution: {integrity: sha512-MeDWGICAF9zA/OZLOKwhoRlsUW+fiMwnfuOAqFVohL31Agj7Q/RBWAYweqjHLgFBCsdnr6XIfwjJnmb2znEWxw==} + + '@vanilla-extract/css@1.21.1': + resolution: {integrity: sha512-T6z6oeT+o0OwqukE6kLs9w5ooB75b8NPRadyU9pPTml83l40TPN+mscPt2OQ9P9cYfwEBMfsfivwiJujs2mzmg==} + + '@vanilla-extract/integration@8.0.10': + resolution: {integrity: sha512-01IB5gbrgTe8IIrtfRXXTmACl5D8Enzqp2cKbCWaMKXmnoilXXVCPbJoA96q88PXkNDXsXepCxUugMvEmL3c7A==} + + '@vanilla-extract/private@1.0.9': + resolution: {integrity: sha512-gT2jbfZuaaCLrAxwXbRgIhGhcXbRZCG3v4TTUnjw0EJ7ArdBRxkq4msNJkbuRkCgfIK5ATmprB5t9ljvLeFDEA==} + + '@vanilla-extract/webpack-plugin@2.3.27': + resolution: {integrity: sha512-i/GFQPifTkGYXYpkVYzScBPwXB82f+5GMLg9rh9UBgTG6M/YUebIDVjVOC9Mn5VMvc8Fk2w/FmagDRLugHA09w==} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + webpack: ^4.30.0 || ^5.20.2 '@vitest/eslint-plugin@1.6.20': resolution: {integrity: sha512-xRwWHFG0Utp6hXtbGiWk4VdKXCGdExD8kbWrrmFEiG5dk8anOJ+vbWbeOa8EbkocKQRTsx7JAWETccZiBgFp/Q==} @@ -6112,10 +6714,6 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - anynum@1.0.1: resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==} @@ -6184,10 +6782,17 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + autoprefixer@10.4.23: resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} engines: {node: ^10 || ^12 || >=14} @@ -6199,12 +6804,18 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + axios@1.18.1: + resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==} + babel-dead-code-elimination@1.0.12: resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} babel-plugin-formatjs@10.5.41: resolution: {integrity: sha512-ZpozYGek+Bdyl52LgzW1MhPYBRKbROdbHBuBz7KAO88Ht7GcyCBiwJxpAjVDb0YBA9LGKUemGQOLdEDkRCe2hg==} + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -6226,13 +6837,13 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.10.40: - resolution: {integrity: sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==} + baseline-browser-mapping@2.10.41: + resolution: {integrity: sha512-WwS7MHhqGHHlaVsqRZnhvCEMS0owDX+SxRlve7JkuH7My1Ara3ZriTmCQupPfYjxMZ8I/tgxtJYr2t7taHaH4A==} engines: {node: '>=6.0.0'} hasBin: true - baseline-browser-mapping@2.10.42: - resolution: {integrity: sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==} + baseline-browser-mapping@2.10.44: + resolution: {integrity: sha512-T3ghW+sl/ZJ8w1v/yQx3qvJ9040DWoLBz8JT/CILbAKcFyG9b2MRe75v6W5uXjv6uH1lumK2Kv46y2zSkcej0Q==} engines: {node: '>=6.0.0'} hasBin: true @@ -6265,10 +6876,6 @@ packages: bignumber.js@9.3.1: resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} @@ -6305,8 +6912,8 @@ packages: brace-expansion@2.1.1: resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} - brace-expansion@5.0.6: - resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + brace-expansion@5.0.7: + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -6325,8 +6932,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - browserslist@4.28.5: - resolution: {integrity: sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==} + browserslist@4.28.6: + resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -6379,11 +6986,14 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} - caniuse-lite@1.0.30001799: - resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + caniuse-lite@1.0.30001800: + resolution: {integrity: sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==} + + caniuse-lite@1.0.30001806: + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} - caniuse-lite@1.0.30001803: - resolution: {integrity: sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} chain-function@1.0.1: resolution: {integrity: sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg==} @@ -6408,12 +7018,27 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + chardet@2.2.0: resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.2.0: + resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==} + engines: {node: '>=20.18.1'} chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} @@ -6481,6 +7106,9 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + collapse-white-space@2.1.0: + resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -6497,6 +7125,13 @@ packages: colorjs.io@0.5.2: resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -6531,6 +7166,9 @@ packages: engines: {node: '>=18'} hasBin: true + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + confbox@0.2.4: resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} @@ -6557,6 +7195,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + cookie-signature@1.0.7: resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} @@ -6752,6 +7393,9 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -6775,6 +7419,9 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deep-object-diff@1.1.9: + resolution: {integrity: sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -6790,6 +7437,10 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -6813,6 +7464,9 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dijkstrajs@1.0.3: resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} @@ -6858,10 +7512,6 @@ packages: dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - dotenv@17.2.3: resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} @@ -6882,14 +7532,14 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - effect@3.21.4: - resolution: {integrity: sha512-B89v/xSgPbl1J2Ai2u18jxq3odpFauU1rC6/eSs4FeNHi72kwKdJp12VGigvRV2lK+kRnx+OOz41XV8guZd4gQ==} + effect@3.22.0: + resolution: {integrity: sha512-jhYFe0zTlIRqYFrKTS+6luhmS/Tm0f+JLo0K9KUxvtFab1SUGEszQi2ehOP6QzAZvy831lDmTwwzvVDZSPNz3g==} - electron-to-chromium@1.5.380: - resolution: {integrity: sha512-W6d5AbuEoRayO447cqrg6lKJIlscgRnnxOZl/08kfV71BQDoEBC7Wwis68z87LjyK6f4kWyTaubuDbhHKrZkbA==} + electron-to-chromium@1.5.385: + resolution: {integrity: sha512-78sa/M08MNAYHQfjoWMvOlKQqZ0ElhSm/L5HNUc96VZ3b+KvDVnngFm8sYQy0XrhTRgAhggHr5abA7yTvRdo4Q==} - electron-to-chromium@1.5.389: - resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==} + electron-to-chromium@1.5.393: + resolution: {integrity: sha512-kiDJdIUawuEIcp9XoICKp1iTYDEbgguIPq526N1Q7jIQDeQ3CqoMx71025PI/7E48Ddtw2HuWsVjY7afEgNxmg==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -6905,6 +7555,9 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} @@ -6926,8 +7579,8 @@ packages: resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} engines: {node: '>=10.13.0'} - enhanced-resolve@5.24.2: - resolution: {integrity: sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==} + enhanced-resolve@5.24.3: + resolution: {integrity: sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==} engines: {node: '>=10.13.0'} enquirer@2.4.1: @@ -6942,6 +7595,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + entities@8.0.0: resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} engines: {node: '>=20.19.0'} @@ -6987,12 +7644,12 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - es-module-lexer@2.1.0: - resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} - es-module-lexer@2.3.0: resolution: {integrity: sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==} + es-module-lexer@2.3.1: + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} + es-object-atoms@1.1.2: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} @@ -7012,6 +7669,12 @@ packages: es-toolkit@1.49.0: resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==} + esast-util-from-estree@2.0.0: + resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} + + esast-util-from-js@2.0.1: + resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + esbuild@0.27.2: resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} @@ -7148,6 +7811,30 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-util-attach-comments@3.0.0: + resolution: {integrity: sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw==} + + estree-util-build-jsx@3.0.1: + resolution: {integrity: sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ==} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-util-scope@1.0.0: + resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} + + estree-util-to-js@2.0.0: + resolution: {integrity: sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==} + + estree-util-visit@2.0.0: + resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -7156,6 +7843,10 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + eval@0.1.8: + resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==} + engines: {node: '>= 0.8'} + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -7205,6 +7896,9 @@ packages: exsolve@1.1.0: resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -7225,8 +7919,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-uri@3.1.2: - resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-uri@3.1.3: + resolution: {integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==} fast-xml-builder@1.2.0: resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==} @@ -7297,6 +7991,15 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -7305,6 +8008,10 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + forwarded-parse@2.1.2: resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} @@ -7315,8 +8022,8 @@ packages: fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} - framer-motion@12.42.0: - resolution: {integrity: sha512-wp7EJnfWaaEScVygKv3e20udoRz+LbtxScsuTkakAxfXmt+ReC6WyPW2nINRAGvd+hG9odwcjBLyOTPjH5pBRA==} + framer-motion@12.42.2: + resolution: {integrity: sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -7400,6 +8107,10 @@ packages: resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} engines: {node: '>=16'} + get-port@7.2.0: + resolution: {integrity: sha512-afP4W205ONCuMoPBqcR6PSXnzX35KTcJygfJfcp+QY+uwm3p20p1YczWXhlICIzGMCxYBQcySEcOgsJcrkyobg==} + engines: {node: '>=16'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -7470,9 +8181,6 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} - globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - goober@2.1.19: resolution: {integrity: sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg==} peerDependencies: @@ -7520,6 +8228,15 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} + hast-util-to-estree@3.1.3: + resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + he@1.2.0: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true @@ -7552,6 +8269,9 @@ packages: htmlparser2@10.0.0: resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + htmlparser2@10.1.0: + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} @@ -7618,8 +8338,8 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immutable@5.1.8: - resolution: {integrity: sha512-TM5YqrGeTsVIPPpILzeqZ8D2Zc2TvNgSDi88zPF2a4cyqQdWV/wVWBDRDbNzzrLeRWScrFcOX9lW2iX6GOtUDw==} + immutable@5.1.9: + resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -7646,6 +8366,9 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + input-otp@1.4.2: resolution: {integrity: sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==} peerDependencies: @@ -7671,6 +8394,12 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -7686,10 +8415,6 @@ packages: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-boolean-object@1.2.2: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} @@ -7710,6 +8435,9 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -7739,6 +8467,9 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} @@ -7893,6 +8624,9 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + javascript-stringify@2.1.0: + resolution: {integrity: sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==} + jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} @@ -7905,6 +8639,13 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + joi@17.13.4: + resolution: {integrity: sha512-1RuuER6kmt8K8I3nIWvPZKi5RQCb568ZPyY4Pwjlua+yo+63ZTmIwxLZH0heBmiKN4uxjvCiarDrjaeH84xicQ==} + + joi@18.2.3: + resolution: {integrity: sha512-N5A3KTWQpPWT4ExxxPlUx7WmykGXRzhNidWhV41d6Abu9YfI2NyWCJuxdPnslJCPWtbRpSVOWSnSS6GakLM/Rg==} + engines: {node: '>= 20'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -8115,8 +8856,8 @@ packages: resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - linkify-it@5.0.1: - resolution: {integrity: sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==} + linkify-it@5.0.2: + resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} litefs-js@2.0.2: resolution: {integrity: sha512-/tsWuxjjp+xokvbCCtnyMJaTAbdmb3n9WS10ZB1/tBaa355qAXJP0Xlg37aO5YNTsJnl2zixpRGcmjcQwIZsdw==} @@ -8161,6 +8902,9 @@ packages: long-timeout@0.1.1: resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -8196,10 +8940,6 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magic-string@0.30.8: - resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} - engines: {node: '>=12'} - make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -8213,6 +8953,10 @@ packages: engines: {node: '>=12.0.0'} hasBin: true + markdown-extensions@2.0.0: + resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} + engines: {node: '>=16'} + markdown-it@14.2.0: resolution: {integrity: sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==} hasBin: true @@ -8231,6 +8975,33 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdx@3.0.0: + resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + mdn-data@2.0.28: resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} @@ -8243,6 +9014,9 @@ packages: mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + media-query-parser@2.0.2: + resolution: {integrity: sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -8273,6 +9047,90 @@ packages: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-mdx-expression@3.0.1: + resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} + + micromark-extension-mdx-jsx@3.0.2: + resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} + + micromark-extension-mdx-md@2.0.0: + resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} + + micromark-extension-mdxjs-esm@3.0.0: + resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} + + micromark-extension-mdxjs@3.0.0: + resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-mdx-expression@2.0.3: + resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-events-to-acorn@2.0.3: + resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -8322,8 +9180,8 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - miniflare@4.20260625.0: - resolution: {integrity: sha512-3kKXwRUObJsnBYPBgR0NiNZYKF/yv8GFyha1cx2EeAEraxNODgRVcyeRo+F1ok1tg5Mg7iUpOWSkknQTHuFhwA==} + miniflare@4.20260701.0: + resolution: {integrity: sha512-L6eAAi6IKtyb/7J6L+YsH2vb1yBrJWKRXI293JYDiMl70+6nncdAgigex58w6WBd+CwvdMsqOyNyGs95Op5gWQ==} engines: {node: '>=22.0.0'} hasBin: true @@ -8391,7 +9249,13 @@ packages: mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - module-details-from-path@1.0.4: + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + + modern-ahocorasick@1.1.0: + resolution: {integrity: sha512-sEKPVl2rM+MNVkGQt3ChdmD8YsigmXdn5NifZn6jiwn9LRJpWm8F3guhaqrJT/JOat6pwpbXEk6kv+b9DMIjsQ==} + + module-details-from-path@1.0.4: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} monaco-editor@0.55.1: @@ -8404,8 +9268,8 @@ packages: resolution: {integrity: sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==} engines: {node: '>= 0.8.0'} - motion-dom@12.42.0: - resolution: {integrity: sha512-M63h4n8R+quJdNhBwuLlgxM+OLYa9+I/T2pzDRboB9fLXRdbou+Gw7Zury+SkpaCyACP1JHSjHgZ1EgTkBr30w==} + motion-dom@12.42.2: + resolution: {integrity: sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA==} motion-utils@12.39.0: resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==} @@ -8480,8 +9344,8 @@ packages: no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - node-abi@3.92.0: - resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==} + node-abi@3.94.0: + resolution: {integrity: sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==} engines: {node: '>=10'} node-addon-api@7.1.1: @@ -8522,10 +9386,6 @@ packages: normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - npm-run-all@4.1.5: resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} engines: {node: '>= 4'} @@ -8642,8 +9502,8 @@ packages: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} - p-map@7.0.4: - resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} + p-map@7.0.5: + resolution: {integrity: sha512-e8vJF4XdVkzqqSHguEMz41mQO1wKwxKm5ENrUJQUu9kLDCtn83cxbyHZcszr4QC5zEA7WffRRC4gsTecC7J9oA==} engines: {node: '>=18'} p-try@2.2.0: @@ -8663,6 +9523,9 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-json@4.0.0: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} engines: {node: '>=4'} @@ -8683,6 +9546,15 @@ packages: resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} engines: {node: '>=0.10.0'} + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + parse5@8.0.1: resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} @@ -8784,8 +9656,8 @@ packages: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} pidtree@0.3.1: @@ -8793,6 +9665,10 @@ packages: engines: {node: '>=0.10'} hasBin: true + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + pify@3.0.0: resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} engines: {node: '>=4'} @@ -8805,6 +9681,9 @@ packages: resolution: {integrity: sha512-u9mdErTewKSMsr+ceCt8VcNuNP0ro5AXiPXhUVApuEyqr2Zlvt+DdCFBcm+yGWN8mhOdZJ27meIDbnoZgfzpOw==} hasBin: true + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + pkg-types@2.3.1: resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} @@ -8826,6 +9705,12 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} + postcss-import@16.1.1: + resolution: {integrity: sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + postcss: ^8.0.0 + postcss-nested@5.0.6: resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==} engines: {node: '>=12.0'} @@ -8839,10 +9724,6 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.15: - resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.16: resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} engines: {node: ^10 || ^12 || >=14} @@ -9005,8 +9886,8 @@ packages: engines: {node: '>=14'} hasBin: true - prettier@3.9.1: - resolution: {integrity: sha512-ppiDo2CSwexck1eyZUwJHg/N3nf1+6IRCv7W/VJ5vaLnVCmB7+3CdRfMwoCHBBX6xTrREDTksZ4OZl5SSf4zXA==} + prettier@3.9.4: + resolution: {integrity: sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==} engines: {node: '>=14'} hasBin: true @@ -9030,10 +9911,6 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -9041,6 +9918,9 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -9048,8 +9928,9 @@ packages: proxy-compare@3.0.1: resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} @@ -9090,6 +9971,9 @@ packages: resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} engines: {node: '>=0.12'} + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + range-parser@1.2.0: resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==} engines: {node: '>= 0.6'} @@ -9196,8 +10080,8 @@ packages: react: '>=16.8' react-dom: '>=16.8' - react-router-dom@7.18.0: - resolution: {integrity: sha512-Fi0yY6kgtKae/Th2xibdWK0KSdYZ4B53Gyf6wRtomOKWgpNm7H7+DyfDhncdz9FKbpS+1jmDhg3F4WoGJ+yFOA==} + react-router-dom@7.18.1: + resolution: {integrity: sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==} engines: {node: '>=20.0.0'} peerDependencies: react: '>=18' @@ -9219,8 +10103,8 @@ packages: react-dom: optional: true - react-router@7.18.0: - resolution: {integrity: sha512-pTTGt8J+ji1NOmYnjzT+bAJy/1zD+Jp4ziO6cL7T3ZLvXKtusO7BpFqlRXitqpcPVqllsIXFHRMt+2/k3Xn6HQ==} + react-router@7.18.1: + resolution: {integrity: sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==} engines: {node: '>=20.0.0'} peerDependencies: react: '>=18' @@ -9229,6 +10113,24 @@ packages: react-dom: optional: true + react-router@8.1.0: + resolution: {integrity: sha512-Mdfi61uObuvWNN9OhChOC0HV6YWOIfKRzEWOvCHRSuQg8IM+Nv10edaM/2HE8ZixBpUTdQbruyWqC3sDkkh9vw==} + engines: {node: '>=22.22.0'} + peerDependencies: + react: '>=19.2.7' + react-dom: '>=19.2.7' + peerDependenciesMeta: + react-dom: + optional: true + + react-server-dom-rspack@0.0.2: + resolution: {integrity: sha512-PowIm+Z65LkvC3yk4t3YrH2nMejwL361jiBT0BMW2bQCDUaNQknDHMPH0aTgMtJ9ZZ1Gc7KFgBvwB0itSzzELQ==} + engines: {node: '>=0.10.0'} + peerDependencies: + '@rspack/core': ^2.0.0-0 + react: ^19.1.0 + react-dom: ^19.1.0 + react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} @@ -9253,6 +10155,9 @@ packages: resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + read-pkg@3.0.0: resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} engines: {node: '>=4'} @@ -9268,10 +10173,6 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -9280,6 +10181,20 @@ packages: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.0'} + recma-build-jsx@1.0.0: + resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} + + recma-jsx@1.0.1: + resolution: {integrity: sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + recma-parse@1.0.0: + resolution: {integrity: sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ==} + + recma-stringify@1.0.0: + resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -9302,6 +10217,18 @@ packages: resolution: {integrity: sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==} engines: {node: '>=0.10.0'} + rehype-recma@1.0.0: + resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + + remark-mdx@3.1.1: + resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + remix-auth-github@3.0.2: resolution: {integrity: sha512-3XxykdwMrcPSyMsdGtBDl3DBc19gJM3t7q/1uzfz3g/SJRsxEytjGiQ17ztKykebCGM454Z0lVJMvSb+LF/yHA==} engines: {node: ^18.0.0 || ^20.0.0 || >=20.0.0} @@ -9365,6 +10292,9 @@ packages: resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} + require-like@0.1.2: + resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} + require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} @@ -9445,6 +10375,12 @@ packages: typescript: optional: true + rsbuild-plugin-rsc@0.1.1: + resolution: {integrity: sha512-aDITXfKR7D6LudDZeErVIzftgVRK6qoA77Mt/Lnp90qXgRRT3X63yOq4bx8bRPeFXIp1Rp+KS3VJq1K9oWmkMQ==} + peerDependencies: + '@rsbuild/core': ^2.0.0 + react-server-dom-rspack: '*' + rslog@2.1.3: resolution: {integrity: sha512-DCUkRKUBR1lSpHKRcxNvHaYwGrUVf9MsoE1u6gd0CF37I8vwwtWc4b+FA9OwYZ4QA/shslzAYorD3MMfd+Rs/Q==} engines: {node: ^20.19.0 || >=22.12.0} @@ -9638,14 +10574,17 @@ packages: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} - seroval-plugins@1.5.5: - resolution: {integrity: sha512-+BDhqYM6CEn3x09v44dpa9p6974FuUB2dxk+Ctn04k0cO1Zt6QODTXfmEZK0eBaTe/fJBvP4NMGuNJ+R8T+QMg==} + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + seroval-plugins@1.5.6: + resolution: {integrity: sha512-HXuLAX2pu/UByPpaeo/TaMfvMIi+1QqIoPJYCcAtU8QkVNwgR6MPlGuCQTErV1JwraaMbYaWVIBX7mppzGLATQ==} engines: {node: '>=10'} peerDependencies: seroval: ^1.0 - seroval@1.5.5: - resolution: {integrity: sha512-bSjOuPcwPKLSJNhr9+bZxA20nQxVle5J5MNsYRVE6cIg7KpRLXGupymePavu0jrxlPiPsr4xGZSB8yUY2sH2sw==} + seroval@1.5.6: + resolution: {integrity: sha512-rVQVWjjSvlINzaQPZH5JFqsqEsIWdTxY3iJZCnTL/5gQbXIRooVZKI60tVCkOVfzcRPejboxO2t0P89dg5mQaA==} engines: {node: '>=10'} serve-handler@6.1.7: @@ -9722,6 +10661,10 @@ packages: resolution: {integrity: sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==} engines: {node: '>= 0.4'} + shelljs@0.10.0: + resolution: {integrity: sha512-Jex+xw5Mg2qMZL3qnzXIfaxEtBaC4n7xifqaqtrZDdlheR70OGkydrPJWT0V1cA1k3nanC86x9FwAmQl6w3Klw==} + engines: {node: '>=18'} + side-channel-list@1.0.1: resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} @@ -9772,8 +10715,8 @@ packages: resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==} engines: {node: '>=10.2.0'} - solid-js@1.9.13: - resolution: {integrity: sha512-6hJeJMOcEX8ktqjpDoJZEmld3ijvcvWBDtiXBm7f4332SiFN66QeAQI1REQshvyUoISsSeJ4PHDauKYbwao9JQ==} + solid-js@1.9.14: + resolution: {integrity: sha512-sAEXC0Kk0S1EDg+8ysEWJDbYhA3RRoEjwuySUGlKIemeo0I5YZfOyumNjNs9Sv3y2nmhD+0rW66ag2HsMuQiGQ==} sonner@2.0.7: resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} @@ -9799,6 +10742,9 @@ packages: resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} engines: {node: '>= 12'} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} @@ -9888,6 +10834,9 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -9916,6 +10865,10 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} + engines: {node: '>=12'} + strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -9927,6 +10880,12 @@ packages: strnum@2.4.1: resolution: {integrity: sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==} + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + supports-color@10.2.2: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} @@ -10031,11 +10990,11 @@ packages: resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} - tldts-core@7.4.5: - resolution: {integrity: sha512-pGrwzZDvPwKe+7NNUqAunb6rqTfynr0VOUhCMdqbu5xlvNiszsAJygRzwvpVycdzejlbpY+SWJOn+s75Og7FEA==} + tldts-core@7.4.6: + resolution: {integrity: sha512-TkQNGJIhlEphpHCjKodMTSe23egUZr/g+flI2qkLgiJ/maAzSgXypSLRTNH3nCmqgayEmtcJBiLcfODSAr1xoA==} - tldts@7.4.5: - resolution: {integrity: sha512-RfEzKWcq5fHUOFq7J3rl3Oz6ylKGtcHqUznzj4EcXsxLSIjJcvpbXAQtWGeJQ0xKnimR5e0Cn+cn9TssfMzm+g==} + tldts@7.4.6: + resolution: {integrity: sha512-rbP0Gyx8b3Ae9yO//CU2wbSnQNoQ66m1nJdSbSHmnwKwzkkz/u8mERYU8T2rmlmy+bJvRNn84yNCW8gYqox44Q==} hasBin: true to-data-view@2.0.0: @@ -10065,23 +11024,18 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' - tsconfck@3.1.6: - resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} - engines: {node: ^18 || >=20} - deprecated: unmaintained - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -10146,8 +11100,8 @@ packages: resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} engines: {node: '>= 0.4'} - typescript-eslint@8.62.0: - resolution: {integrity: sha512-8QxXi+ZACKX0kaqO4gY8kn0RSD9gFfaHDWwjqtEN48aWCBkX4MJaufWN+c3BzlrXLOxfywDL8CaoqUwcRq4j4Q==} + typescript-eslint@8.62.1: + resolution: {integrity: sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -10166,6 +11120,9 @@ packages: uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} @@ -10197,6 +11154,27 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position-from-estree@2.0.0: + resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -10209,9 +11187,6 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - unplugin@1.0.1: - resolution: {integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==} - unrs-resolver@1.12.2: resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} @@ -10296,21 +11271,17 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite-env-only@3.0.3: - resolution: {integrity: sha512-iAb7cTXRrvFShaF1n+G8f6Yqq7sRJcxipNYNQQu0DN5N9P55vJMmLG5lNU5moYGpd+ZH1WhBHdkWi5WjrfImHg==} - peerDependencies: - vite: '*' + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite-tsconfig-paths@6.1.1: - resolution: {integrity: sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==} - peerDependencies: - vite: '*' - vite@7.3.1: resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -10355,6 +11326,16 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + wait-on@7.2.0: + resolution: {integrity: sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==} + engines: {node: '>=12.0.0'} + hasBin: true + + wait-on@9.0.10: + resolution: {integrity: sha512-rCoJEhvMr0X6alHmwc9abbrA5ZrLZFKpFQVKPNFwl2h7DapXOGdmimIHDtLOWhT4PjhZhxFEtZoQgEXbkDWdZw==} + engines: {node: '>=20.0.0'} + hasBin: true + warning@3.0.0: resolution: {integrity: sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==} @@ -10372,19 +11353,12 @@ packages: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} - webpack-sources@3.5.0: - resolution: {integrity: sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==} - engines: {node: '>=10.13.0'} - webpack-sources@3.5.1: resolution: {integrity: sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==} engines: {node: '>=10.13.0'} - webpack-virtual-modules@0.5.0: - resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} - - webpack@5.108.1: - resolution: {integrity: sha512-UUCihHQK3O7483Woa0SulNLDeAiOhHI2PN2PAPU4fVWJqbzhv04EJ8FaWtB9WWh3i8fRt28543U7VfuJTOrpgQ==} + webpack@5.108.3: + resolution: {integrity: sha512-hOpaCHmQVVY66IVTjofnH14IgSdmod2aquSGHGuYig/OIdWge01Hk2Wt988DZcwXumFUT4+FvJY5N+ikl8o/ww==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -10393,6 +11367,11 @@ packages: webpack-cli: optional: true + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} @@ -10444,17 +11423,17 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerd@1.20260625.1: - resolution: {integrity: sha512-GApQvFX52SDM6L4u0+RRnUDB1wJOnEwoXjinkmOPtIyofWBxrlZckdegJSYc1leg++lLZ3+DQ4zMVmBqYVtzfA==} + workerd@1.20260701.1: + resolution: {integrity: sha512-uF813NG09JwNRRUfJ0zBomyTslSPM810dMj9LVvkQ7RAkLrQLzAlPU8Xh/3dIqZDo2bfd7tChbf2PtqLRARRJQ==} engines: {node: '>=16'} hasBin: true - wrangler@4.105.0: - resolution: {integrity: sha512-7dXFH6OLj1Fv0y6ZeRPUxFTkp+duWD7/xxVi/1c0vfOeEYwIFKWB7cdqnY05DvY1Ta3BnqAwRkXfLs8PDj538g==} + wrangler@4.107.0: + resolution: {integrity: sha512-fw69ThymNitZ0oIEBU2yNeq3kK59UKz/jyA3udwRrQIAIsxX57q5qLOpPTN7qc5t8n9pnUeofe0uxtMuhQZW8w==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20260625.1 + '@cloudflare/workers-types': ^4.20260701.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -10568,6 +11547,9 @@ packages: zrender@6.1.0: resolution: {integrity: sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==} + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@acemir/cssom@0.9.31': {} @@ -10881,16 +11863,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -10960,7 +11932,7 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@biomejs/cli-darwin-arm64@2.5.1': + '@biomejs/cli-darwin-arm64@2.5.2': optional: true '@bkrem/react-transition-group@1.3.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': @@ -11121,28 +12093,28 @@ snapshots: '@cloudflare/kv-asset-handler@0.5.0': {} - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260625.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260701.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260625.1 + workerd: 1.20260701.1 - '@cloudflare/workerd-darwin-64@1.20260625.1': + '@cloudflare/workerd-darwin-64@1.20260701.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260625.1': + '@cloudflare/workerd-darwin-arm64@1.20260701.1': optional: true - '@cloudflare/workerd-linux-64@1.20260625.1': + '@cloudflare/workerd-linux-64@1.20260701.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260625.1': + '@cloudflare/workerd-linux-arm64@1.20260701.1': optional: true - '@cloudflare/workerd-windows-64@1.20260625.1': + '@cloudflare/workerd-windows-64@1.20260701.1': optional: true - '@cloudflare/workers-types@4.20260628.1': {} + '@cloudflare/workers-types@4.20260702.1': {} '@conform-to/dom@1.16.0': {} @@ -11196,6 +12168,12 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.2': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 @@ -11206,6 +12184,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.2': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 @@ -11216,15 +12199,17 @@ snapshots: tslib: 2.8.1 optional: true + '@emotion/hash@0.9.2': {} + '@epic-web/cachified@5.6.1': {} '@epic-web/client-hints@1.3.8': {} - '@epic-web/config@1.21.3(@testing-library/dom@10.4.1)(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/utils@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(prettier@3.8.1)(typescript@5.9.3)': + '@epic-web/config@1.21.3(@testing-library/dom@10.4.1)(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/utils@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(prettier@3.8.1)(typescript@5.9.3)': dependencies: '@total-typescript/ts-reset': 0.6.1 - '@vitest/eslint-plugin': 1.6.20(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) - eslint-plugin-import-x: 4.17.1(@typescript-eslint/utils@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0)) + '@vitest/eslint-plugin': 1.6.20(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + eslint-plugin-import-x: 4.17.1(@typescript-eslint/utils@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0)) eslint-plugin-jest-dom: 5.5.0(@testing-library/dom@10.4.1)(eslint@9.39.2(jiti@2.7.0)) eslint-plugin-playwright: 2.10.4(eslint@9.39.2(jiti@2.7.0)) eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.7.0)) @@ -11233,7 +12218,7 @@ snapshots: globals: 16.5.0 prettier-plugin-tailwindcss: 0.6.14(prettier@3.8.1) tslib: 2.8.1 - typescript-eslint: 8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + typescript-eslint: 8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) transitivePeerDependencies: - '@ianvs/prettier-plugin-sort-imports' - '@prettier/plugin-hermes' @@ -11540,12 +12525,34 @@ snapshots: '@formatjs/ts-transformer@3.14.2': dependencies: '@formatjs/icu-messageformat-parser': 2.11.4 - '@types/node': 22.15.31 + '@types/node': 22.20.0 chalk: 4.1.2 json-stable-stringify: 1.3.0 tslib: 2.8.1 typescript: 5.9.3 + '@hapi/address@5.1.1': + dependencies: + '@hapi/hoek': 11.0.7 + + '@hapi/formula@3.0.2': {} + + '@hapi/hoek@11.0.7': {} + + '@hapi/hoek@9.3.0': {} + + '@hapi/pinpoint@2.0.1': {} + + '@hapi/tlds@1.1.7': {} + + '@hapi/topo@5.1.0': + dependencies: + '@hapi/hoek': 9.3.0 + + '@hapi/topo@6.0.2': + dependencies: + '@hapi/hoek': 11.0.7 + '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -11747,6 +12754,55 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 + '@mdx-js/loader@3.1.1(webpack@5.108.3(lightningcss@1.32.0))': + dependencies: + '@mdx-js/mdx': 3.1.1 + source-map: 0.7.6 + optionalDependencies: + webpack: 5.108.3(lightningcss@1.32.0) + transitivePeerDependencies: + - supports-color + + '@mdx-js/mdx@3.1.1': + dependencies: + '@types/estree': 1.0.9 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdx': 2.0.14 + acorn: 8.17.0 + collapse-white-space: 2.1.0 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-util-scope: 1.0.0 + estree-walker: 3.0.3 + hast-util-to-jsx-runtime: 2.3.6 + markdown-extensions: 2.0.0 + recma-build-jsx: 1.0.0 + recma-jsx: 1.0.1(acorn@8.17.0) + recma-stringify: 1.0.0 + rehype-recma: 1.0.0 + remark-mdx: 3.1.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + source-map: 0.7.6 + unified: 11.0.5 + unist-util-position-from-estree: 2.0.0 + unist-util-stringify-position: 4.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@mdx-js/rollup@3.1.1(rollup@4.62.2)': + dependencies: + '@mdx-js/mdx': 3.1.1 + '@rollup/pluginutils': 5.4.0(rollup@4.62.2) + rollup: 4.62.2 + source-map: 0.7.6 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + '@mjackson/form-data-parser@0.9.1': dependencies: '@mjackson/multipart-parser': 0.10.1 @@ -11802,7 +12858,7 @@ snapshots: - node-fetch - utf-8-validate - '@module-federation/enhanced@2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': + '@module-federation/enhanced@2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': dependencies: '@module-federation/bridge-react-webpack-plugin': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/cli': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) @@ -11811,7 +12867,7 @@ snapshots: '@module-federation/inject-external-runtime-core-plugin': 2.5.1(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13))) '@module-federation/managers': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/manifest': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) - '@module-federation/rspack': 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) + '@module-federation/rspack': 2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) '@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/webpack-bundler-runtime': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) @@ -11820,7 +12876,7 @@ snapshots: upath: 2.0.1 optionalDependencies: typescript: 5.9.3 - webpack: 5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16) + webpack: 5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16) transitivePeerDependencies: - '@rspack/core' - bufferutil @@ -11855,16 +12911,16 @@ snapshots: - utf-8-validate - vue-tsc - '@module-federation/node@2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': + '@module-federation/node@2.7.44(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': dependencies: - '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/runtime': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) encoding: 0.1.13 node-fetch: 2.7.0(encoding@0.1.13) tapable: 2.3.0 optionalDependencies: - webpack: 5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16) + webpack: 5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16) transitivePeerDependencies: - '@rspack/core' - bufferutil @@ -11872,10 +12928,10 @@ snapshots: - utf-8-validate - vue-tsc - '@module-federation/rsbuild-plugin@2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': + '@module-federation/rsbuild-plugin@2.5.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16))': dependencies: - '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) - '@module-federation/node': 2.7.44(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + '@module-federation/enhanced': 2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + '@module-federation/node': 2.7.44(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) '@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) optionalDependencies: '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) @@ -11888,7 +12944,7 @@ snapshots: - vue-tsc - webpack - '@module-federation/rspack@2.5.1(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)': + '@module-federation/rspack@2.5.1(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3)': dependencies: '@module-federation/bridge-react-webpack-plugin': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/dts-plugin': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) @@ -11897,7 +12953,7 @@ snapshots: '@module-federation/manifest': 2.5.1(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.9.3) '@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@module-federation/sdk': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) - '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -12004,10 +13060,10 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 '@tybys/wasm-util': 0.10.3 optional: true @@ -12050,13 +13106,13 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs@0.219.0': + '@opentelemetry/api-logs@0.220.0': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api@1.9.1': {} - '@opentelemetry/context-async-hooks@2.8.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/context-async-hooks@2.9.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -12065,7 +13121,7 @@ snapshots: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.41.1 - '@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.41.1 @@ -12073,7 +13129,7 @@ snapshots: '@opentelemetry/instrumentation-amqplib@0.58.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: @@ -12082,7 +13138,7 @@ snapshots: '@opentelemetry/instrumentation-connect@0.54.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 '@types/connect': 3.4.38 @@ -12099,7 +13155,7 @@ snapshots: '@opentelemetry/instrumentation-express@0.59.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: @@ -12108,7 +13164,7 @@ snapshots: '@opentelemetry/instrumentation-fs@0.30.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) transitivePeerDependencies: - supports-color @@ -12130,7 +13186,7 @@ snapshots: '@opentelemetry/instrumentation-hapi@0.57.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: @@ -12174,7 +13230,7 @@ snapshots: '@opentelemetry/instrumentation-koa@0.59.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: @@ -12198,7 +13254,7 @@ snapshots: '@opentelemetry/instrumentation-mongoose@0.57.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: @@ -12225,7 +13281,7 @@ snapshots: '@opentelemetry/instrumentation-pg@0.63.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.1) @@ -12255,7 +13311,7 @@ snapshots: '@opentelemetry/instrumentation-undici@0.21.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: @@ -12279,10 +13335,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.219.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.219.0 + '@opentelemetry/api-logs': 0.220.0 import-in-the-middle: 3.2.0 require-in-the-middle: 8.0.1 transitivePeerDependencies: @@ -12290,17 +13346,25 @@ snapshots: '@opentelemetry/redis-common@0.38.3': {} - '@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 - '@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-trace@2.9.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 '@opentelemetry/semantic-conventions@1.41.1': {} @@ -12308,7 +13372,7 @@ snapshots: '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@oslojs/asn1@1.0.0': dependencies: @@ -12379,7 +13443,7 @@ snapshots: detect-libc: 2.1.2 is-glob: 4.0.3 node-addon-api: 7.1.1 - picomatch: 4.0.4 + picomatch: 4.0.5 optionalDependencies: '@parcel/watcher-android-arm64': 2.5.6 '@parcel/watcher-darwin-arm64': 2.5.6 @@ -12451,7 +13515,7 @@ snapshots: '@prisma/instrumentation@6.19.3(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) transitivePeerDependencies: - supports-color @@ -12473,16 +13537,16 @@ snapshots: '@radix-ui/primitive@1.1.4': {} - '@radix-ui/react-accordion@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-accordion@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collapsible': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collapsible': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.10)(react@19.2.7) '@radix-ui/react-context': 1.1.4(@types/react@19.2.10)(react@19.2.7) '@radix-ui/react-direction': 1.1.2(@types/react@19.2.10)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.10)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.10)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -12515,14 +13579,14 @@ snapshots: '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-collapsible@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-collapsible@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/primitive': 1.1.4 '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.10)(react@19.2.7) '@radix-ui/react-context': 1.1.4(@types/react@19.2.10)(react@19.2.7) '@radix-ui/react-id': 1.1.2(@types/react@19.2.10)(react@19.2.7) '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.10)(react@19.2.7) '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.10)(react@19.2.7) react: 19.2.7 @@ -12531,11 +13595,11 @@ snapshots: '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-collection@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-collection@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.10)(react@19.2.7) '@radix-ui/react-context': 1.1.4(@types/react@19.2.10)(react@19.2.7) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': 1.3.0(@types/react@19.2.10)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -12750,7 +13814,7 @@ snapshots: '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) - '@radix-ui/react-primitive@2.1.6(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@radix-ui/react-slot': 1.3.0(@types/react@19.2.10)(react@19.2.7) react: 19.2.7 @@ -13014,7 +14078,7 @@ snapshots: '@react-email/render@2.0.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: html-to-text: 9.0.5 - prettier: 3.8.1 + prettier: 3.9.4 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -13030,7 +14094,7 @@ snapshots: dependencies: '@react-email/text': 0.1.6(react@19.2.7) react: 19.2.7 - tailwindcss: 4.3.1 + tailwindcss: 4.3.2 optionalDependencies: '@react-email/body': 0.2.1(react@19.2.7) '@react-email/button': 0.2.1(react@19.2.7) @@ -13047,14 +14111,14 @@ snapshots: dependencies: react: 19.2.7 - '@react-router/cloudflare@7.18.0(@cloudflare/workers-types@4.20260628.1)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + '@react-router/cloudflare@7.18.1(@cloudflare/workers-types@4.20260702.1)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': dependencies: - '@cloudflare/workers-types': 4.20260628.1 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@cloudflare/workers-types': 4.20260702.1 + react-router: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) optionalDependencies: typescript: 5.9.3 - '@react-router/dev@7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc))(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@6.0.1-rc)(vite@7.3.1(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1))': + '@react-router/dev@7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc))(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@6.0.1-rc)(vite@7.3.1(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1))': dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.7 @@ -13074,7 +14138,7 @@ snapshots: isbot: 5.1.34 jsesc: 3.0.2 lodash: 4.18.1 - p-map: 7.0.4 + p-map: 7.0.5 pathe: 1.1.2 picocolors: 1.1.1 pkg-types: 2.3.1 @@ -13089,7 +14153,7 @@ snapshots: optionalDependencies: '@react-router/serve': 7.14.0(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc) typescript: 6.0.1-rc - wrangler: 4.105.0(@cloudflare/workers-types@4.20260628.1) + wrangler: 4.107.0(@cloudflare/workers-types@4.20260702.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -13105,7 +14169,7 @@ snapshots: - tsx - yaml - '@react-router/dev@7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1))': + '@react-router/dev@7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1))': dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.7 @@ -13114,7 +14178,7 @@ snapshots: '@babel/preset-typescript': 7.29.7(@babel/core@7.29.0) '@babel/traverse': 7.29.7 '@babel/types': 7.29.7 - '@react-router/node': 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/node': 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@remix-run/node-fetch-server': 0.13.3 arg: 5.0.2 babel-dead-code-elimination: 1.0.12 @@ -13125,22 +14189,22 @@ snapshots: isbot: 5.1.34 jsesc: 3.0.2 lodash: 4.18.1 - p-map: 7.0.4 + p-map: 7.0.5 pathe: 1.1.2 picocolors: 1.1.1 pkg-types: 2.3.1 prettier: 3.8.1 react-refresh: 0.14.2 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) semver: 7.8.5 tinyglobby: 0.2.17 valibot: 1.4.2(typescript@5.9.3) vite: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) vite-node: 3.2.4(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) optionalDependencies: - '@react-router/serve': 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/serve': 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) typescript: 5.9.3 - wrangler: 4.105.0(@cloudflare/workers-types@4.20260628.1) + wrangler: 4.107.0(@cloudflare/workers-types@4.20260702.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -13156,7 +14220,7 @@ snapshots: - tsx - yaml - '@react-router/dev@7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1))': + '@react-router/dev@7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1))': dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.7 @@ -13165,7 +14229,7 @@ snapshots: '@babel/preset-typescript': 7.29.7(@babel/core@7.29.0) '@babel/traverse': 7.29.7 '@babel/types': 7.29.7 - '@react-router/node': 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/node': 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@remix-run/node-fetch-server': 0.13.3 arg: 5.0.2 babel-dead-code-elimination: 1.0.12 @@ -13176,22 +14240,22 @@ snapshots: isbot: 5.1.34 jsesc: 3.0.2 lodash: 4.18.1 - p-map: 7.0.4 + p-map: 7.0.5 pathe: 1.1.2 picocolors: 1.1.1 pkg-types: 2.3.1 prettier: 3.8.1 react-refresh: 0.14.2 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) semver: 7.8.5 tinyglobby: 0.2.17 valibot: 1.4.2(typescript@5.9.3) vite: 7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) vite-node: 3.2.4(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) optionalDependencies: - '@react-router/serve': 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/serve': 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) typescript: 5.9.3 - wrangler: 4.105.0(@cloudflare/workers-types@4.20260628.1) + wrangler: 4.107.0(@cloudflare/workers-types@4.20260702.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -13207,7 +14271,7 @@ snapshots: - tsx - yaml - '@react-router/dev@8.0.1(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1))': + '@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1))': dependencies: '@babel/core': 7.29.7 '@babel/generator': 7.29.7 @@ -13216,48 +14280,116 @@ snapshots: '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) '@babel/traverse': 7.29.7 '@babel/types': 7.29.7 - '@react-router/node': 8.0.1(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/node': 8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) '@remix-run/node-fetch-server': 0.13.3 - arg: 5.0.2 babel-dead-code-elimination: 1.0.12 chokidar: 5.0.0 dedent: 1.7.2 - es-module-lexer: 2.1.0 + es-module-lexer: 2.3.0 exit-hook: 5.1.0 isbot: 5.1.44 jsesc: 3.1.0 lodash: 4.18.1 - p-map: 7.0.4 + p-map: 7.0.5 pathe: 2.0.3 picocolors: 1.1.1 pkg-types: 2.3.1 - prettier: 3.9.1 + prettier: 3.9.4 react-refresh: 0.18.0 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) semver: 7.8.5 tinyglobby: 0.2.17 valibot: 1.4.2(typescript@5.9.3) vite: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) optionalDependencies: + '@react-router/serve': 8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) typescript: 5.9.3 - wrangler: 4.105.0(@cloudflare/workers-types@4.20260628.1) + wrangler: 4.107.0(@cloudflare/workers-types@4.20260702.1) transitivePeerDependencies: - babel-plugin-macros - supports-color - '@react-router/express@7.13.0(express@4.22.2)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + '@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1))': dependencies: - '@react-router/node': 7.13.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) - express: 4.22.2 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@babel/core': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@remix-run/node-fetch-server': 0.13.3 + babel-dead-code-elimination: 1.0.12 + chokidar: 5.0.0 + dedent: 1.7.2 + es-module-lexer: 2.3.0 + exit-hook: 5.1.0 + isbot: 5.1.44 + jsesc: 3.1.0 + lodash: 4.18.1 + p-map: 7.0.5 + pathe: 2.0.3 + picocolors: 1.1.1 + pkg-types: 2.3.1 + prettier: 3.9.4 + react-refresh: 0.18.0 + react-router: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + semver: 7.8.5 + tinyglobby: 0.2.17 + valibot: 1.4.2(typescript@5.9.3) + vite: 7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) + optionalDependencies: + '@react-router/serve': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + typescript: 5.9.3 + wrangler: 4.107.0(@cloudflare/workers-types@4.20260702.1) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + '@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1))': + dependencies: + '@babel/core': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@remix-run/node-fetch-server': 0.13.3 + babel-dead-code-elimination: 1.0.12 + chokidar: 5.0.0 + dedent: 1.7.2 + es-module-lexer: 2.3.0 + exit-hook: 5.1.0 + isbot: 5.1.44 + jsesc: 3.1.0 + lodash: 4.18.1 + p-map: 7.0.5 + pathe: 2.0.3 + picocolors: 1.1.1 + pkg-types: 2.3.1 + prettier: 3.9.4 + react-refresh: 0.18.0 + react-router: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + semver: 7.8.5 + tinyglobby: 0.2.17 + valibot: 1.4.2(typescript@5.9.3) + vite: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) optionalDependencies: + '@react-router/serve': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) typescript: 5.9.3 + wrangler: 4.107.0(@cloudflare/workers-types@4.20260702.1) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color - '@react-router/express@7.13.0(express@5.2.1)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + '@react-router/express@7.13.0(express@5.2.1)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': dependencies: - '@react-router/node': 7.13.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/node': 7.13.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) express: 5.2.1 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) optionalDependencies: typescript: 5.9.3 @@ -13269,18 +14401,71 @@ snapshots: optionalDependencies: typescript: 6.0.1-rc - '@react-router/express@7.18.0(express@4.22.2)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + '@react-router/express@7.14.0(express@4.22.2)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + dependencies: + '@react-router/node': 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + express: 4.22.2 + react-router: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + optionalDependencies: + typescript: 5.9.3 + + '@react-router/express@8.1.0(express@4.22.2)(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + dependencies: + '@react-router/node': 8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + express: 4.22.2 + react-router: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + optionalDependencies: + typescript: 5.9.3 + + '@react-router/express@8.1.0(express@4.22.2)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': dependencies: - '@react-router/node': 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) express: 4.22.2 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + optionalDependencies: + typescript: 5.9.3 + + '@react-router/express@8.1.0(express@5.2.1)(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + dependencies: + '@react-router/node': 8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + express: 5.2.1 + react-router: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + optionalDependencies: + typescript: 5.9.3 + + '@react-router/express@8.1.0(express@5.2.1)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + dependencies: + '@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + express: 5.2.1 + react-router: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + optionalDependencies: + typescript: 5.9.3 + + '@react-router/fs-routes@8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3)': + dependencies: + '@react-router/dev': 8.1.0(@react-router/serve@8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + minimatch: 10.2.5 + optionalDependencies: + typescript: 5.9.3 + + '@react-router/fs-routes@8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3)': + dependencies: + '@react-router/dev': 8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + minimatch: 10.2.5 + optionalDependencies: + typescript: 5.9.3 + + '@react-router/fs-routes@8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3)': + dependencies: + '@react-router/dev': 8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + minimatch: 10.2.5 optionalDependencies: typescript: 5.9.3 - '@react-router/node@7.13.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + '@react-router/node@7.13.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': dependencies: '@mjackson/node-fetch-server': 0.2.0 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) optionalDependencies: typescript: 5.9.3 @@ -13291,51 +14476,105 @@ snapshots: optionalDependencies: typescript: 6.0.1-rc - '@react-router/node@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + '@react-router/node@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + dependencies: + '@mjackson/node-fetch-server': 0.2.0 + react-router: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + optionalDependencies: + typescript: 5.9.3 + + '@react-router/node@7.14.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': dependencies: '@mjackson/node-fetch-server': 0.2.0 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) optionalDependencies: typescript: 5.9.3 - '@react-router/node@8.0.1(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + '@react-router/node@8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': dependencies: '@remix-run/node-fetch-server': 0.13.3 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) optionalDependencies: typescript: 5.9.3 - '@react-router/remix-routes-option-adapter@7.13.0(@react-router/dev@7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)))(typescript@5.9.3)': + '@react-router/node@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': dependencies: - '@react-router/dev': 7.18.0(@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1)) + '@remix-run/node-fetch-server': 0.13.3 + react-router: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) optionalDependencies: typescript: 5.9.3 - '@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc)': + '@react-router/remix-routes-option-adapter@7.13.0(@react-router/dev@7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3)': dependencies: - '@mjackson/node-fetch-server': 0.2.0 - '@react-router/express': 7.14.0(express@4.22.2)(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc) - '@react-router/node': 7.14.0(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc) - compression: 1.8.1 - express: 4.22.2 - get-port: 5.1.1 - morgan: 1.10.1 + '@react-router/dev': 7.14.0(@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + optionalDependencies: + typescript: 5.9.3 + + '@react-router/remix-routes-option-adapter@8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3)': + dependencies: + '@react-router/dev': 8.1.0(@react-router/serve@8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + optionalDependencies: + typescript: 5.9.3 + + '@react-router/remix-routes-option-adapter@8.1.0(@react-router/dev@8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)))(typescript@5.9.3)': + dependencies: + '@react-router/dev': 8.1.0(@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3))(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)(vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))(wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1)) + optionalDependencies: + typescript: 5.9.3 + + '@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc)': + dependencies: + '@mjackson/node-fetch-server': 0.2.0 + '@react-router/express': 7.14.0(express@4.22.2)(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc) + '@react-router/node': 7.14.0(react-router@7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(typescript@6.0.1-rc) + compression: 1.8.1 + express: 4.22.2 + get-port: 5.1.1 + morgan: 1.10.1 react-router: 7.14.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3) source-map-support: 0.5.21 transitivePeerDependencies: - supports-color - typescript - '@react-router/serve@7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + '@react-router/serve@7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': dependencies: '@mjackson/node-fetch-server': 0.2.0 - '@react-router/express': 7.18.0(express@4.22.2)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) - '@react-router/node': 7.18.0(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/express': 7.14.0(express@4.22.2)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/node': 7.14.0(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) compression: 1.8.1 express: 4.22.2 get-port: 5.1.1 morgan: 1.10.1 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + source-map-support: 0.5.21 + transitivePeerDependencies: + - supports-color + - typescript + + '@react-router/serve@8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + dependencies: + '@react-router/express': 8.1.0(express@5.2.1)(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/node': 8.1.0(react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@remix-run/node-fetch-server': 0.13.3 + compression: 1.8.1 + express: 5.2.1 + morgan: 1.10.1 + react-router: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + source-map-support: 0.5.21 + transitivePeerDependencies: + - supports-color + - typescript + + '@react-router/serve@8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3)': + dependencies: + '@react-router/express': 8.1.0(express@5.2.1)(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@react-router/node': 8.1.0(react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(typescript@5.9.3) + '@remix-run/node-fetch-server': 0.13.3 + compression: 1.8.1 + express: 5.2.1 + morgan: 1.10.1 + react-router: 8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) source-map-support: 0.5.21 transitivePeerDependencies: - supports-color @@ -13383,7 +14622,13 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@rolldown/pluginutils@1.0.0-beta.53': {} + '@rollup/pluginutils@5.4.0(rollup@4.62.2)': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.5 + optionalDependencies: + rollup: 4.62.2 '@rollup/rollup-android-arm-eabi@4.62.2': optional: true @@ -13468,9 +14713,9 @@ snapshots: core-js: 3.47.0 jiti: 2.7.0 - '@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)': + '@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)': dependencies: - '@rspack/core': 2.0.8(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23) + '@rspack/core': 2.0.8(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) '@swc/helpers': 0.5.23 optionalDependencies: core-js: 3.47.0 @@ -13486,7 +14731,16 @@ snapshots: transitivePeerDependencies: - '@module-federation/runtime-tools' - '@rsbuild/plugin-babel@2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))': + '@rsbuild/core@2.1.6(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)': + dependencies: + '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@swc/helpers': 0.5.23 + optionalDependencies: + core-js: 3.47.0 + transitivePeerDependencies: + - '@module-federation/runtime-tools' + + '@rsbuild/plugin-babel@2.0.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.7) @@ -13499,7 +14753,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@rsbuild/plugin-check-syntax@1.6.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))': + '@rsbuild/plugin-check-syntax@1.6.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))': dependencies: acorn: 8.17.0 browserslist-to-es-version: 1.4.2 @@ -13509,11 +14763,23 @@ snapshots: optionalDependencies: '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) - '@rsbuild/plugin-less@1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsbuild/plugin-less@1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': + dependencies: + deepmerge: 4.3.1 + less: 4.6.7 + less-loader: 12.3.3(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0)) + reduce-configs: 1.1.2 + optionalDependencies: + '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + transitivePeerDependencies: + - '@rspack/core' + - webpack + + '@rsbuild/plugin-less@1.6.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': dependencies: deepmerge: 4.3.1 less: 4.6.7 - less-loader: 12.3.3(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.1(lightningcss@1.32.0)) + less-loader: 12.3.3(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0)) reduce-configs: 1.1.2 optionalDependencies: '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) @@ -13521,6 +14787,15 @@ snapshots: - '@rspack/core' - webpack + '@rsbuild/plugin-mdx@1.1.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(webpack@5.108.3(lightningcss@1.32.0))': + dependencies: + '@mdx-js/loader': 3.1.1(webpack@5.108.3(lightningcss@1.32.0)) + optionalDependencies: + '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + transitivePeerDependencies: + - supports-color + - webpack + '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))': dependencies: '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0) @@ -13530,7 +14805,16 @@ snapshots: transitivePeerDependencies: - '@rspack/core' - '@rsbuild/plugin-sass@1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))': + '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))': + dependencies: + '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0) + react-refresh: 0.18.0 + optionalDependencies: + '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + transitivePeerDependencies: + - '@rspack/core' + + '@rsbuild/plugin-sass@1.5.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))': dependencies: deepmerge: 4.3.1 loader-utils: 2.0.4 @@ -13540,12 +14824,42 @@ snapshots: optionalDependencies: '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) - '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(typescript@6.0.1-rc)': + '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)': dependencies: '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3) + deepmerge: 4.3.1 + loader-utils: 3.3.1 + optionalDependencies: + '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + transitivePeerDependencies: + - '@rspack/core' + - supports-color + - typescript + + '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@5.9.3)': + dependencies: + '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) + '@svgr/core': 8.1.0(typescript@5.9.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3) + deepmerge: 4.3.1 + loader-utils: 3.3.1 + optionalDependencies: + '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + transitivePeerDependencies: + - '@rspack/core' + - supports-color + - typescript + + '@rsbuild/plugin-svgr@2.0.4(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(typescript@6.0.1-rc)': + dependencies: + '@rsbuild/plugin-react': 2.1.0(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)) '@svgr/core': 8.1.0(typescript@6.0.1-rc) - '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@6.0.1-rc)) - '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@6.0.1-rc))(typescript@6.0.1-rc) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@6.0.1-rc) deepmerge: 4.3.1 loader-utils: 3.3.1 optionalDependencies: @@ -13555,25 +14869,34 @@ snapshots: - supports-color - typescript - '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': + dependencies: + '@tailwindcss/webpack': 4.3.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + optionalDependencies: + '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + transitivePeerDependencies: + - '@rspack/core' + - webpack + + '@rsbuild/plugin-tailwindcss@2.0.3(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': dependencies: - '@tailwindcss/webpack': 4.3.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@tailwindcss/webpack': 4.3.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) optionalDependencies: '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) transitivePeerDependencies: - '@rspack/core' - webpack - '@rsdoctor/client@1.5.16': {} + '@rsdoctor/client@1.5.17': {} - '@rsdoctor/core@1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/core@1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': dependencies: - '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)) - '@rsdoctor/graph': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/sdk': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/types': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rspack/resolver': 0.2.8(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@rsbuild/plugin-check-syntax': 1.6.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)) + '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsdoctor/sdk': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rspack/resolver': 0.2.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) browserslist-load-config: 1.0.3 es-toolkit: 1.49.0 filesize: 11.0.19 @@ -13590,10 +14913,10 @@ snapshots: - utf-8-validate - webpack - '@rsdoctor/graph@1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/graph@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': dependencies: - '@rsdoctor/types': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) es-toolkit: 1.49.0 path-browserify: 1.0.1 source-map: 0.7.6 @@ -13601,15 +14924,15 @@ snapshots: - '@rspack/core' - webpack - '@rsdoctor/rspack-plugin@1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/rspack-plugin@1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': dependencies: - '@rsdoctor/core': 1.5.16(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/graph': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/sdk': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/types': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/core': 1.5.17(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsdoctor/sdk': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) optionalDependencies: - '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -13619,12 +14942,12 @@ snapshots: - utf-8-validate - webpack - '@rsdoctor/sdk@1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/sdk@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': dependencies: - '@rsdoctor/client': 1.5.16 - '@rsdoctor/graph': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/types': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) - '@rsdoctor/utils': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/client': 1.5.17 + '@rsdoctor/graph': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) + '@rsdoctor/utils': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) launch-editor: 2.14.1 safer-buffer: 2.1.2 socket.io: 4.8.1 @@ -13636,20 +14959,20 @@ snapshots: - utf-8-validate - webpack - '@rsdoctor/types@1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/types@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': dependencies: '@types/connect': 3.4.38 '@types/estree': 1.0.5 '@types/tapable': 2.3.0 source-map: 0.7.6 optionalDependencies: - '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) - webpack: 5.108.1(lightningcss@1.32.0) + '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + webpack: 5.108.3(lightningcss@1.32.0) - '@rsdoctor/utils@1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@rsdoctor/utils@1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': dependencies: '@babel/code-frame': 7.26.2 - '@rsdoctor/types': 1.5.16(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0)) + '@rsdoctor/types': 1.5.17(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0)) '@types/estree': 1.0.5 acorn: 8.17.0 acorn-import-attributes: 1.9.5(acorn@8.17.0) @@ -13667,10 +14990,10 @@ snapshots: - '@rspack/core' - webpack - '@rslib/core@0.22.1(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0)(typescript@5.9.3)': + '@rslib/core@0.22.1(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(typescript@5.9.3)': dependencies: - '@rsbuild/core': 2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0) - rsbuild-plugin-dts: 0.22.1(@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(typescript@5.9.3) + '@rsbuild/core': 2.0.15(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + rsbuild-plugin-dts: 0.22.1(@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -13687,6 +15010,9 @@ snapshots: '@rspack/binding-darwin-arm64@2.1.0': optional: true + '@rspack/binding-darwin-arm64@2.1.4': + optional: true + '@rspack/binding-darwin-x64@1.7.12': optional: true @@ -13696,6 +15022,9 @@ snapshots: '@rspack/binding-darwin-x64@2.1.0': optional: true + '@rspack/binding-darwin-x64@2.1.4': + optional: true + '@rspack/binding-linux-arm64-gnu@1.7.12': optional: true @@ -13705,6 +15034,9 @@ snapshots: '@rspack/binding-linux-arm64-gnu@2.1.0': optional: true + '@rspack/binding-linux-arm64-gnu@2.1.4': + optional: true + '@rspack/binding-linux-arm64-musl@1.7.12': optional: true @@ -13714,12 +15046,21 @@ snapshots: '@rspack/binding-linux-arm64-musl@2.1.0': optional: true + '@rspack/binding-linux-arm64-musl@2.1.4': + optional: true + '@rspack/binding-linux-riscv64-gnu@2.1.0': optional: true + '@rspack/binding-linux-riscv64-gnu@2.1.4': + optional: true + '@rspack/binding-linux-riscv64-musl@2.1.0': optional: true + '@rspack/binding-linux-riscv64-musl@2.1.4': + optional: true + '@rspack/binding-linux-x64-gnu@1.7.12': optional: true @@ -13729,6 +15070,9 @@ snapshots: '@rspack/binding-linux-x64-gnu@2.1.0': optional: true + '@rspack/binding-linux-x64-gnu@2.1.4': + optional: true + '@rspack/binding-linux-x64-musl@1.7.12': optional: true @@ -13738,6 +15082,9 @@ snapshots: '@rspack/binding-linux-x64-musl@2.1.0': optional: true + '@rspack/binding-linux-x64-musl@2.1.4': + optional: true + '@rspack/binding-wasm32-wasi@1.7.12': dependencies: '@napi-rs/wasm-runtime': 1.0.7 @@ -13757,6 +15104,13 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true + '@rspack/binding-wasm32-wasi@2.1.4': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + '@rspack/binding-win32-arm64-msvc@1.7.12': optional: true @@ -13766,6 +15120,9 @@ snapshots: '@rspack/binding-win32-arm64-msvc@2.1.0': optional: true + '@rspack/binding-win32-arm64-msvc@2.1.4': + optional: true + '@rspack/binding-win32-ia32-msvc@1.7.12': optional: true @@ -13775,6 +15132,9 @@ snapshots: '@rspack/binding-win32-ia32-msvc@2.1.0': optional: true + '@rspack/binding-win32-ia32-msvc@2.1.4': + optional: true + '@rspack/binding-win32-x64-msvc@1.7.12': optional: true @@ -13784,6 +15144,9 @@ snapshots: '@rspack/binding-win32-x64-msvc@2.1.0': optional: true + '@rspack/binding-win32-x64-msvc@2.1.4': + optional: true + '@rspack/binding@1.7.12': optionalDependencies: '@rspack/binding-darwin-arm64': 1.7.12 @@ -13825,6 +15188,21 @@ snapshots: '@rspack/binding-win32-ia32-msvc': 2.1.0 '@rspack/binding-win32-x64-msvc': 2.1.0 + '@rspack/binding@2.1.4': + optionalDependencies: + '@rspack/binding-darwin-arm64': 2.1.4 + '@rspack/binding-darwin-x64': 2.1.4 + '@rspack/binding-linux-arm64-gnu': 2.1.4 + '@rspack/binding-linux-arm64-musl': 2.1.4 + '@rspack/binding-linux-riscv64-gnu': 2.1.4 + '@rspack/binding-linux-riscv64-musl': 2.1.4 + '@rspack/binding-linux-x64-gnu': 2.1.4 + '@rspack/binding-linux-x64-musl': 2.1.4 + '@rspack/binding-wasm32-wasi': 2.1.4 + '@rspack/binding-win32-arm64-msvc': 2.1.4 + '@rspack/binding-win32-ia32-msvc': 2.1.4 + '@rspack/binding-win32-x64-msvc': 2.1.4 + '@rspack/core@1.7.12(@swc/helpers@0.5.23)': dependencies: '@module-federation/runtime-tools': 0.22.0 @@ -13833,7 +15211,7 @@ snapshots: optionalDependencies: '@swc/helpers': 0.5.23 - '@rspack/core@2.0.8(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23)': + '@rspack/core@2.0.8(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)': dependencies: '@rspack/binding': 2.0.8 optionalDependencies: @@ -13847,6 +15225,13 @@ snapshots: '@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) '@swc/helpers': 0.5.23 + '@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23)': + dependencies: + '@rspack/binding': 2.1.4 + optionalDependencies: + '@module-federation/runtime-tools': 2.5.1(node-fetch@2.7.0(encoding@0.1.13)) + '@swc/helpers': 0.5.23 + '@rspack/lite-tapable@1.1.0': {} '@rspack/lite-tapable@1.1.2': {} @@ -13857,6 +15242,12 @@ snapshots: optionalDependencies: '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@rspack/plugin-react-refresh@2.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-refresh@0.18.0)': + dependencies: + react-refresh: 0.18.0 + optionalDependencies: + '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + '@rspack/resolver-binding-darwin-arm64@0.2.8': optional: true @@ -13875,9 +15266,9 @@ snapshots: '@rspack/resolver-binding-linux-x64-musl@0.2.8': optional: true - '@rspack/resolver-binding-wasm32-wasi@0.2.8(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@rspack/resolver-binding-wasm32-wasi@0.2.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': dependencies: - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -13892,7 +15283,7 @@ snapshots: '@rspack/resolver-binding-win32-x64-msvc@0.2.8': optional: true - '@rspack/resolver@0.2.8(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@rspack/resolver@0.2.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': optionalDependencies: '@rspack/resolver-binding-darwin-arm64': 0.2.8 '@rspack/resolver-binding-darwin-x64': 0.2.8 @@ -13900,7 +15291,7 @@ snapshots: '@rspack/resolver-binding-linux-arm64-musl': 0.2.8 '@rspack/resolver-binding-linux-x64-gnu': 0.2.8 '@rspack/resolver-binding-linux-x64-musl': 0.2.8 - '@rspack/resolver-binding-wasm32-wasi': 0.2.8(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@rspack/resolver-binding-wasm32-wasi': 0.2.8(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) '@rspack/resolver-binding-win32-arm64-msvc': 0.2.8 '@rspack/resolver-binding-win32-ia32-msvc': 0.2.8 '@rspack/resolver-binding-win32-x64-msvc': 0.2.8 @@ -13908,6 +15299,16 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' + '@rstest/core@0.11.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0))': + dependencies: + '@rsbuild/core': 2.1.6(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + '@types/chai': 5.2.3 + optionalDependencies: + jsdom: 27.4.0(@noble/hashes@2.2.0) + transitivePeerDependencies: + - '@module-federation/runtime-tools' + - core-js + '@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0))': dependencies: '@rsbuild/core': 1.7.2 @@ -13916,6 +15317,17 @@ snapshots: optionalDependencies: jsdom: 27.4.0(@noble/hashes@2.2.0) + '@rstest/coverage-istanbul@0.11.4(@rstest/core@0.11.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)))': + dependencies: + '@rstest/core': 0.11.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0)(jsdom@27.4.0(@noble/hashes@2.2.0)) + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + swc-plugin-coverage-instrument: 0.0.32 + transitivePeerDependencies: + - supports-color + '@rstest/coverage-istanbul@0.2.0(@rstest/core@0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)))': dependencies: '@rstest/core': 0.8.1(jsdom@27.4.0(@noble/hashes@2.2.0)) @@ -13945,7 +15357,7 @@ snapshots: '@sentry-internal/node-cpu-profiler@2.4.1': dependencies: detect-libc: 2.1.2 - node-abi: 3.92.0 + node-abi: 3.94.0 '@sentry-internal/replay-canvas@10.37.0': dependencies: @@ -13957,8 +15369,6 @@ snapshots: '@sentry-internal/browser-utils': 10.37.0 '@sentry/core': 10.37.0 - '@sentry/babel-plugin-component-annotate@4.8.0': {} - '@sentry/browser@10.37.0': dependencies: '@sentry-internal/browser-utils': 10.37.0 @@ -13967,78 +15377,20 @@ snapshots: '@sentry-internal/replay-canvas': 10.37.0 '@sentry/core': 10.37.0 - '@sentry/bundler-plugin-core@4.8.0(encoding@0.1.13)': - dependencies: - '@babel/core': 7.29.0 - '@sentry/babel-plugin-component-annotate': 4.8.0 - '@sentry/cli': 2.58.6(encoding@0.1.13) - dotenv: 16.6.1 - find-up: 5.0.0 - glob: 10.5.0 - magic-string: 0.30.8 - unplugin: 1.0.1 - transitivePeerDependencies: - - encoding - - supports-color - - '@sentry/cli-darwin@2.58.6': - optional: true - - '@sentry/cli-linux-arm64@2.58.6': - optional: true - - '@sentry/cli-linux-arm@2.58.6': - optional: true - - '@sentry/cli-linux-i686@2.58.6': - optional: true - - '@sentry/cli-linux-x64@2.58.6': - optional: true - - '@sentry/cli-win32-arm64@2.58.6': - optional: true - - '@sentry/cli-win32-i686@2.58.6': - optional: true - - '@sentry/cli-win32-x64@2.58.6': - optional: true - - '@sentry/cli@2.58.6(encoding@0.1.13)': - dependencies: - https-proxy-agent: 5.0.1 - node-fetch: 2.7.0(encoding@0.1.13) - progress: 2.0.3 - proxy-from-env: 1.1.0 - which: 2.0.2 - optionalDependencies: - '@sentry/cli-darwin': 2.58.6 - '@sentry/cli-linux-arm': 2.58.6 - '@sentry/cli-linux-arm64': 2.58.6 - '@sentry/cli-linux-i686': 2.58.6 - '@sentry/cli-linux-x64': 2.58.6 - '@sentry/cli-win32-arm64': 2.58.6 - '@sentry/cli-win32-i686': 2.58.6 - '@sentry/cli-win32-x64': 2.58.6 - transitivePeerDependencies: - - encoding - - supports-color - '@sentry/core@10.37.0': {} - '@sentry/node-core@10.37.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': + '@sentry/node-core@10.37.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': dependencies: '@apm-js-collab/tracing-hooks': 0.3.1 '@opentelemetry/api': 1.9.1 - '@opentelemetry/context-async-hooks': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/context-async-hooks': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 '@sentry/core': 10.37.0 - '@sentry/opentelemetry': 10.37.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + '@sentry/opentelemetry': 10.37.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) import-in-the-middle: 2.0.6 transitivePeerDependencies: - supports-color @@ -14046,8 +15398,8 @@ snapshots: '@sentry/node@10.37.0': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/context-async-hooks': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/context-async-hooks': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-amqplib': 0.58.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-connect': 0.54.0(@opentelemetry/api@1.9.1) @@ -14071,24 +15423,24 @@ snapshots: '@opentelemetry/instrumentation-redis': 0.59.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-tedious': 0.30.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-undici': 0.21.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 '@prisma/instrumentation': 7.2.0(@opentelemetry/api@1.9.1) '@sentry/core': 10.37.0 - '@sentry/node-core': 10.37.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) - '@sentry/opentelemetry': 10.37.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + '@sentry/node-core': 10.37.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.1))(@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) + '@sentry/opentelemetry': 10.37.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1) import-in-the-middle: 2.0.6 minimatch: 9.0.9 transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@10.37.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': + '@sentry/opentelemetry@10.37.0(@opentelemetry/api@1.9.1)(@opentelemetry/context-async-hooks@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/semantic-conventions@1.41.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/context-async-hooks': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/context-async-hooks': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 '@sentry/core': 10.37.0 @@ -14106,13 +15458,13 @@ snapshots: '@sentry/core': 10.37.0 react: 19.2.7 - '@sentry/vite-plugin@4.8.0(encoding@0.1.13)': + '@sideway/address@4.1.5': dependencies: - '@sentry/bundler-plugin-core': 4.8.0(encoding@0.1.13) - unplugin: 1.0.1 - transitivePeerDependencies: - - encoding - - supports-color + '@hapi/hoek': 9.3.0 + + '@sideway/formula@3.0.1': {} + + '@sideway/pinpoint@2.0.0': {} '@sindresorhus/is@7.2.0': {} @@ -14134,39 +15486,39 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} - '@solid-primitives/event-listener@2.4.5(solid-js@1.9.13)': + '@solid-primitives/event-listener@2.4.5(solid-js@1.9.14)': dependencies: - '@solid-primitives/utils': 6.4.0(solid-js@1.9.13) - solid-js: 1.9.13 + '@solid-primitives/utils': 6.4.0(solid-js@1.9.14) + solid-js: 1.9.14 - '@solid-primitives/keyboard@1.3.5(solid-js@1.9.13)': + '@solid-primitives/keyboard@1.3.5(solid-js@1.9.14)': dependencies: - '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.13) - '@solid-primitives/rootless': 1.5.3(solid-js@1.9.13) - '@solid-primitives/utils': 6.4.0(solid-js@1.9.13) - solid-js: 1.9.13 + '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.14) + '@solid-primitives/rootless': 1.5.3(solid-js@1.9.14) + '@solid-primitives/utils': 6.4.0(solid-js@1.9.14) + solid-js: 1.9.14 - '@solid-primitives/resize-observer@2.1.5(solid-js@1.9.13)': + '@solid-primitives/resize-observer@2.1.5(solid-js@1.9.14)': dependencies: - '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.13) - '@solid-primitives/rootless': 1.5.3(solid-js@1.9.13) - '@solid-primitives/static-store': 0.1.3(solid-js@1.9.13) - '@solid-primitives/utils': 6.4.0(solid-js@1.9.13) - solid-js: 1.9.13 + '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.14) + '@solid-primitives/rootless': 1.5.3(solid-js@1.9.14) + '@solid-primitives/static-store': 0.1.3(solid-js@1.9.14) + '@solid-primitives/utils': 6.4.0(solid-js@1.9.14) + solid-js: 1.9.14 - '@solid-primitives/rootless@1.5.3(solid-js@1.9.13)': + '@solid-primitives/rootless@1.5.3(solid-js@1.9.14)': dependencies: - '@solid-primitives/utils': 6.4.0(solid-js@1.9.13) - solid-js: 1.9.13 + '@solid-primitives/utils': 6.4.0(solid-js@1.9.14) + solid-js: 1.9.14 - '@solid-primitives/static-store@0.1.3(solid-js@1.9.13)': + '@solid-primitives/static-store@0.1.3(solid-js@1.9.14)': dependencies: - '@solid-primitives/utils': 6.4.0(solid-js@1.9.13) - solid-js: 1.9.13 + '@solid-primitives/utils': 6.4.0(solid-js@1.9.14) + solid-js: 1.9.14 - '@solid-primitives/utils@6.4.0(solid-js@1.9.13)': + '@solid-primitives/utils@6.4.0(solid-js@1.9.14)': dependencies: - solid-js: 1.9.13 + solid-js: 1.9.14 '@speed-highlight/core@1.2.17': {} @@ -14216,6 +15568,17 @@ snapshots: '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.29.0) '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.29.0) + '@svgr/core@8.1.0(typescript@5.9.3)': + dependencies: + '@babel/core': 7.29.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.0) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.9.3) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + '@svgr/core@8.1.0(typescript@6.0.1-rc)': dependencies: '@babel/core': 7.29.0 @@ -14232,19 +15595,28 @@ snapshots: '@babel/types': 7.29.7 entities: 4.5.0 - '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@6.0.1-rc))': + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))': dependencies: '@babel/core': 7.29.0 '@svgr/babel-preset': 8.1.0(@babel/core@7.29.0) - '@svgr/core': 8.1.0(typescript@6.0.1-rc) + '@svgr/core': 8.1.0(typescript@5.9.3) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 transitivePeerDependencies: - supports-color - '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@6.0.1-rc))(typescript@6.0.1-rc)': + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@5.9.3)': dependencies: - '@svgr/core': 8.1.0(typescript@6.0.1-rc) + '@svgr/core': 8.1.0(typescript@5.9.3) + cosmiconfig: 8.3.6(typescript@5.9.3) + deepmerge: 4.3.1 + svgo: 3.3.3 + transitivePeerDependencies: + - typescript + + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))(typescript@6.0.1-rc)': + dependencies: + '@svgr/core': 8.1.0(typescript@5.9.3) cosmiconfig: 8.3.6(typescript@6.0.1-rc) deepmerge: 4.3.1 svgo: 3.3.3 @@ -14260,16 +15632,6 @@ snapshots: postcss: 8.5.16 postcss-nested: 5.0.6(postcss@8.5.16) - '@tailwindcss/node@4.3.1': - dependencies: - '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.21.6 - jiti: 2.7.0 - lightningcss: 1.32.0 - magic-string: 0.30.21 - source-map-js: 1.2.1 - tailwindcss: 4.3.1 - '@tailwindcss/node@4.3.2': dependencies: '@jridgewell/remapping': 2.3.5 @@ -14280,93 +15642,42 @@ snapshots: source-map-js: 1.2.1 tailwindcss: 4.3.2 - '@tailwindcss/oxide-android-arm64@4.3.1': - optional: true - '@tailwindcss/oxide-android-arm64@4.3.2': optional: true - '@tailwindcss/oxide-darwin-arm64@4.3.1': - optional: true - '@tailwindcss/oxide-darwin-arm64@4.3.2': optional: true - '@tailwindcss/oxide-darwin-x64@4.3.1': - optional: true - '@tailwindcss/oxide-darwin-x64@4.3.2': optional: true - '@tailwindcss/oxide-freebsd-x64@4.3.1': - optional: true - '@tailwindcss/oxide-freebsd-x64@4.3.2': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': - optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': - optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.3.1': - optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.3.2': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.3.1': - optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.3.2': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.3.1': - optional: true - '@tailwindcss/oxide-linux-x64-musl@4.3.2': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.3.1': - optional: true - '@tailwindcss/oxide-wasm32-wasi@4.3.2': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': - optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.3.1': - optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.3.2': optional: true - '@tailwindcss/oxide@4.3.1': - optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.3.1 - '@tailwindcss/oxide-darwin-arm64': 4.3.1 - '@tailwindcss/oxide-darwin-x64': 4.3.1 - '@tailwindcss/oxide-freebsd-x64': 4.3.1 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1 - '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1 - '@tailwindcss/oxide-linux-arm64-musl': 4.3.1 - '@tailwindcss/oxide-linux-x64-gnu': 4.3.1 - '@tailwindcss/oxide-linux-x64-musl': 4.3.1 - '@tailwindcss/oxide-wasm32-wasi': 4.3.1 - '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 - '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 - '@tailwindcss/oxide@4.3.2': optionalDependencies: '@tailwindcss/oxide-android-arm64': 4.3.2 @@ -14382,15 +15693,15 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.2 '@tailwindcss/oxide-win32-x64-msvc': 4.3.2 - '@tailwindcss/postcss@4.3.1': + '@tailwindcss/postcss@4.3.2': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.3.1 - '@tailwindcss/oxide': 4.3.1 - postcss: 8.5.15 - tailwindcss: 4.3.1 + '@tailwindcss/node': 4.3.2 + '@tailwindcss/oxide': 4.3.2 + postcss: 8.5.16 + tailwindcss: 4.3.2 - '@tailwindcss/webpack@4.3.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(webpack@5.108.1(lightningcss@1.32.0))': + '@tailwindcss/webpack@4.3.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': dependencies: '@alloc/quick-lru': 5.2.0 '@tailwindcss/node': 4.3.2 @@ -14398,7 +15709,17 @@ snapshots: tailwindcss: 4.3.2 optionalDependencies: '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) - webpack: 5.108.1(lightningcss@1.32.0) + webpack: 5.108.3(lightningcss@1.32.0) + + '@tailwindcss/webpack@4.3.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(webpack@5.108.3(lightningcss@1.32.0))': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.2 + '@tailwindcss/oxide': 4.3.2 + tailwindcss: 4.3.2 + optionalDependencies: + '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + webpack: 5.108.3(lightningcss@1.32.0) '@tanstack/devtools-client@0.0.5': dependencies: @@ -14424,12 +15745,12 @@ snapshots: '@tanstack/devtools-event-client@0.4.4': {} - '@tanstack/devtools-ui@0.5.0(csstype@3.2.3)(solid-js@1.9.13)': + '@tanstack/devtools-ui@0.5.0(csstype@3.2.3)(solid-js@1.9.14)': dependencies: clsx: 2.1.1 dayjs: 1.11.21 goober: 2.1.19(csstype@3.2.3) - solid-js: 1.9.13 + solid-js: 1.9.14 transitivePeerDependencies: - csstype @@ -14444,32 +15765,32 @@ snapshots: '@tanstack/devtools-event-bus': 0.4.0 chalk: 5.6.2 launch-editor: 2.14.1 - picomatch: 4.0.4 + picomatch: 4.0.5 vite: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@tanstack/devtools@0.10.14(csstype@3.2.3)(solid-js@1.9.13)': + '@tanstack/devtools@0.10.14(csstype@3.2.3)(solid-js@1.9.14)': dependencies: - '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.13) - '@solid-primitives/keyboard': 1.3.5(solid-js@1.9.13) - '@solid-primitives/resize-observer': 2.1.5(solid-js@1.9.13) + '@solid-primitives/event-listener': 2.4.5(solid-js@1.9.14) + '@solid-primitives/keyboard': 1.3.5(solid-js@1.9.14) + '@solid-primitives/resize-observer': 2.1.5(solid-js@1.9.14) '@tanstack/devtools-client': 0.0.6 '@tanstack/devtools-event-bus': 0.4.1 - '@tanstack/devtools-ui': 0.5.0(csstype@3.2.3)(solid-js@1.9.13) + '@tanstack/devtools-ui': 0.5.0(csstype@3.2.3)(solid-js@1.9.14) clsx: 2.1.1 goober: 2.1.19(csstype@3.2.3) - solid-js: 1.9.13 + solid-js: 1.9.14 transitivePeerDependencies: - bufferutil - csstype - utf-8-validate - '@tanstack/react-devtools@0.9.13(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.13)': + '@tanstack/react-devtools@0.9.13(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.14)': dependencies: - '@tanstack/devtools': 0.10.14(csstype@3.2.3)(solid-js@1.9.13) + '@tanstack/devtools': 0.10.14(csstype@3.2.3)(solid-js@1.9.14) '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) react: 19.2.7 @@ -14583,8 +15904,18 @@ snapshots: dependencies: '@types/node': 25.0.10 + '@types/cross-spawn@6.0.6': + dependencies: + '@types/node': 25.0.10 + '@types/d3-hierarchy@1.1.11': {} + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/dedent@0.7.2': {} + '@types/deep-eql@4.0.2': {} '@types/eslint@9.6.1': @@ -14592,6 +15923,10 @@ snapshots: '@types/estree': 1.0.9 '@types/json-schema': 7.0.15 + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.9 + '@types/estree@1.0.5': {} '@types/estree@1.0.9': {} @@ -14618,6 +15953,10 @@ snapshots: dependencies: glob: 13.0.0 + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + '@types/http-errors@2.0.5': {} '@types/jsesc@3.0.3': {} @@ -14628,10 +15967,18 @@ snapshots: dependencies: '@types/node': 25.0.10 + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdx@2.0.14': {} + '@types/morgan@1.9.10': dependencies: '@types/node': 25.0.10 + '@types/ms@2.1.0': {} + '@types/mysql@2.15.27': dependencies: '@types/node': 25.0.10 @@ -14642,6 +15989,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@22.20.0': + dependencies: + undici-types: 6.21.0 + '@types/node@25.0.10': dependencies: undici-types: 7.16.0 @@ -14689,6 +16040,8 @@ snapshots: '@types/semver@7.5.8': {} + '@types/semver@7.7.1': {} + '@types/send@1.2.1': dependencies: '@types/node': 25.0.10 @@ -14702,6 +16055,11 @@ snapshots: dependencies: '@types/node': 25.0.10 + '@types/shelljs@0.10.0': + dependencies: + '@types/node': 25.0.10 + fast-glob: 3.3.3 + '@types/source-map-support@0.5.10': dependencies: source-map: 0.6.1 @@ -14719,18 +16077,28 @@ snapshots: '@types/trusted-types@2.0.7': optional: true + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/wait-on@5.3.4': + dependencies: + '@types/node': 25.0.10 + + '@types/webpack-env@1.18.8': {} + '@types/ws@8.18.1': dependencies: '@types/node': 25.0.10 - '@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.62.0 - '@typescript-eslint/type-utils': 8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) - '@typescript-eslint/utils': 8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.62.0 + '@typescript-eslint/parser': 8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/type-utils': 8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.62.1 eslint: 9.39.2(jiti@2.7.0) ignore: 7.0.5 natural-compare: 1.4.0 @@ -14739,41 +16107,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': + '@typescript-eslint/parser@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.62.0 - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/typescript-estree': 8.62.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.62.0 + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.62.1 debug: 4.4.3 eslint: 9.39.2(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.62.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.62.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@5.9.3) - '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@5.9.3) + '@typescript-eslint/types': 8.62.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.62.0': + '@typescript-eslint/scope-manager@8.62.1': dependencies: - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/visitor-keys': 8.62.0 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/visitor-keys': 8.62.1 - '@typescript-eslint/tsconfig-utils@8.62.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.62.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/typescript-estree': 8.62.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.2(jiti@2.7.0) ts-api-utils: 2.5.0(typescript@5.9.3) @@ -14781,14 +16149,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.62.0': {} + '@typescript-eslint/types@8.62.1': {} - '@typescript-eslint/typescript-estree@8.62.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.62.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.62.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@5.9.3) - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/visitor-keys': 8.62.0 + '@typescript-eslint/project-service': 8.62.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@5.9.3) + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/visitor-keys': 8.62.1 debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.5 @@ -14798,22 +16166,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': + '@typescript-eslint/utils@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.7.0)) - '@typescript-eslint/scope-manager': 8.62.0 - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/typescript-estree': 8.62.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) eslint: 9.39.2(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.62.0': + '@typescript-eslint/visitor-keys@8.62.1': dependencies: - '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/types': 8.62.1 eslint-visitor-keys: 5.0.1 + '@ungap/structured-clone@1.3.2': {} + '@unrs/resolver-binding-android-arm-eabi@1.12.2': optional: true @@ -14878,31 +16248,70 @@ snapshots: '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': optional: true - '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': - optional: true + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + optional: true + + '@vanilla-extract/babel-plugin-debug-ids@1.2.2': + dependencies: + '@babel/core': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@vanilla-extract/css@1.21.1': + dependencies: + '@emotion/hash': 0.9.2 + '@vanilla-extract/private': 1.0.9 + css-what: 6.2.2 + csstype: 3.2.3 + dedent: 1.7.2 + deep-object-diff: 1.1.9 + deepmerge: 4.3.1 + lru-cache: 10.4.3 + media-query-parser: 2.0.2 + modern-ahocorasick: 1.1.0 + picocolors: 1.1.1 + transitivePeerDependencies: + - babel-plugin-macros + + '@vanilla-extract/integration@8.0.10': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.0) + '@vanilla-extract/babel-plugin-debug-ids': 1.2.2 + '@vanilla-extract/css': 1.21.1 + dedent: 1.7.2 + esbuild: 0.27.2 + eval: 0.1.8 + find-up: 5.0.0 + javascript-stringify: 2.1.0 + mlly: 1.8.2 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color - '@unrs/resolver-binding-win32-x64-msvc@1.12.2': - optional: true + '@vanilla-extract/private@1.0.9': {} - '@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0))': + '@vanilla-extract/webpack-plugin@2.3.27(webpack@5.108.3(lightningcss@1.32.0))': dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.0) - '@rolldown/pluginutils': 1.0.0-beta.53 - '@types/babel__core': 7.20.5 - react-refresh: 0.18.0 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) + '@vanilla-extract/integration': 8.0.10 + debug: 4.4.3 + loader-utils: 2.0.4 + picocolors: 1.1.1 + webpack: 5.108.3(lightningcss@1.32.0) transitivePeerDependencies: + - babel-plugin-macros - supports-color - '@vitest/eslint-plugin@1.6.20(@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': + '@vitest/eslint-plugin@1.6.20(@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.62.0 - '@typescript-eslint/utils': 8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/utils': 8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) eslint: 9.39.2(jiti@2.7.0) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -15153,14 +16562,14 @@ snapshots: ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 + fast-uri: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 + fast-uri: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -15186,11 +16595,6 @@ snapshots: ansi-styles@6.2.3: {} - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.2 - anynum@1.0.1: {} arch@2.2.0: {} @@ -15282,12 +16686,16 @@ snapshots: assertion-error@2.0.1: {} + astring@1.9.0: {} + async-function@1.0.0: {} + asynckit@0.4.0: {} + autoprefixer@10.4.23(postcss@8.5.16): dependencies: browserslist: 4.28.4 - caniuse-lite: 1.0.30001799 + caniuse-lite: 1.0.30001800 fraction.js: 5.3.4 picocolors: 1.1.1 postcss: 8.5.16 @@ -15297,6 +16705,16 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 + axios@1.18.1: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.6 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + babel-dead-code-elimination@1.0.12: dependencies: '@babel/core': 7.29.0 @@ -15323,6 +16741,8 @@ snapshots: - supports-color - ts-jest + bail@2.0.2: {} + balanced-match@1.0.2: {} balanced-match@4.0.4: {} @@ -15337,9 +16757,9 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.40: {} + baseline-browser-mapping@2.10.41: {} - baseline-browser-mapping@2.10.42: {} + baseline-browser-mapping@2.10.44: {} basic-auth@2.0.1: dependencies: @@ -15366,8 +16786,6 @@ snapshots: bignumber.js@9.3.1: {} - binary-extensions@2.3.0: {} - bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 @@ -15441,7 +16859,7 @@ snapshots: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.6: + brace-expansion@5.0.7: dependencies: balanced-match: 4.0.4 @@ -15457,19 +16875,19 @@ snapshots: browserslist@4.28.4: dependencies: - baseline-browser-mapping: 2.10.40 - caniuse-lite: 1.0.30001799 - electron-to-chromium: 1.5.380 + baseline-browser-mapping: 2.10.41 + caniuse-lite: 1.0.30001800 + electron-to-chromium: 1.5.385 node-releases: 2.0.50 update-browserslist-db: 1.2.3(browserslist@4.28.4) - browserslist@4.28.5: + browserslist@4.28.6: dependencies: - baseline-browser-mapping: 2.10.42 - caniuse-lite: 1.0.30001803 - electron-to-chromium: 1.5.389 + baseline-browser-mapping: 2.10.44 + caniuse-lite: 1.0.30001806 + electron-to-chromium: 1.5.393 node-releases: 2.0.51 - update-browserslist-db: 1.2.3(browserslist@4.28.5) + update-browserslist-db: 1.2.3(browserslist@4.28.6) buffer-from@1.1.2: {} @@ -15514,9 +16932,11 @@ snapshots: camelcase@7.0.1: {} - caniuse-lite@1.0.30001799: {} + caniuse-lite@1.0.30001800: {} + + caniuse-lite@1.0.30001806: {} - caniuse-lite@1.0.30001803: {} + ccount@2.0.1: {} chain-function@1.0.1: {} @@ -15539,19 +16959,38 @@ snapshots: chalk@5.6.2: {} + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + chardet@2.2.0: {} - chokidar@3.6.0: + cheerio-select@2.1.0: dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.2.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.1 + htmlparser2: 10.1.0 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 7.28.0 + whatwg-mimetype: 4.0.0 chokidar@4.0.3: dependencies: @@ -15609,6 +17048,8 @@ snapshots: clsx@2.1.1: {} + collapse-white-space@2.1.0: {} + color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -15623,6 +17064,12 @@ snapshots: colorjs.io@0.5.2: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@2.0.3: {} + commander@11.1.0: {} commander@2.20.3: {} @@ -15660,6 +17107,8 @@ snapshots: tree-kill: 1.2.2 yargs: 17.7.2 + confbox@0.1.8: {} + confbox@0.2.4: {} content-disposition@0.5.2: {} @@ -15676,6 +17125,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie-es@3.1.1: {} + cookie-signature@1.0.7: {} cookie-signature@1.2.2: {} @@ -15867,6 +17318,10 @@ snapshots: decimal.js@10.6.0: {} + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -15881,6 +17336,8 @@ snapshots: deep-is@0.1.4: {} + deep-object-diff@1.1.9: {} + deepmerge@4.3.1: {} defaults@1.0.4: @@ -15899,6 +17356,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + delayed-stream@1.0.0: {} + depd@2.0.0: {} dequal@2.0.3: {} @@ -15911,6 +17370,10 @@ snapshots: detect-node-es@1.1.0: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + dijkstrajs@1.0.3: {} dingbat-to-unicode@1.0.1: {} @@ -15960,8 +17423,6 @@ snapshots: no-case: 3.0.4 tslib: 2.8.1 - dotenv@16.6.1: {} - dotenv@17.2.3: {} duck@0.1.12: @@ -15983,14 +17444,14 @@ snapshots: ee-first@1.1.1: {} - effect@3.21.4: + effect@3.22.0: dependencies: '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 - electron-to-chromium@1.5.380: {} + electron-to-chromium@1.5.385: {} - electron-to-chromium@1.5.389: {} + electron-to-chromium@1.5.393: {} emoji-regex@8.0.0: {} @@ -16000,6 +17461,11 @@ snapshots: encodeurl@2.0.0: {} + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + encoding@0.1.13: dependencies: iconv-lite: 0.6.3 @@ -16034,7 +17500,7 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.3 - enhanced-resolve@5.24.2: + enhanced-resolve@5.24.3: dependencies: graceful-fs: 4.2.11 tapable: 2.3.3 @@ -16048,6 +17514,8 @@ snapshots: entities@6.0.1: {} + entities@7.0.1: {} + entities@8.0.0: {} envinfo@7.21.0: {} @@ -16154,10 +17622,10 @@ snapshots: es-module-lexer@1.7.0: {} - es-module-lexer@2.1.0: {} - es-module-lexer@2.3.0: {} + es-module-lexer@2.3.1: {} + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -16184,6 +17652,20 @@ snapshots: es-toolkit@1.49.0: {} + esast-util-from-estree@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + unist-util-position-from-estree: 2.0.0 + + esast-util-from-js@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + acorn: 8.17.0 + esast-util-from-estree: 2.0.0 + vfile-message: 4.0.3 + esbuild@0.27.2: optionalDependencies: '@esbuild/aix-ppc64': 0.27.2 @@ -16257,9 +17739,9 @@ snapshots: optionalDependencies: unrs-resolver: 1.12.2 - eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0)): + eslint-plugin-import-x@4.17.1(@typescript-eslint/utils@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0)): dependencies: - '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/types': 8.62.1 comment-parser: 1.4.7 debug: 4.4.3 eslint: 9.39.2(jiti@2.7.0) @@ -16270,7 +17752,7 @@ snapshots: stable-hash-x: 0.2.0 unrs-resolver: 1.12.2 optionalDependencies: - '@typescript-eslint/utils': 8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) transitivePeerDependencies: - supports-color @@ -16315,8 +17797,8 @@ snapshots: eslint-plugin-testing-library@7.16.2(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3): dependencies: - '@typescript-eslint/scope-manager': 8.62.0 - '@typescript-eslint/utils': 8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/utils': 8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) eslint: 9.39.2(jiti@2.7.0) transitivePeerDependencies: - supports-color @@ -16399,10 +17881,50 @@ snapshots: estraverse@5.3.0: {} + estree-util-attach-comments@3.0.0: + dependencies: + '@types/estree': 1.0.9 + + estree-util-build-jsx@3.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + estree-walker: 3.0.3 + + estree-util-is-identifier-name@3.0.0: {} + + estree-util-scope@1.0.0: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + + estree-util-to-js@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + astring: 1.9.0 + source-map: 0.7.6 + + estree-util-visit@2.0.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/unist': 3.0.3 + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + esutils@2.0.3: {} etag@1.8.1: {} + eval@0.1.8: + dependencies: + '@types/node': 25.0.10 + require-like: 0.1.2 + events@3.3.0: {} execa@5.1.1: @@ -16530,6 +18052,8 @@ snapshots: exsolve@1.1.0: {} + extend@3.0.2: {} + extendable-error@0.1.7: {} fast-check@3.23.2: @@ -16550,7 +18074,7 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-uri@3.1.2: {} + fast-uri@3.1.3: {} fast-xml-builder@1.2.0: dependencies: @@ -16569,9 +18093,9 @@ snapshots: dependencies: reusify: 1.1.0 - fdir@6.5.0(picomatch@4.0.4): + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: - picomatch: 4.0.4 + picomatch: 4.0.5 figures@6.1.0: dependencies: @@ -16637,6 +18161,8 @@ snapshots: flatted@3.4.2: {} + follow-redirects@1.16.0: {} + for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -16646,15 +18172,23 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + forwarded-parse@2.1.2: {} forwarded@0.2.0: {} fraction.js@5.3.4: {} - framer-motion@12.42.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + framer-motion@12.42.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - motion-dom: 12.42.0 + motion-dom: 12.42.2 motion-utils: 12.39.0 tslib: 2.8.1 optionalDependencies: @@ -16732,6 +18266,8 @@ snapshots: get-port@7.1.0: {} + get-port@7.2.0: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -16815,8 +18351,6 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 - globrex@0.1.2: {} - goober@2.1.19(csstype@3.2.3): dependencies: csstype: 3.2.3 @@ -16851,6 +18385,51 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-to-estree@3.1.3: + dependencies: + '@types/estree': 1.0.9 + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-attach-comments: 3.0.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + zwitch: 2.0.4 + transitivePeerDependencies: + - supports-color + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + he@1.2.0: {} headers-polyfill@4.0.3: {} @@ -16886,6 +18465,13 @@ snapshots: domutils: 3.2.2 entities: 6.0.1 + htmlparser2@10.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 7.0.1 + htmlparser2@8.0.2: dependencies: domelementtype: 2.3.0 @@ -16953,7 +18539,7 @@ snapshots: immediate@3.0.6: {} - immutable@5.1.8: {} + immutable@5.1.9: {} import-fresh@3.3.1: dependencies: @@ -16982,6 +18568,8 @@ snapshots: ini@1.3.8: {} + inline-style-parser@0.2.7: {} + input-otp@1.4.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: react: 19.2.7 @@ -17004,6 +18592,13 @@ snapshots: ipaddr.js@1.9.1: {} + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.9 @@ -17024,10 +18619,6 @@ snapshots: dependencies: has-bigints: 1.1.0 - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - is-boolean-object@1.2.2: dependencies: call-bound: 1.0.4 @@ -17050,6 +18641,8 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-decimal@2.0.1: {} + is-docker@2.2.1: {} is-document.all@1.0.0: @@ -17076,6 +18669,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-hexadecimal@2.0.1: {} + is-interactive@2.0.0: {} is-map@2.0.3: {} @@ -17210,6 +18805,8 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + javascript-stringify@2.1.0: {} + jest-worker@27.5.1: dependencies: '@types/node': 25.0.10 @@ -17220,6 +18817,24 @@ snapshots: jiti@2.7.0: {} + joi@17.13.4: + dependencies: + '@hapi/hoek': 9.3.0 + '@hapi/topo': 5.1.0 + '@sideway/address': 4.1.5 + '@sideway/formula': 3.0.1 + '@sideway/pinpoint': 2.0.0 + + joi@18.2.3: + dependencies: + '@hapi/address': 5.1.1 + '@hapi/formula': 3.0.2 + '@hapi/hoek': 11.0.7 + '@hapi/pinpoint': 2.0.1 + '@hapi/tlds': 1.1.7 + '@hapi/topo': 6.0.2 + '@standard-schema/spec': 1.1.0 + js-tokens@4.0.0: {} js-yaml@3.15.0: @@ -17339,12 +18954,19 @@ snapshots: leaflet@1.9.4: {} - less-loader@12.3.3(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1)(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.1(lightningcss@1.32.0)): + less-loader@12.3.3(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0)): dependencies: less: 4.6.7 optionalDependencies: '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) - webpack: 5.108.1(lightningcss@1.32.0) + webpack: 5.108.3(lightningcss@1.32.0) + + less-loader@12.3.3(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(less@4.6.7)(webpack@5.108.3(lightningcss@1.32.0)): + dependencies: + less: 4.6.7 + optionalDependencies: + '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + webpack: 5.108.3(lightningcss@1.32.0) less@4.6.7: dependencies: @@ -17421,7 +19043,7 @@ snapshots: lines-and-columns@2.0.4: {} - linkify-it@5.0.1: + linkify-it@5.0.2: dependencies: uc.micro: 2.1.0 @@ -17467,6 +19089,8 @@ snapshots: long-timeout@0.1.1: {} + longest-streak@3.1.0: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -17487,80 +19111,387 @@ snapshots: lru-cache@11.5.1: {} - lru-cache@5.1.1: + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + luxon@3.7.2: {} + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-dir@4.0.0: + dependencies: + semver: 7.8.5 + + make-dir@5.1.0: + optional: true + + mammoth@1.12.0: + dependencies: + '@xmldom/xmldom': 0.8.13 + argparse: 1.0.10 + base64-js: 1.5.1 + bluebird: 3.4.7 + dingbat-to-unicode: 1.0.1 + jszip: 3.10.1 + lop: 0.4.2 + path-is-absolute: 1.0.1 + underscore: 1.13.8 + xmlbuilder: 10.1.1 + + markdown-extensions@2.0.0: {} + + markdown-it@14.2.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.2 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + marked@14.0.0: {} + + marked@15.0.12: {} + + math-intrinsics@1.1.0: {} + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx@3.0.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.2 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + mdn-data@2.27.1: {} + + mdurl@2.0.0: {} + + media-query-parser@2.0.2: + dependencies: + '@babel/runtime': 7.29.7 + + media-typer@0.3.0: {} + + media-typer@1.1.0: {} + + memorystream@0.3.1: {} + + merge-descriptors@1.0.3: {} + + merge-descriptors@2.0.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-expression@3.0.1: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-mdx-jsx@3.0.2: + dependencies: + '@types/estree': 1.0.9 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + micromark-factory-mdx-expression: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 + + micromark-extension-mdx-md@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-mdxjs-esm@3.0.0: dependencies: - yallist: 3.1.1 + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 - luxon@3.7.2: {} + micromark-extension-mdxjs@3.0.0: + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + micromark-extension-mdx-expression: 3.0.1 + micromark-extension-mdx-jsx: 3.0.2 + micromark-extension-mdx-md: 2.0.0 + micromark-extension-mdxjs-esm: 3.0.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 - lz-string@1.5.0: {} + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - magic-string@0.30.21: + micromark-factory-label@2.0.1: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - magic-string@0.30.8: + micromark-factory-mdx-expression@2.0.3: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@types/estree': 1.0.9 + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-events-to-acorn: 2.0.3 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-position-from-estree: 2.0.0 + vfile-message: 4.0.3 - make-dir@4.0.0: + micromark-factory-space@2.0.1: dependencies: - semver: 7.8.5 + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 - make-dir@5.1.0: - optional: true + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - mammoth@1.12.0: + micromark-factory-whitespace@2.0.1: dependencies: - '@xmldom/xmldom': 0.8.13 - argparse: 1.0.10 - base64-js: 1.5.1 - bluebird: 3.4.7 - dingbat-to-unicode: 1.0.1 - jszip: 3.10.1 - lop: 0.4.2 - path-is-absolute: 1.0.1 - underscore: 1.13.8 - xmlbuilder: 10.1.1 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - markdown-it@14.2.0: + micromark-util-character@2.1.1: dependencies: - argparse: 2.0.1 - entities: 4.5.0 - linkify-it: 5.0.1 - mdurl: 2.0.0 - punycode.js: 2.3.1 - uc.micro: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - marked@14.0.0: {} + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 - marked@15.0.12: {} + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - math-intrinsics@1.1.0: {} + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 - mdn-data@2.0.28: {} + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 - mdn-data@2.0.30: {} + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 - mdn-data@2.27.1: {} + micromark-util-encode@2.0.1: {} - mdurl@2.0.0: {} + micromark-util-events-to-acorn@2.0.3: + dependencies: + '@types/estree': 1.0.9 + '@types/unist': 3.0.3 + devlop: 1.1.0 + estree-util-visit: 2.0.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + vfile-message: 4.0.3 - media-typer@0.3.0: {} + micromark-util-html-tag-name@2.0.1: {} - media-typer@1.1.0: {} + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 - memorystream@0.3.1: {} + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 - merge-descriptors@1.0.3: {} + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 - merge-descriptors@2.0.0: {} + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 - merge-stream@2.0.0: {} + micromark-util-symbol@2.0.1: {} - merge2@1.4.1: {} + micromark-util-types@2.0.2: {} - methods@1.1.2: {} + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color micromatch@4.0.8: dependencies: @@ -17595,12 +19526,12 @@ snapshots: min-indent@1.0.1: {} - miniflare@4.20260625.0: + miniflare@4.20260701.0: dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 undici: 7.28.0 - workerd: 1.20260625.1 + workerd: 1.20260701.1 ws: 8.21.0 youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -17609,7 +19540,7 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.6 + brace-expansion: 5.0.7 minimatch@3.1.5: dependencies: @@ -17621,26 +19552,26 @@ snapshots: minimist@1.2.8: {} - minimizer-webpack-plugin@5.6.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)): + minimizer-webpack-plugin@5.6.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.49.0 - webpack: 5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16) + webpack: 5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16) optionalDependencies: esbuild: 0.27.2 lightningcss: 1.32.0 postcss: 8.5.16 optional: true - minimizer-webpack-plugin@5.6.1(lightningcss@1.32.0)(webpack@5.108.1(lightningcss@1.32.0)): + minimizer-webpack-plugin@5.6.1(lightningcss@1.32.0)(webpack@5.108.3(lightningcss@1.32.0)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.49.0 - webpack: 5.108.1(lightningcss@1.32.0) + webpack: 5.108.3(lightningcss@1.32.0) optionalDependencies: lightningcss: 1.32.0 @@ -17648,6 +19579,15 @@ snapshots: mkdirp-classic@0.5.3: {} + mlly@1.8.2: + dependencies: + acorn: 8.17.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + + modern-ahocorasick@1.1.0: {} + module-details-from-path@1.0.4: {} monaco-editor@0.55.1: @@ -17667,7 +19607,7 @@ snapshots: transitivePeerDependencies: - supports-color - motion-dom@12.42.0: + motion-dom@12.42.2: dependencies: motion-utils: 12.39.0 @@ -17742,7 +19682,7 @@ snapshots: lower-case: 2.0.2 tslib: 2.8.1 - node-abi@3.92.0: + node-abi@3.94.0: dependencies: semver: 7.8.5 @@ -17789,8 +19729,6 @@ snapshots: semver: 5.7.2 validate-npm-package-license: 3.0.4 - normalize-path@3.0.0: {} - npm-run-all@4.1.5: dependencies: ansi-styles: 3.2.1 @@ -17933,7 +19871,7 @@ snapshots: p-map@2.1.0: {} - p-map@7.0.4: {} + p-map@7.0.5: {} p-try@2.2.0: {} @@ -17949,6 +19887,16 @@ snapshots: dependencies: callsites: 3.1.0 + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + parse-json@4.0.0: dependencies: error-ex: 1.3.4 @@ -17967,6 +19915,19 @@ snapshots: parse-passwd@1.0.0: {} + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.3.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + parse5@8.0.1: dependencies: entities: 8.0.0 @@ -18049,16 +20010,24 @@ snapshots: picomatch@2.3.2: {} - picomatch@4.0.4: {} + picomatch@4.0.5: {} pidtree@0.3.1: {} + pify@2.3.0: {} + pify@3.0.0: {} pify@4.0.1: {} pkg-pr-new@0.0.75: {} + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + pkg-types@2.3.1: dependencies: confbox: 0.2.4 @@ -18077,6 +20046,13 @@ snapshots: possible-typed-array-names@1.1.0: {} + postcss-import@16.1.1(postcss@8.5.16): + dependencies: + postcss: 8.5.16 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.12 + postcss-nested@5.0.6(postcss@8.5.16): dependencies: postcss: 8.5.16 @@ -18089,12 +20065,6 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.5.15: - dependencies: - nanoid: 3.3.15 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.16: dependencies: nanoid: 3.3.15 @@ -18119,7 +20089,7 @@ snapshots: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.92.0 + node-abi: 3.94.0 pump: 3.0.4 rc: 1.2.8 simple-get: 4.0.1 @@ -18148,7 +20118,7 @@ snapshots: prettier@3.8.1: {} - prettier@3.9.1: {} + prettier@3.9.4: {} pretty-format@27.5.1: dependencies: @@ -18170,8 +20140,6 @@ snapshots: process-nextick-args@2.0.1: {} - progress@2.0.3: {} - prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -18183,6 +20151,8 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + property-information@7.2.0: {} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -18190,7 +20160,7 @@ snapshots: proxy-compare@3.0.1: {} - proxy-from-env@1.1.0: {} + proxy-from-env@2.1.0: {} prr@1.0.1: optional: true @@ -18228,6 +20198,10 @@ snapshots: discontinuous-range: 1.0.0 ret: 0.1.15 + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + range-parser@1.2.0: {} range-parser@1.2.1: {} @@ -18321,33 +20295,33 @@ snapshots: optionalDependencies: '@types/react': 19.2.10 - react-router-devtools@6.2.1(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(solid-js@1.9.13)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)): + react-router-devtools@6.2.1(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(solid-js@1.9.14)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)): dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.7 '@babel/parser': 7.29.7 '@babel/traverse': 7.29.7 '@babel/types': 7.29.7 - '@radix-ui/react-accordion': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-accordion': 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tanstack/devtools-client': 0.0.5 '@tanstack/devtools-event-client': 0.4.4 '@tanstack/devtools-vite': 0.4.1(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)) - '@tanstack/react-devtools': 0.9.13(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.13) + '@tanstack/react-devtools': 0.9.13(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(csstype@3.2.3)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(solid-js@1.9.14) '@types/react': 19.2.10 '@types/react-dom': 19.2.3(@types/react@19.2.10) chalk: 5.6.2 clsx: 2.1.1 - framer-motion: 12.42.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + framer-motion: 12.42.2(react-dom@19.2.7(react@19.2.7))(react@19.2.7) goober: 2.1.19(csstype@3.2.3) react: 19.2.7 react-d3-tree: 3.6.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react-dom: 19.2.7(react@19.2.7) react-hotkeys-hook: 5.3.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react-tooltip: 5.30.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) vite: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.5.1 + '@biomejs/cli-darwin-arm64': 2.5.2 '@rollup/rollup-darwin-arm64': 4.62.2 '@rollup/rollup-linux-x64-gnu': 4.62.2 transitivePeerDependencies: @@ -18365,11 +20339,11 @@ snapshots: react-dom: 19.2.7(react@19.2.7) react-router: 6.30.4(react@19.2.7) - react-router-dom@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + react-router-dom@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react-router@6.30.4(react@19.2.7): dependencies: @@ -18384,7 +20358,15 @@ snapshots: optionalDependencies: react-dom: 19.2.3(react@19.2.3) - react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + cookie: 1.1.1 + react: 19.2.7 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.7(react@19.2.7) + + react-router@7.18.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: cookie: 1.1.1 react: 19.2.7 @@ -18392,6 +20374,25 @@ snapshots: optionalDependencies: react-dom: 19.2.7(react@19.2.7) + react-router@8.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + cookie-es: 3.1.1 + react: 19.2.7 + optionalDependencies: + react-dom: 19.2.7(react@19.2.7) + + react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + '@rspack/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + '@rspack/core': 2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-style-singleton@2.2.3(@types/react@19.2.10)(react@19.2.7): dependencies: get-nonce: 1.0.1 @@ -18411,6 +20412,10 @@ snapshots: react@19.2.7: {} + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + read-pkg@3.0.0: dependencies: load-json-file: 4.0.0 @@ -18440,14 +20445,39 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.2 - readdirp@4.1.2: {} readdirp@5.0.0: {} + recma-build-jsx@1.0.0: + dependencies: + '@types/estree': 1.0.9 + estree-util-build-jsx: 3.0.1 + vfile: 6.0.3 + + recma-jsx@1.0.1(acorn@8.17.0): + dependencies: + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) + estree-util-to-js: 2.0.0 + recma-parse: 1.0.0 + recma-stringify: 1.0.0 + unified: 11.0.5 + + recma-parse@1.0.0: + dependencies: + '@types/estree': 1.0.9 + esast-util-from-js: 2.0.1 + unified: 11.0.5 + vfile: 6.0.3 + + recma-stringify@1.0.0: + dependencies: + '@types/estree': 1.0.9 + estree-util-to-js: 2.0.0 + unified: 11.0.5 + vfile: 6.0.3 + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -18484,6 +20514,38 @@ snapshots: dependencies: rc: 1.2.8 + rehype-recma@1.0.0: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.4 + hast-util-to-estree: 3.1.3 + transitivePeerDependencies: + - supports-color + + remark-mdx@3.1.1: + dependencies: + mdast-util-mdx: 3.0.0 + micromark-extension-mdxjs: 3.0.0 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + remix-auth-github@3.0.2(remix-auth@4.2.0): dependencies: '@mjackson/headers': 0.9.0 @@ -18500,7 +20562,7 @@ snapshots: fs-extra: 11.3.3 minimatch: 10.2.5 - remix-utils@9.0.0(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(@standard-schema/spec@1.1.0)(intl-parse-accept-language@1.0.0)(react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7): + remix-utils@9.0.0(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(@standard-schema/spec@1.1.0)(intl-parse-accept-language@1.0.0)(react-router@7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7): dependencies: type-fest: 4.41.0 optionalDependencies: @@ -18509,7 +20571,7 @@ snapshots: '@standard-schema/spec': 1.1.0 intl-parse-accept-language: 1.0.0 react: 19.2.7 - react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router: 7.14.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) require-directory@2.1.1: {} @@ -18522,6 +20584,8 @@ snapshots: transitivePeerDependencies: - supports-color + require-like@0.1.2: {} + require-main-filename@2.0.0: {} requireindex@1.2.0: {} @@ -18615,13 +20679,23 @@ snapshots: transitivePeerDependencies: - supports-color - rsbuild-plugin-dts@0.22.1(@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0))(typescript@5.9.3): + rsbuild-plugin-dts@0.22.1(@rsbuild/core@2.0.15(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(typescript@5.9.3): dependencies: '@ast-grep/napi': 0.37.0 - '@rsbuild/core': 2.0.15(@module-federation/runtime-tools@2.5.1)(core-js@3.47.0) + '@rsbuild/core': 2.0.15(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) optionalDependencies: typescript: 5.9.3 + rsbuild-plugin-rsc@0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)): + dependencies: + '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + react-server-dom-rspack: 0.0.2(@rspack/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + + rsbuild-plugin-rsc@0.1.1(@rsbuild/core@2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0))(react-server-dom-rspack@0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)): + dependencies: + '@rsbuild/core': 2.1.0(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(core-js@3.47.0) + react-server-dom-rspack: 0.0.2(@rspack/core@2.1.4(@module-federation/runtime-tools@2.5.1(node-fetch@2.7.0(encoding@0.1.13)))(@swc/helpers@0.5.23))(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + rslog@2.1.3: {} run-parallel@1.2.0: @@ -18719,7 +20793,7 @@ snapshots: dependencies: '@bufbuild/protobuf': 2.12.1 colorjs.io: 0.5.2 - immutable: 5.1.8 + immutable: 5.1.9 rxjs: 7.8.2 supports-color: 8.1.1 sync-child-process: 1.0.2 @@ -18747,7 +20821,7 @@ snapshots: sass@1.100.0: dependencies: chokidar: 5.0.0 - immutable: 5.1.8 + immutable: 5.1.9 source-map-js: 1.2.1 optionalDependencies: '@parcel/watcher': 2.5.6 @@ -18821,11 +20895,15 @@ snapshots: transitivePeerDependencies: - supports-color - seroval-plugins@1.5.5(seroval@1.5.5): + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + seroval-plugins@1.5.6(seroval@1.5.6): dependencies: - seroval: 1.5.5 + seroval: 1.5.6 - seroval@1.5.5: {} + seroval@1.5.6: {} serve-handler@6.1.7: dependencies: @@ -18952,6 +21030,11 @@ snapshots: shell-quote@1.9.0: {} + shelljs@0.10.0: + dependencies: + execa: 5.1.1 + fast-glob: 3.3.3 + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 @@ -19031,11 +21114,11 @@ snapshots: - supports-color - utf-8-validate - solid-js@1.9.13: + solid-js@1.9.14: dependencies: csstype: 3.2.3 - seroval: 1.5.5 - seroval-plugins: 1.5.5(seroval@1.5.5) + seroval: 1.5.6 + seroval-plugins: 1.5.6(seroval@1.5.6) sonner@2.0.7(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: @@ -19055,6 +21138,8 @@ snapshots: source-map@0.7.6: {} + space-separated-tokens@2.0.2: {} + spawndamnit@3.0.1: dependencies: cross-spawn: 7.0.6 @@ -19100,10 +21185,10 @@ snapshots: strict-event-emitter@0.5.1: {} - string-replace-loader@3.3.0(webpack@5.108.1(lightningcss@1.32.0)): + string-replace-loader@3.3.0(webpack@5.108.3(lightningcss@1.32.0)): dependencies: schema-utils: 4.3.3 - webpack: 5.108.1(lightningcss@1.32.0) + webpack: 5.108.3(lightningcss@1.32.0) string-width@4.2.3: dependencies: @@ -19177,6 +21262,11 @@ snapshots: dependencies: safe-buffer: 5.2.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -19197,6 +21287,8 @@ snapshots: dependencies: min-indent: 1.0.1 + strip-indent@4.1.1: {} + strip-json-comments@2.0.1: {} strip-json-comments@3.1.1: {} @@ -19205,6 +21297,14 @@ snapshots: dependencies: anynum: 1.0.1 + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + supports-color@10.2.2: {} supports-color@5.5.0: @@ -19293,16 +21393,16 @@ snapshots: tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 tinypool@1.1.1: {} - tldts-core@7.4.5: {} + tldts-core@7.4.6: {} - tldts@7.4.5: + tldts@7.4.6: dependencies: - tldts-core: 7.4.5 + tldts-core: 7.4.6 to-data-view@2.0.0: {} @@ -19314,7 +21414,7 @@ snapshots: tough-cookie@6.0.1: dependencies: - tldts: 7.4.5 + tldts: 7.4.6 tr46@0.0.3: {} @@ -19324,14 +21424,14 @@ snapshots: tree-kill@1.2.2: {} + trim-lines@3.0.1: {} + + trough@2.2.0: {} + ts-api-utils@2.5.0(typescript@5.9.3): dependencies: typescript: 5.9.3 - tsconfck@3.1.6(typescript@5.9.3): - optionalDependencies: - typescript: 5.9.3 - tslib@1.14.1: {} tslib@2.3.0: {} @@ -19409,12 +21509,12 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3): + typescript-eslint@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) - '@typescript-eslint/parser': 8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.62.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.62.0(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.62.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.1(eslint@9.39.2(jiti@2.7.0))(typescript@5.9.3) eslint: 9.39.2(jiti@2.7.0) typescript: 5.9.3 transitivePeerDependencies: @@ -19426,6 +21526,8 @@ snapshots: uc.micro@2.1.0: {} + ufo@1.6.4: {} + unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 @@ -19452,19 +21554,49 @@ snapshots: unicorn-magic@0.3.0: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position-from-estree@2.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + universalify@0.1.2: {} universalify@2.0.1: {} unpipe@1.0.0: {} - unplugin@1.0.1: - dependencies: - acorn: 8.17.0 - chokidar: 3.6.0 - webpack-sources: 3.5.0 - webpack-virtual-modules: 0.5.0 - unrs-resolver@1.12.2: dependencies: napi-postinstall: 0.3.4 @@ -19502,9 +21634,9 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - update-browserslist-db@1.2.3(browserslist@4.28.5): + update-browserslist-db@1.2.3(browserslist@4.28.6): dependencies: - browserslist: 4.28.5 + browserslist: 4.28.6 escalade: 3.2.0 picocolors: 1.1.1 @@ -19562,18 +21694,15 @@ snapshots: vary@1.1.2: {} - vite-env-only@3.0.3(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)): + vfile-message@4.0.3: dependencies: - '@babel/core': 7.29.0 - '@babel/generator': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 - babel-dead-code-elimination: 1.0.12 - micromatch: 4.0.8 - vite: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) - transitivePeerDependencies: - - supports-color + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 vite-node@3.2.4(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0): dependencies: @@ -19638,21 +21767,11 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0)): - dependencies: - debug: 4.4.3 - globrex: 0.1.2 - tsconfck: 3.1.6(typescript@5.9.3) - vite: 7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0) - transitivePeerDependencies: - - supports-color - - typescript - vite@7.3.1(@types/node@22.15.31)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0): dependencies: esbuild: 0.27.2 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 postcss: 8.5.16 rollup: 4.62.2 tinyglobby: 0.2.17 @@ -19667,11 +21786,30 @@ snapshots: terser: 5.49.0 tsx: 4.21.0 + vite@7.3.1(@types/node@22.20.0)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0): + dependencies: + esbuild: 0.27.2 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + postcss: 8.5.16 + rollup: 4.62.2 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 22.20.0 + fsevents: 2.3.3 + jiti: 2.7.0 + less: 4.6.7 + lightningcss: 1.32.0 + sass: 1.100.0 + sass-embedded: 1.100.0 + terser: 5.49.0 + tsx: 4.21.0 + vite@7.3.1(@types/node@25.0.10)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0): dependencies: esbuild: 0.27.2 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 postcss: 8.5.16 rollup: 4.62.2 tinyglobby: 0.2.17 @@ -19689,8 +21827,8 @@ snapshots: vite@7.3.1(@types/node@25.9.5)(jiti@2.7.0)(less@4.6.7)(lightningcss@1.32.0)(sass-embedded@1.100.0)(sass@1.100.0)(terser@5.49.0)(tsx@4.21.0): dependencies: esbuild: 0.27.2 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 postcss: 8.5.16 rollup: 4.62.2 tinyglobby: 0.2.17 @@ -19709,6 +21847,28 @@ snapshots: dependencies: xml-name-validator: 5.0.0 + wait-on@7.2.0: + dependencies: + axios: 1.18.1 + joi: 17.13.4 + lodash: 4.18.1 + minimist: 1.2.8 + rxjs: 7.8.2 + transitivePeerDependencies: + - debug + - supports-color + + wait-on@9.0.10: + dependencies: + axios: 1.18.1 + joi: 18.2.3 + lodash: 4.18.1 + minimist: 1.2.8 + rxjs: 7.8.2 + transitivePeerDependencies: + - debug + - supports-color + warning@3.0.0: dependencies: loose-envify: 1.4.0 @@ -19725,13 +21885,9 @@ snapshots: webidl-conversions@8.0.1: {} - webpack-sources@3.5.0: {} - webpack-sources@3.5.1: {} - webpack-virtual-modules@0.5.0: {} - - webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16): + webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16): dependencies: '@types/estree': 1.0.9 '@types/json-schema': 7.0.15 @@ -19740,16 +21896,16 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.17.0 acorn-import-phases: 1.0.4(acorn@8.17.0) - browserslist: 4.28.5 + browserslist: 4.28.6 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.24.2 - es-module-lexer: 2.3.0 + enhanced-resolve: 5.24.3 + es-module-lexer: 2.3.1 eslint-scope: 5.1.1 events: 3.3.0 graceful-fs: 4.2.11 loader-runner: 4.3.2 mime-db: 1.54.0 - minimizer-webpack-plugin: 5.6.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)(webpack@5.108.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) + minimizer-webpack-plugin: 5.6.1(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)(webpack@5.108.3(esbuild@0.27.2)(lightningcss@1.32.0)(postcss@8.5.16)) neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.3 @@ -19770,7 +21926,7 @@ snapshots: - uglify-js optional: true - webpack@5.108.1(lightningcss@1.32.0): + webpack@5.108.3(lightningcss@1.32.0): dependencies: '@types/estree': 1.0.9 '@types/json-schema': 7.0.15 @@ -19779,16 +21935,16 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.17.0 acorn-import-phases: 1.0.4(acorn@8.17.0) - browserslist: 4.28.5 + browserslist: 4.28.6 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.24.2 - es-module-lexer: 2.3.0 + enhanced-resolve: 5.24.3 + es-module-lexer: 2.3.1 eslint-scope: 5.1.1 events: 3.3.0 graceful-fs: 4.2.11 loader-runner: 4.3.2 mime-db: 1.54.0 - minimizer-webpack-plugin: 5.6.1(lightningcss@1.32.0)(webpack@5.108.1(lightningcss@1.32.0)) + minimizer-webpack-plugin: 5.6.1(lightningcss@1.32.0)(webpack@5.108.3(lightningcss@1.32.0)) neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.3 @@ -19808,6 +21964,10 @@ snapshots: - postcss - uglify-js + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-mimetype@4.0.0: {} whatwg-mimetype@5.0.0: {} @@ -19879,26 +22039,26 @@ snapshots: word-wrap@1.2.5: {} - workerd@1.20260625.1: + workerd@1.20260701.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260625.1 - '@cloudflare/workerd-darwin-arm64': 1.20260625.1 - '@cloudflare/workerd-linux-64': 1.20260625.1 - '@cloudflare/workerd-linux-arm64': 1.20260625.1 - '@cloudflare/workerd-windows-64': 1.20260625.1 + '@cloudflare/workerd-darwin-64': 1.20260701.1 + '@cloudflare/workerd-darwin-arm64': 1.20260701.1 + '@cloudflare/workerd-linux-64': 1.20260701.1 + '@cloudflare/workerd-linux-arm64': 1.20260701.1 + '@cloudflare/workerd-windows-64': 1.20260701.1 - wrangler@4.105.0(@cloudflare/workers-types@4.20260628.1): + wrangler@4.107.0(@cloudflare/workers-types@4.20260702.1): dependencies: '@cloudflare/kv-asset-handler': 0.5.0 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260625.1) + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260701.1) blake3-wasm: 2.1.5 esbuild: 0.28.1 - miniflare: 4.20260625.0 + miniflare: 4.20260701.0 path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260625.1 + workerd: 1.20260701.1 optionalDependencies: - '@cloudflare/workers-types': 4.20260628.1 + '@cloudflare/workers-types': 4.20260702.1 fsevents: 2.3.3 transitivePeerDependencies: - bufferutil @@ -20055,3 +22215,5 @@ snapshots: zrender@6.1.0: dependencies: tslib: 2.3.0 + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d3e61d7d..264a1c51 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,3 +4,7 @@ packages: - 'config' - 'examples/*' - 'examples/federation/*' + - 'tests/react-router-framework/integration' + - 'tests/react-router-framework/integration/helpers/rsbuild-template' + - 'tests/react-router-framework/integration/helpers/rsc-framework' + - 'tests/react-router-framework/integration/helpers/rsc-preview' diff --git a/rslib.config.ts b/rslib.config.ts index 566bf25c..7dab55e6 100644 --- a/rslib.config.ts +++ b/rslib.config.ts @@ -13,8 +13,12 @@ const config = defineConfig({ index: './src/index.ts', 'parallel-route-transform-worker': './src/parallel-route-transform-worker.ts', + 'rsc-route-transform-loader': './src/rsc-route-transform-loader.ts', 'templates/entry.server': './src/templates/entry.server.tsx', 'templates/entry.client': './src/templates/entry.client.tsx', + 'templates/entry.rsc': './src/templates/entry.rsc.tsx', + 'templates/entry.rsc.client': './src/templates/entry.rsc.client.tsx', + 'templates/entry.rsc.ssr': './src/templates/entry.rsc.ssr.tsx', }, }, }, @@ -23,6 +27,7 @@ const config = defineConfig({ source: { entry: { index: './src/index.ts', + 'rsc-route-transform-loader': './src/rsc-route-transform-loader.ts', 'templates/entry.server': './src/templates/entry.server.tsx', 'templates/entry.client': './src/templates/entry.client.tsx', }, @@ -34,11 +39,15 @@ const config = defineConfig({ externals: [ ...commonExternals, 'user-routes', + /^virtual\/react-router\//, + /^virtual:react-router\//, /^react-router-dom/, /^react-router/, /^@react-router/, 'react', /^react-dom/, + /^react-server-dom-rspack/, + /^rsbuild-plugin-rsc/, ], }, }, diff --git a/rstest.config.ts b/rstest.config.ts index 2df703fe..8e7ba5a1 100644 --- a/rstest.config.ts +++ b/rstest.config.ts @@ -1,7 +1,10 @@ import { defineConfig } from '@rstest/core'; export default defineConfig({ - include: ['tests/**/*.test.ts'], + include: [ + 'tests/**/*.test.ts', + 'tests/react-router-framework/react-router-dev/__tests__/rsc-virtual-route-modules-test.ts', + ], exclude: ['**/node_modules/**', '**/dist/**', 'examples/**'], testEnvironment: 'node', setupFiles: ['./tests/setup.ts'], diff --git a/scripts/check-react-router-framework-upstream.d.mts b/scripts/check-react-router-framework-upstream.d.mts new file mode 100644 index 00000000..16a467d4 --- /dev/null +++ b/scripts/check-react-router-framework-upstream.d.mts @@ -0,0 +1,38 @@ +export interface UpstreamChange { + kind: string; + status: string; + path: string; + oldPath?: string; +} + +export interface CategorizedUpstreamChanges { + directoriesWithAddedFiles: string[]; + added: string[]; + modified: string[]; + deleted: string[]; + renamed: UpstreamChange[]; + other: UpstreamChange[]; +} + +export interface UpstreamAuditOptions { + sourceRoot: string; + baseRef: string; + targetRef?: string; + sourceDirs: string[]; +} + +export interface UpstreamAudit extends CategorizedUpstreamChanges { + baseCommit: string; + targetCommit: string; +} + +export declare const parseNameStatus: ( + output: Buffer | string +) => UpstreamChange[]; +export declare const categorizeChanges: ( + changes: UpstreamChange[] +) => CategorizedUpstreamChanges; +export declare const auditUpstream: ( + options: UpstreamAuditOptions +) => UpstreamAudit; +export declare const printAudit: (audit: UpstreamAudit) => void; diff --git a/scripts/check-react-router-framework-upstream.mjs b/scripts/check-react-router-framework-upstream.mjs new file mode 100755 index 00000000..74461578 --- /dev/null +++ b/scripts/check-react-router-framework-upstream.mjs @@ -0,0 +1,178 @@ +#!/usr/bin/env node + +import { execFileSync } from 'node:child_process'; +import { readFile } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const scriptPath = fileURLToPath(import.meta.url); +const repoRoot = path.resolve(path.dirname(scriptPath), '..'); +const manifestPath = path.join( + repoRoot, + 'tests/react-router-framework/UPSTREAM.json' +); + +const git = (sourceRoot, args, options = {}) => + execFileSync('git', ['-C', sourceRoot, ...args], { + stdio: ['ignore', 'pipe', 'pipe'], + ...options, + }); + +export const parseNameStatus = output => { + const fields = output.toString('utf8').split('\0'); + if (fields.at(-1) === '') { + fields.pop(); + } + + const changes = []; + for (let index = 0; index < fields.length; ) { + const status = fields[index++]; + if (!status) { + throw new Error('Malformed git diff output: missing status'); + } + const kind = status[0]; + if (kind === 'R' || kind === 'C') { + const oldPath = fields[index++]; + const newPath = fields[index++]; + if (!oldPath || !newPath) { + throw new Error(`Malformed git diff output for ${status}`); + } + changes.push({ kind, status, oldPath, path: newPath }); + continue; + } + + const changedPath = fields[index++]; + if (!changedPath) { + throw new Error(`Malformed git diff output for ${status}`); + } + changes.push({ kind, status, path: changedPath }); + } + return changes; +}; + +export const categorizeChanges = changes => { + const result = { + directoriesWithAddedFiles: [], + added: [], + modified: [], + deleted: [], + renamed: [], + other: [], + }; + const directoriesWithAddedFiles = new Set(); + + for (const change of changes) { + if (change.kind === 'A') { + result.added.push(change.path); + const directory = path.posix.dirname(change.path); + if (directory !== '.') { + directoriesWithAddedFiles.add(directory); + } + } else if (change.kind === 'M') { + result.modified.push(change.path); + } else if (change.kind === 'D') { + result.deleted.push(change.path); + } else if (change.kind === 'R') { + result.renamed.push(change); + } else { + result.other.push(change); + } + } + + result.directoriesWithAddedFiles = [...directoriesWithAddedFiles].sort(); + for (const key of ['added', 'modified', 'deleted']) { + result[key].sort(); + } + result.renamed.sort((left, right) => left.path.localeCompare(right.path)); + result.other.sort((left, right) => left.path.localeCompare(right.path)); + return result; +}; + +const resolveCommit = (sourceRoot, ref) => + git(sourceRoot, ['rev-parse', '--verify', `${ref}^{commit}`], { + encoding: 'utf8', + }).trim(); + +export const auditUpstream = ({ + sourceRoot, + baseRef, + targetRef = 'HEAD', + sourceDirs, +}) => { + const baseCommit = resolveCommit(sourceRoot, baseRef); + const targetCommit = resolveCommit(sourceRoot, targetRef); + const output = git( + sourceRoot, + [ + 'diff', + '--name-status', + '-z', + '--find-renames', + baseCommit, + targetCommit, + '--', + ...sourceDirs, + ], + { encoding: 'buffer' } + ); + return { + baseCommit, + targetCommit, + ...categorizeChanges(parseNameStatus(output)), + }; +}; + +const printPaths = (label, paths) => { + console.log(`${label} (${paths.length})`); + for (const entry of paths) { + console.log(` ${entry}`); + } +}; + +export const printAudit = audit => { + console.log(`React Router framework upstream audit`); + console.log(` reviewed: ${audit.baseCommit}`); + console.log(` target: ${audit.targetCommit}`); + printPaths('Directories with added files', audit.directoriesWithAddedFiles); + printPaths('Added files', audit.added); + printPaths('Modified files', audit.modified); + printPaths('Deleted files', audit.deleted); + console.log(`Renamed files (${audit.renamed.length})`); + for (const change of audit.renamed) { + console.log(` ${change.oldPath} -> ${change.path}`); + } + console.log(`Other changes (${audit.other.length})`); + for (const change of audit.other) { + console.log(` ${change.status} ${change.path}`); + } + console.log( + '\nReview these changes manually. Copy and adapt only the tests this ' + + 'repository needs, run them, then update lastReviewedRef and reviewedAt ' + + 'in tests/react-router-framework/UPSTREAM.json.' + ); +}; + +const readOption = name => + process.argv + .find(argument => argument.startsWith(`--${name}=`)) + ?.slice(name.length + 3); + +const isDirectExecution = + process.argv[1] && path.resolve(process.argv[1]) === scriptPath; + +if (isDirectExecution) { + const sourceOption = readOption('source') ?? process.env.REACT_ROUTER_REPO; + if (!sourceOption) { + throw new Error( + 'Pass --source=/path/to/react-router or set REACT_ROUTER_REPO.' + ); + } + const manifest = JSON.parse(await readFile(manifestPath, 'utf8')); + const audit = auditUpstream({ + sourceRoot: path.resolve(sourceOption), + baseRef: manifest.lastReviewedRef, + targetRef: readOption('target') ?? 'HEAD', + sourceDirs: manifest.sourceDirs, + }); + printAudit(audit); +} diff --git a/scripts/test-package-interop.mts b/scripts/test-package-interop.mts index 34369239..ce106ee4 100644 --- a/scripts/test-package-interop.mts +++ b/scripts/test-package-interop.mts @@ -1,5 +1,6 @@ import assert from 'node:assert/strict'; import { execFile } from 'node:child_process'; +import { readFileSync } from 'node:fs'; import { createRequire } from 'node:module'; import { fileURLToPath } from 'node:url'; import { promisify } from 'node:util'; @@ -23,6 +24,7 @@ const build = { const collect = hooks => hook => hooks.push(hook); const noop = () => undefined; +const invoke = hook => (typeof hook === 'function' ? hook() : hook.handler?.()); const loadEntryPoints = async () => { const esm = await import('../dist/index.js'); @@ -32,7 +34,9 @@ const loadEntryPoints = async () => { const verifyRegistration = async (writer, reader) => { const starts = []; - const closes = []; + const closeBuilds = []; + const closeDevServers = []; + const exits = []; const api = { context: { action: 'dev', rootPath: process.cwd() }, logger: { info: noop, warn: noop, error: noop }, @@ -40,10 +44,14 @@ const verifyRegistration = async (writer, reader) => { modifyRsbuildConfig: noop, modifyEnvironmentConfig: noop, onBeforeBuild: noop, + onBeforeCreateCompiler: noop, + modifyBundlerChain: noop, + isPluginExists: () => false, onBeforeStartDevServer: collect(starts), onAfterStartDevServer: noop, - onCloseDevServer: collect(closes), - onCloseBuild: noop, + onCloseDevServer: collect(closeDevServers), + onCloseBuild: collect(closeBuilds), + onExit: collect(exits), onAfterEnvironmentCompile: noop, onAfterBuild: noop, processAssets: noop, @@ -55,9 +63,12 @@ const verifyRegistration = async (writer, reader) => { await writer.pluginReactRouter({ customServer: true }).setup(api); const startHook = starts.find(hook => hook.order === 'pre'); - const closeHook = closes.find(hook => hook.order === 'pre'); + const closeHook = closeDevServers.find(hook => hook.order === 'pre'); assert(startHook, 'Expected a pre dev-server start hook'); assert(closeHook, 'Expected a pre dev-server close hook'); + assert.equal(closeBuilds.length, 1); + assert.deepEqual(closeBuilds, exits); + assert.deepEqual(closeDevServers, [closeBuilds[0], closeHook]); const start = startHook.handler; const server = { close: async () => undefined, @@ -67,13 +78,7 @@ const verifyRegistration = async (writer, reader) => { await start({ environments: {}, server }); const pending = reader.loadReactRouterServerBuild(server); - for (const close of closes) { - if (typeof close === 'function') { - await close(); - } else { - await close.handler?.(); - } - } + await Promise.all([...closeBuilds, ...closeDevServers, ...exits].map(invoke)); await assert.rejects(pending, /closed before a React Router build was ready/); await assert.rejects( reader.loadReactRouterServerBuild(server), @@ -102,9 +107,75 @@ const verifyPackIncludesOriginalSource = async () => { ); }; +const verifyEffectFreeRuntimeOutputs = () => { + const dist = new URL('../dist/', import.meta.url); + const pending = [ + 'parallel-route-transform-worker.js', + 'templates/entry.client.js', + 'templates/entry.client.cjs', + 'templates/entry.rsc.client.js', + ].map(file => new URL(file, dist)); + const visited = new Set(); + + while (pending.length > 0) { + const file = pending.pop(); + if (!file || visited.has(file.href)) continue; + visited.add(file.href); + const source = readFileSync(file, 'utf8'); + assert.doesNotMatch(source, /["']effect(?:\/|["'])/); + for (const [, specifier] of source.matchAll( + /(?:\bfrom\s*|\b(?:import|require)\s*\(?\s*)["'](\.{1,2}\/[^"']+)["']/g + )) { + const dependency = new URL(specifier, file); + assert(dependency.href.startsWith(dist.href)); + pending.push(dependency); + } + } +}; + +const verifyRscPublicSurface = (esm, commonjs) => { + assert.equal(typeof esm.pluginReactRouterRSC, 'function'); + assert.equal(typeof commonjs.pluginReactRouterRSC, 'function'); + assert.match( + readFileSync(new URL('../dist/index.js', import.meta.url), 'utf8'), + /rsc-route-transform-loader\.js/ + ); + assert.match( + readFileSync(new URL('../dist/index.cjs', import.meta.url), 'utf8'), + /rsc-route-transform-loader\.cjs/ + ); + + for (const specifier of [ + 'rsbuild-plugin-react-router/templates/entry.rsc', + 'rsbuild-plugin-react-router/templates/entry.rsc.client', + 'rsbuild-plugin-react-router/templates/entry.rsc.ssr', + ]) { + const resolved = import.meta.resolve(specifier); + assert.match( + resolved, + /\/dist\/templates\/entry\.rsc(?:\.client|\.ssr)?\.js$/ + ); + assert.throws( + () => require.resolve(specifier), + error => { + const code = + error && typeof error === 'object' && 'code' in error + ? error.code + : undefined; + return ( + code === 'ERR_PACKAGE_PATH_NOT_EXPORTED' || + code === 'ERR_PACKAGE_IMPORT_NOT_DEFINED' + ); + } + ); + } +}; + const main = async () => { const [esm, commonjs] = await loadEntryPoints(); await verifyPackIncludesOriginalSource(); + verifyEffectFreeRuntimeOutputs(); + verifyRscPublicSurface(esm, commonjs); process.chdir( fileURLToPath(new URL('../tests/fixtures/dev-runtime/', import.meta.url)) ); diff --git a/src/build-output-transforms.ts b/src/build-output-transforms.ts index 9b14d0ec..918918ae 100644 --- a/src/build-output-transforms.ts +++ b/src/build-output-transforms.ts @@ -3,19 +3,75 @@ import jsesc from 'jsesc'; import { relative } from 'pathe'; import { PLUGIN_NAME } from './constants.js'; import { + createReactRouterManifestOptions, getReactRouterManifestForDev, + type ReactRouterManifestForDev as ReactRouterManifest, + type RouteChunkManifestOptions, + type RouteModuleAnalysisProvider, type ReactRouterManifestStats, } from './manifest.js'; import type { RouteTransformExecutor } from './parallel-route-transforms.js'; import type { ReactRouterPerformanceProfiler } from './performance.js'; +import { validateSpaModeRouteExports } from './route-transform-tasks.js'; import { createBundlerRouteExportResolver } from './route-export-resolution.js'; -import type { RouteChunkConfig } from './route-chunks.js'; +import { + getRouteChunkNameFromModuleId, + type RouteChunkConfig, +} from './route-chunks.js'; import type { PluginOptions, Route } from './types.js'; import { isSourceMapEnabled } from './warnings/warn-on-client-source-maps.js'; +import { + analyzeRouteModuleCode, + type RouteModuleAnalysis, +} from './export-utils.js'; +import { + relocateServerAssetsToClient, + type RelocatableAssetCompilation, +} from './ssr-asset-relocation.js'; -type ReactRouterManifest = Awaited< - ReturnType ->; +/** + * Register the node-compilation hook that relocates server-only static assets + * (`?url` imports, `.css?url` files, and other `asset/resource` outputs + * referenced only by loaders or `.server` modules) into the client build. The + * loader/`links()` export returns the asset URL to the client, which fetches it + * from `build/client` at runtime, so the file must exist there even though only + * the node compilation referenced it. The assets are also stripped from the + * server build to avoid shipping duplicate static files, mirroring upstream + * React Router's Vite plugin. This runs for every node compilation, so it also + * covers `serverBundles` (multiple node outputs) and dev mode (where + * `writeToDisk` is enabled). + * + * Registered by both the classic build-output transforms and the RSC branch so + * `.css?url`/`?url` assets referenced from `links()` resolve in RSC framework + * mode too. + */ +export const registerSsrAssetRelocation = ({ + api, + outputClientPath, + performanceProfiler, +}: { + api: RsbuildPluginAPI; + outputClientPath: string; + performanceProfiler: ReactRouterPerformanceProfiler; +}): void => { + const relocatedDestinations = new Map(); + api.processAssets( + { stage: 'report', targets: ['node'] }, + async ({ compilation }) => { + await performanceProfiler.record( + 'node', + 'assets:relocate-ssr-only', + 'ssr-only-assets', + () => + relocateServerAssetsToClient({ + compilation: compilation as unknown as RelocatableAssetCompilation, + outputClientPath, + relocatedDestinations, + }) + ); + } + ); +}; type RegisterBuildOutputTransformsOptions = { api: RsbuildPluginAPI; @@ -30,7 +86,8 @@ type RegisterBuildOutputTransformsOptions = { getClientStats: () => ReactRouterManifestStats | undefined; appDirectory: string; getAssetPrefix: () => string; - routeChunkOptions: Parameters[5]; + routeChunkOptions: RouteChunkManifestOptions | undefined; + routeModuleAnalysis?: RouteModuleAnalysisProvider; routeTransformExecutor: RouteTransformExecutor; routeByFilePath: Map; routeChunkConfig: RouteChunkConfig; @@ -39,7 +96,12 @@ type RegisterBuildOutputTransformsOptions = { ssr: boolean; isSpaMode: boolean; rootRoutePath: string; + outputClientPath: string; isDevHmrEnabled?: () => boolean; + onRouteModuleAnalysis?: ( + resourcePath: string, + analysis: RouteModuleAnalysis + ) => void; }; export const registerBuildOutputTransforms = ({ @@ -54,6 +116,7 @@ export const registerBuildOutputTransforms = ({ appDirectory, getAssetPrefix, routeChunkOptions, + routeModuleAnalysis, routeTransformExecutor, routeByFilePath, routeChunkConfig, @@ -62,10 +125,26 @@ export const registerBuildOutputTransforms = ({ ssr, isSpaMode, rootRoutePath, + outputClientPath, isDevHmrEnabled = () => false, + onRouteModuleAnalysis, }: RegisterBuildOutputTransformsOptions): void => { - const transformRouteModule = async (args: Parameters[0]) => - performanceProfiler.record( + const rememberRouteModuleAnalysis = ( + args: Parameters[0] + ): void => { + if (!routeByFilePath.has(args.resourcePath)) { + return; + } + onRouteModuleAnalysis?.( + args.resourcePath, + analyzeRouteModuleCode(args.code) + ); + }; + + const transformRouteModule = async ( + args: Parameters[0] + ) => { + return performanceProfiler.record( args.environment?.name, 'route:module', args.resource, @@ -86,6 +165,7 @@ export const registerBuildOutputTransforms = ({ devHmr: isDevHmrEnabled(), }) ); + }; api.processAssets( { stage: 'additional', targets: ['node'] }, @@ -103,6 +183,8 @@ export const registerBuildOutputTransforms = ({ } ); + registerSsrAssetRelocation({ api, outputClientPath, performanceProfiler }); + api.transform( { test: /virtual\/react-router\/(browser|server)-manifest/, @@ -135,7 +217,10 @@ export const registerBuildOutputTransforms = ({ getClientStats(), appDirectory, getAssetPrefix(), - routeChunkOptions + createReactRouterManifestOptions({ + routeChunks: routeChunkOptions, + routeModuleAnalysis, + }) )); return { code: `export default ${jsesc(manifest, { es6: true })};`, @@ -147,9 +232,11 @@ export const registerBuildOutputTransforms = ({ api.transform( { resourceQuery: /__react-router-build-client-route/, + order: 'post', }, - async args => - performanceProfiler.record( + async args => { + rememberRouteModuleAnalysis(args); + return performanceProfiler.record( args.environment?.name, 'route:client-entry', args.resource, @@ -164,29 +251,67 @@ export const registerBuildOutputTransforms = ({ routeId: routeByFilePath.get(args.resourcePath)?.id, devHmr: isDevHmrEnabled(), }) - ) + ); + } ); api.transform( { resourceQuery: /route-chunk=/, environments: ['web'], + order: 'post', }, - async args => - performanceProfiler.record( + async args => { + return performanceProfiler.record( args.environment?.name, 'route:chunk', args.resource, - async () => - routeTransformExecutor.run({ + async () => { + const routeChunkName = getRouteChunkNameFromModuleId(args.resource); + if (isBuild && isSpaMode && routeChunkName === 'main') { + validateSpaModeRouteExports({ + exportNames: analyzeRouteModuleCode(args.code, args.resourcePath) + .exports, + resourcePath: args.resourcePath, + rootRoutePath, + }); + } + + const routeChunkArtifact = await routeTransformExecutor.run({ kind: 'routeChunk', code: args.code, resource: args.resource, resourcePath: args.resourcePath, isBuild, routeChunkConfig, - }) - ) + }); + + // Invariant with the transformRouteModule registration below: in + // split-chunk production builds, web route modules are transformed + // HERE (on the main chunk) and the shared registration is scoped to + // ['node']. If either gate changes, web modules get transformed + // twice or not at all. + if (!isBuild || routeChunkName !== 'main') { + return routeChunkArtifact; + } + + return routeTransformExecutor.run({ + kind: 'routeModule', + code: routeChunkArtifact.code, + resource: args.resource, + resourcePath: args.resourcePath, + environmentName: 'web', + sourceMaps: isSourceMapEnabled( + args.environment.config.output.sourceMap + ), + ssr, + isBuild, + isSpaMode, + rootRoutePath, + }); + } + ); + } ); if (isBuild && splitRouteModules) { @@ -197,9 +322,10 @@ export const registerBuildOutputTransforms = ({ not: /__react-router-build-client-route|react-router-route|route-chunk=/, }, environments: ['web'], + order: 'post', }, - async args => - performanceProfiler.record( + async args => { + return performanceProfiler.record( args.environment?.name, 'route:split-exports', args.resource, @@ -210,7 +336,8 @@ export const registerBuildOutputTransforms = ({ resourcePath: args.resourcePath, routeChunkConfig, }) - ) + ); + } ); } @@ -271,6 +398,11 @@ export const registerBuildOutputTransforms = ({ resourceQuery: { not: /__react-router-build-client-route|react-router-route|route-chunk=/, }, + // Invariant with the route-chunk= handler above: when split-chunk + // production builds transform web modules on the main chunk, this + // registration must stay scoped to ['node'] so web modules are not + // transformed twice. + environments: isBuild && splitRouteModules ? ['node'] : undefined, order: 'post', }, transformRouteModule diff --git a/src/classic-mode.ts b/src/classic-mode.ts new file mode 100644 index 00000000..47ed8509 --- /dev/null +++ b/src/classic-mode.ts @@ -0,0 +1,253 @@ +import { readFileSync } from 'node:fs'; +import type { RsbuildEntryDescription, RsbuildPluginAPI } from '@rsbuild/core'; +import type { RouteConfigEntry } from '@react-router/dev/routes'; +import { resolve } from 'pathe'; +import { + getBuildManifest, + getRoutesByServerBundleId, +} from './build-manifest.js'; +import { BUILD_CLIENT_ROUTE_QUERY_STRING } from './constants.js'; +import { + DEV_HMR_RUNTIME_MODULE_ID, + type DevHmrPlanOptions, +} from './dev-hmr.js'; +import { + createReactRouterDevRuntimeController, + type ReactRouterDevRuntimeController, +} from './dev-runtime-controller.js'; +import { generateWithProps } from './plugin-utils.js'; +import { resolvePrerenderPaths } from './prerender.js'; +import { generateServerBuild } from './server-utils.js'; +import { + createReactRouterServerBuildPlan, + type ReactRouterServerBundleEntry, +} from './server-build-plan.js'; +import { + getRouteChunkEntryName, + getRouteChunkModuleId, + routeChunkExportNames, +} from './route-chunks.js'; +import type { Config } from './react-router-config.js'; +import type { Route } from './types.js'; + +type CreateClassicWebRouteEntriesOptions = { + appDirectory: string; + isBuild: boolean; + routes: Record; + splitRouteModules: boolean; +}; + +type CreateClassicVirtualModulesOptions = { + allowedActionOrigins: string[] | undefined; + appDirectory: string; + assetsBuildDirectory: string; + basename: string; + devHmrRuntimeModule?: string; + entryServerPath: string; + future: Config['future']; + isSpaMode: boolean; + prerenderPaths: string[]; + publicPath: string; + routeDiscovery: Config['routeDiscovery']; + routes: Record; + routesByServerBundleId: Record>; + ssr: boolean; +}; + +type CreateClassicBuildArtifactsOptions = { + api: RsbuildPluginAPI; + defaultEntryName: string; + isBuild: boolean; + prerenderConfig: Config['prerender']; + reactRouterConfig: Required< + Pick< + Config, + 'appDirectory' | 'buildDirectory' | 'serverBuildFile' | 'future' + > + > & + Pick; + routeConfig: RouteConfigEntry[]; + routes: Record; + rootDirectory: string; + ssr: boolean; + devHmr?: DevHmrPlanOptions; +}; + +export type ClassicBuildArtifacts = { + buildManifest: Awaited>; + devRuntime: ReactRouterDevRuntimeController; + prerenderPaths: string[]; + routesByServerBundleId: Record>; + serverBundleEntries: ReactRouterServerBundleEntry[]; +}; + +export const createClassicBuildArtifacts = async ({ + api, + defaultEntryName, + isBuild, + prerenderConfig, + reactRouterConfig, + routeConfig, + routes, + rootDirectory, + ssr, + devHmr, +}: CreateClassicBuildArtifactsOptions): Promise => { + const buildManifest = await getBuildManifest({ + reactRouterConfig, + routes, + rootDirectory, + }); + const routesByServerBundleId = getRoutesByServerBundleId( + buildManifest, + routes + ); + const serverBuildPlan = createReactRouterServerBuildPlan({ + routesByServerBundleId, + serverBuildFile: reactRouterConfig.serverBuildFile, + defaultEntryName, + }); + const prerenderPaths = await resolvePrerenderPaths( + prerenderConfig, + ssr, + routeConfig, + { + logWarning: true, + warn: message => api.logger.warn(message), + } + ); + + return { + buildManifest, + devRuntime: createReactRouterDevRuntimeController({ + api, + isBuild, + buildPlan: serverBuildPlan, + clientPatchesRouteMetadata: devHmr?.isEnabled, + onNodeRebuildCommitted: devHmr?.onNodeRebuildCommitted, + }), + prerenderPaths, + routesByServerBundleId, + serverBundleEntries: serverBuildPlan.serverBundleEntries, + }; +}; + +export const createClassicWebRouteEntries = ({ + appDirectory, + isBuild, + routes, + splitRouteModules, +}: CreateClassicWebRouteEntriesOptions): { + manifestChunkNames: Set; + webRouteEntries: Record; +} => { + const manifestChunkNames = new Set(['entry.client']); + const webRouteEntries = Object.values(routes).reduce( + (acc, route) => { + const entryName = route.file.slice(0, route.file.lastIndexOf('.')); + const routeFilePath = resolve(appDirectory, route.file); + manifestChunkNames.add(entryName); + acc[entryName] = { + import: `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`, + html: false, + }; + + if (isBuild && splitRouteModules && route.id !== 'root') { + let source: string; + try { + source = readFileSync(routeFilePath, 'utf8'); + } catch (error) { + if ((error as NodeJS.ErrnoException).code === 'ENOENT') { + return acc; + } + throw error; + } + for (const exportName of routeChunkExportNames) { + if (!source.includes(exportName)) { + continue; + } + const chunkEntryName = getRouteChunkEntryName(route.id, exportName); + manifestChunkNames.add(chunkEntryName); + acc[chunkEntryName] = { + import: getRouteChunkModuleId(routeFilePath, exportName), + html: false, + }; + } + } + + return acc; + }, + {} as Record + ); + + return { + manifestChunkNames, + webRouteEntries, + }; +}; + +export const createClassicVirtualModules = ({ + allowedActionOrigins, + appDirectory, + assetsBuildDirectory, + basename, + devHmrRuntimeModule, + entryServerPath, + future, + isSpaMode, + prerenderPaths, + publicPath, + routeDiscovery, + routes, + routesByServerBundleId, + ssr, +}: CreateClassicVirtualModulesOptions): Record => { + const serverBuildOptions = { + entryServerPath, + assetsBuildDirectory, + basename, + appDirectory, + ssr, + isSpaMode, + future, + allowedActionOrigins, + prerender: prerenderPaths, + routeDiscovery, + publicPath, + }; + const bundleVirtualModules = Object.fromEntries( + Object.entries(routesByServerBundleId).map(([bundleId, bundleRoutes]) => [ + `virtual/react-router/server-build-${bundleId}`, + generateServerBuild(bundleRoutes, { + ...serverBuildOptions, + serverManifestId: `virtual/react-router/server-manifest-${bundleId}`, + }), + ]) + ); + const bundleManifestModules = Object.fromEntries( + Object.entries(routesByServerBundleId) + .filter( + ([, bundleRoutes]) => + bundleRoutes && Object.keys(bundleRoutes).length > 0 + ) + .map(([bundleId]) => [ + `virtual/react-router/server-manifest-${bundleId}`, + 'export default {};', + ]) + ); + + return { + 'virtual/react-router/browser-manifest': 'export default {};', + 'virtual/react-router/server-manifest': 'export default {};', + 'virtual/react-router/server-build': generateServerBuild( + routes, + serverBuildOptions + ), + ...bundleVirtualModules, + ...bundleManifestModules, + 'virtual/react-router/with-props': generateWithProps(), + ...(devHmrRuntimeModule !== undefined + ? { [DEV_HMR_RUNTIME_MODULE_ID]: devHmrRuntimeModule } + : {}), + }; +}; diff --git a/src/config-imports.ts b/src/config-imports.ts index a8163b75..012db80e 100644 --- a/src/config-imports.ts +++ b/src/config-imports.ts @@ -1,14 +1,162 @@ -import type { ModuleCache } from 'jiti'; +import type { JitiOptions, ModuleCache, TransformOptions } from 'jiti'; import { createJiti } from 'jiti'; import { resolve } from 'pathe'; +import { parse } from './yuku.js'; -type ConfigImporter = Pick, 'import'>; +export type ConfigImporter = Pick, 'import'>; + +type ConfigImportOptions = { + define?: Record; + moduleCache?: boolean; +}; const normalizePath = (filePath: string): string => resolve(filePath); const isNodeModulePath = (filePath: string): boolean => filePath.split(/[\\/]/).includes('node_modules'); +type AstNode = { + type?: string; + start?: number; + end?: number; + object?: AstNode; + property?: AstNode; + meta?: AstNode; + name?: string; + value?: unknown; + computed?: boolean; +}; + +const normalizeDefineValue = (value: unknown): string | undefined => { + if (value === undefined) { + return undefined; + } + if (typeof value === 'string') { + return value; + } + return JSON.stringify(value); +}; + +const getMemberExpressionName = (node: AstNode | undefined): string | null => { + if (!node) { + return null; + } + if (node.type === 'Identifier') { + return node.name ?? null; + } + if (node.type === 'MetaProperty') { + const meta = getMemberExpressionName(node.meta); + const property = getMemberExpressionName(node.property); + return meta && property ? `${meta}.${property}` : null; + } + if (node.type !== 'MemberExpression') { + return null; + } + const object = getMemberExpressionName(node.object); + const property = + node.computed && typeof node.property?.value === 'string' + ? node.property.value + : getMemberExpressionName(node.property); + return object && property ? `${object}.${property}` : null; +}; + +const collectDefineReplacements = ( + node: unknown, + define: ReadonlyMap, + replacements: Array<{ start: number; end: number; value: string }> +): void => { + if (!node || typeof node !== 'object') { + return; + } + + const astNode = node as AstNode; + const memberName = getMemberExpressionName(astNode); + const value = memberName ? define.get(memberName) : undefined; + if ( + value !== undefined && + typeof astNode.start === 'number' && + typeof astNode.end === 'number' + ) { + replacements.push({ + start: astNode.start, + end: astNode.end, + value, + }); + return; + } + + for (const child of Object.values(node)) { + if (Array.isArray(child)) { + for (const item of child) { + collectDefineReplacements(item, define, replacements); + } + continue; + } + collectDefineReplacements(child, define, replacements); + } +}; + +export const applyConfigDefines = ( + source: string, + define: Record | undefined +): string => { + if (!define) { + return source; + } + + const normalizedDefine = new Map( + Object.entries(define).flatMap(([key, value]) => { + const normalized = normalizeDefineValue(value); + return normalized === undefined ? [] : [[key, normalized]]; + }) + ); + if (normalizedDefine.size === 0) { + return source; + } + + const replacements: Array<{ start: number; end: number; value: string }> = []; + collectDefineReplacements( + parse(source, { sourceType: 'module' }), + normalizedDefine, + replacements + ); + + return replacements + .sort((left, right) => right.start - left.start) + .reduce( + (contents, replacement) => + `${contents.slice(0, replacement.start)}${replacement.value}${contents.slice( + replacement.end + )}`, + source + ); +}; + +export const createConfigImporter = ({ + define, + moduleCache = true, +}: ConfigImportOptions = {}): ReturnType => { + const defaultJiti = createJiti(process.cwd(), { moduleCache }); + if (!define || Object.keys(define).length === 0) { + return defaultJiti; + } + + const defaultTransform = defaultJiti.transform; + const options = { + moduleCache, + transform(transformOptions: TransformOptions) { + return { + code: defaultTransform({ + ...transformOptions, + source: applyConfigDefines(transformOptions.source, define), + }), + }; + }, + } satisfies JitiOptions; + + return createJiti(process.cwd(), options); +}; + export const collectConfigImportWatchPaths = ( configPath: string, moduleCache: ModuleCache, @@ -50,11 +198,10 @@ export const clearConfigImportCache = ( export const importConfigWithWatchPaths = async ( configPath: string, load: (importer: ConfigImporter) => PromiseLike | T = async importer => - importer.import(configPath, { default: true }) + importer.import(configPath, { default: true }), + options?: ConfigImportOptions ): Promise<{ value: Awaited; watchPaths: string | string[] }> => { - const jiti = createJiti(process.cwd(), { - moduleCache: true, - }); + const jiti = createConfigImporter({ moduleCache: true, ...options }); const previousCacheKeys = new Set(Object.keys(jiti.cache)); let importPaths: string[] = []; diff --git a/src/constants.ts b/src/constants.ts index 7af426db..bba5b143 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,8 @@ export const PLUGIN_NAME = 'rsbuild:react-router'; +/** Default web `output.distPath.js` segment when the user has not customized it. */ +export const DEFAULT_JS_DIST_PATH = 'static/js'; + export const JS_EXTENSIONS = [ '.tsx', '.ts', @@ -32,8 +35,6 @@ export const SERVER_ONLY_ROUTE_EXPORTS_SET: ReadonlySet = new Set( SERVER_ONLY_ROUTE_EXPORTS ); -// Client route exports are split into non-component exports and component exports. -// This mirrors upstream React Router Vite plugin intent and is used for export filtering. export const CLIENT_NON_COMPONENT_EXPORTS = [ 'clientAction', 'clientLoader', @@ -89,3 +90,6 @@ export const CLIENT_EXPORTS = { meta: 'meta', shouldRevalidate: 'shouldRevalidate', } as const; + +// SPA-mode prerender fallback document, served when no prerendered page matches. +export const SPA_FALLBACK_HTML_FILE = '__spa-fallback.html'; diff --git a/src/dev-background-resources.ts b/src/dev-background-resources.ts index 64a979a3..eaae083f 100644 --- a/src/dev-background-resources.ts +++ b/src/dev-background-resources.ts @@ -7,11 +7,10 @@ import { createDelayedPluginTask, DEV_BACKGROUND_STARTUP_DELAY_MS, normalizeEffectError, - runPluginEffect, - tryPluginPromise, + type PluginEffectRuntime, } from './effect-runtime.js'; import { - createLazyCompilationPrewarmController, + acquireLazyCompilationPrewarm, normalizeLazyCompilationPrewarmOptions, } from './lazy-compilation-prewarm.js'; import { @@ -20,8 +19,8 @@ import { } from './manifest.js'; import type { RouteTransformExecutor } from './parallel-route-transforms.js'; import { + acquireRouteTopologyWatcher, createRouteManifestSnapshot, - createRouteTopologyWatcher, ensureDevRestartMarker, type WatchFileConfig, } from './route-watch.js'; @@ -29,9 +28,10 @@ import type { PluginOptions } from './types.js'; type RegisterReactRouterDevBackgroundResourcesOptions = { api: RsbuildPluginAPI; + runtime: PluginEffectRuntime; isBuild: boolean; lazyCompilationPrewarm: PluginOptions['unstableLazyCompilationPrewarm']; - routeTransformExecutor: RouteTransformExecutor; + routeTransformExecutor?: RouteTransformExecutor; routeRestartMarkerPath: string; watchDirectory: string; getRouteTopology: () => Promise>; @@ -100,7 +100,7 @@ export const createReactRouterRouteWatchFiles = ({ watchFiles.push( { paths: routeConfigWatchPaths, - type: 'reload-server', + type: 'reload-page', }, { paths: routeRestartMarkerPath, @@ -112,27 +112,9 @@ export const createReactRouterRouteWatchFiles = ({ return watchFiles; }; -const closeAll = async ( - message: string, - closers: Array<() => Promise> -): Promise => { - const results = await Promise.allSettled(closers.map(closer => closer())); - const errors = results - .filter( - (result): result is PromiseRejectedResult => result.status === 'rejected' - ) - .map(result => normalizeEffectError(result.reason)); - - if (errors.length === 1) { - throw errors[0]; - } - if (errors.length > 1) { - throw new AggregateError(errors, message); - } -}; - -export const registerReactRouterDevBackgroundResources = ({ +export const registerReactRouterDevBackgroundResources = async ({ api, + runtime, isBuild, lazyCompilationPrewarm, routeTransformExecutor, @@ -141,9 +123,8 @@ export const registerReactRouterDevBackgroundResources = ({ getRouteTopology, initialRouteTopology, onRouteTopologyChange, -}: RegisterReactRouterDevBackgroundResourcesOptions): ReactRouterDevBackgroundResources => { - let closeActiveRouteTopologyWatcher: (() => Promise) | undefined; - let routeTopologyWatcherClosed = false; +}: RegisterReactRouterDevBackgroundResourcesOptions): Promise => { + let routeTopologyWatcherStarted = false; const reportRouteTopologyWatcherError = (error: unknown): void => { api.logger.warn( @@ -152,54 +133,49 @@ export const registerReactRouterDevBackgroundResources = ({ }; const routeTopologyWatcherTask = createDelayedPluginTask({ + runtime, delayMs: DEV_BACKGROUND_STARTUP_DELAY_MS, run: () => - Effect.gen(function* () { - yield* tryPluginPromise(() => - ensureDevRestartMarker(routeRestartMarkerPath) - ); - const closeWatcher = yield* tryPluginPromise(() => - createRouteTopologyWatcher({ - watchDirectory, - getRouteTopology, - initialRouteTopology, - restartMarkerPath: routeRestartMarkerPath, - onRouteTopologyChange, - onError: reportRouteTopologyWatcherError, - }) - ); - if (routeTopologyWatcherClosed) { - yield* tryPluginPromise(() => closeWatcher()); - return; - } - closeActiveRouteTopologyWatcher = closeWatcher; - }), + acquireRouteTopologyWatcher({ + runtime, + watchDirectory, + getRouteTopology, + initialRouteTopology, + restartMarkerPath: routeRestartMarkerPath, + onRouteTopologyChange, + onError: reportRouteTopologyWatcherError, + }).pipe( + Effect.tap(() => + Effect.sync(() => (routeTopologyWatcherStarted = true)) + ), + Effect.asVoid + ), onError: reportRouteTopologyWatcherError, }); - const scheduleRouteTopologyWatcher = (): void => { - if (routeTopologyWatcherClosed || closeActiveRouteTopologyWatcher) { - return; - } - routeTopologyWatcherTask.schedule(); - }; - const lazyCompilationPrewarmConfig = normalizeLazyCompilationPrewarmOptions( lazyCompilationPrewarm ); const lazyCompilationPrewarmController = lazyCompilationPrewarmConfig - ? createLazyCompilationPrewarmController({ - config: lazyCompilationPrewarmConfig, - onError: error => - api.logger.warn( - `[${PLUGIN_NAME}] Lazy compilation prewarm skipped: ${error.message}` - ), - }) + ? await runtime.runPromise( + acquireLazyCompilationPrewarm({ + runtime, + config: lazyCompilationPrewarmConfig, + onError: error => + api.logger.warn( + `[${PLUGIN_NAME}] Lazy compilation prewarm skipped: ${error.message}` + ), + }) + ) : null; if (!isBuild) { - api.onBeforeStartDevServer(() => { - routeTopologyWatcherClosed = false; + await runtime.runPromise( + Effect.acquireRelease(Effect.void, routeTopologyWatcherTask.cancelEffect) + ); + + api.onBeforeStartDevServer(async () => { + await ensureDevRestartMarker(routeRestartMarkerPath); }); api.onAfterStartDevServer(({ port }) => { @@ -210,43 +186,17 @@ export const registerReactRouterDevBackgroundResources = ({ }); api.onAfterDevCompile(() => { - scheduleRouteTopologyWatcher(); + if (!routeTopologyWatcherStarted) { + routeTopologyWatcherTask.schedule(); + } lazyCompilationPrewarmController?.schedule(); }); // Spawn transform workers now so thread startup overlaps Rsbuild's own // compiler creation instead of delaying the first route transform. - routeTransformExecutor.prewarm(); + routeTransformExecutor?.prewarm(); } - const closeRouteTopologyWatcher = async (): Promise => { - routeTopologyWatcherClosed = true; - await runPluginEffect(routeTopologyWatcherTask.cancelEffect()); - await closeActiveRouteTopologyWatcher?.(); - closeActiveRouteTopologyWatcher = undefined; - }; - - const closeLazyCompilationPrewarm = async (): Promise => { - await runPluginEffect( - lazyCompilationPrewarmController?.cancelEffect() ?? Effect.void - ); - }; - - const closeRouteTransformExecutor = (): Promise => - routeTransformExecutor.close(); - - api.onCloseDevServer(() => - closeAll( - '[rsbuild-plugin-react-router] Failed to close dev server resources.', - [ - closeRouteTopologyWatcher, - closeLazyCompilationPrewarm, - closeRouteTransformExecutor, - ] - ) - ); - api.onCloseBuild(closeRouteTransformExecutor); - return { setManifest(manifest) { lazyCompilationPrewarmController?.setManifest(manifest); diff --git a/src/dev-generation.ts b/src/dev-generation.ts index d62cabe6..9862f9fd 100644 --- a/src/dev-generation.ts +++ b/src/dev-generation.ts @@ -1,7 +1,9 @@ import type { RsbuildDevServer, Rspack } from '@rsbuild/core'; import * as EffectDeferred from 'effect/Deferred'; import * as Effect from 'effect/Effect'; +import * as FiberId from 'effect/FiberId'; import type { ServerBuild } from 'react-router'; +import { HMR_PATCHABLE_ROUTE_FLAGS } from './route-artifacts.js'; import { evaluateServerBuilds, getEnvironmentStats, @@ -20,14 +22,10 @@ import { } from './dev-runtime-artifacts.js'; import { normalizeEffectError, runPluginEffect } from './effect-runtime.js'; -export { snapshotDevChangedFiles } from './dev-runtime-artifacts.js'; export type { - DevChangedFiles, DevGraphChanges, DevGraphIdentity, - ReactRouterDevBuildPlan, ReactRouterDevManifest, - ReactRouterDevManifestSet, } from './dev-runtime-artifacts.js'; type CommittedGeneration = { @@ -73,7 +71,9 @@ type CreateReactRouterDevRuntimeOptions = { buildPlan: ReactRouterDevBuildPlan; onEvaluationError: (error: Error) => void; onCssAssetOwnershipChanged?: (change: 'removed' | 'restored') => void; - onRouteManifestChanged?: () => void; + onRouteManifestChanged?: ( + manifest: ReactRouterDevManifestSet[string] + ) => void; onWarning?: (message: string) => void; }; @@ -199,28 +199,35 @@ type DevRouteManifestEntry = NonNullable< const hasSameRouteMetadata = ( previous: DevRouteManifestEntry, - next: DevRouteManifestEntry -): boolean => - previous.caseSensitive === next.caseSensitive && - previous.clientActionModule === next.clientActionModule && - previous.clientLoaderModule === next.clientLoaderModule && - previous.clientMiddlewareModule === next.clientMiddlewareModule && - previous.hasErrorBoundary === next.hasErrorBoundary && - previous.hasAction === next.hasAction && - previous.hasClientAction === next.hasClientAction && - previous.hasClientLoader === next.hasClientLoader && - previous.hasClientMiddleware === next.hasClientMiddleware && - previous.hasDefaultExport === next.hasDefaultExport && - previous.hasLoader === next.hasLoader && - previous.hydrateFallbackModule === next.hydrateFallbackModule && - previous.id === next.id && - previous.index === next.index && - previous.parentId === next.parentId && - previous.path === next.path; + next: DevRouteManifestEntry, + includeHmrPatchableFlags: boolean +): boolean => { + const structuralEqual = + previous.caseSensitive === next.caseSensitive && + previous.clientActionModule === next.clientActionModule && + previous.clientLoaderModule === next.clientLoaderModule && + previous.clientMiddlewareModule === next.clientMiddlewareModule && + previous.hasDefaultExport === next.hasDefaultExport && + previous.hydrateFallbackModule === next.hydrateFallbackModule && + previous.id === next.id && + previous.index === next.index && + previous.parentId === next.parentId && + previous.path === next.path; + if (!structuralEqual) { + return false; + } + if (!includeHmrPatchableFlags) { + return true; + } + // Without a client-side HMR runtime these flags can only reach the browser + // through a full reload. + return HMR_PATCHABLE_ROUTE_FLAGS.every(flag => previous[flag] === next[flag]); +}; const hasRouteManifestMetadataChanges = ( previous: ReactRouterDevManifestSet, - next: ReactRouterDevManifestSet + next: ReactRouterDevManifestSet, + includeHmrPatchableFlags: boolean ): boolean => { const previousEntryNames = Object.keys(previous); if (previousEntryNames.length !== Object.keys(next).length) { @@ -240,7 +247,14 @@ const hasRouteManifestMetadataChanges = ( for (const routeId of previousRouteIds) { const previousRoute = previousRoutes[routeId]; const nextRoute = nextRoutes[routeId]; - if (!nextRoute || !hasSameRouteMetadata(previousRoute, nextRoute)) { + if ( + !nextRoute || + !hasSameRouteMetadata( + previousRoute, + nextRoute, + includeHmrPatchableFlags + ) + ) { return true; } } @@ -251,7 +265,7 @@ const hasRouteManifestMetadataChanges = ( const createReadinessDeferred = (): EffectDeferred.Deferred< CommittedGeneration, Error -> => Effect.runSync(EffectDeferred.make()); +> => EffectDeferred.unsafeMake(FiberId.none); export const createReactRouterDevRuntime = ({ server, @@ -285,9 +299,11 @@ export const createReactRouterDevRuntime = ({ } }; - const notifyRouteManifestChanged = (): void => { + const notifyRouteManifestChanged = ( + manifest: ReactRouterDevManifestSet[string] + ): void => { try { - onRouteManifestChanged(); + onRouteManifestChanged(manifest); } catch (cause) { onWarning( `[rsbuild-plugin-react-router] Failed to notify the browser after route manifest metadata changed: ${normalizeEffectError(cause).message}` @@ -340,7 +356,7 @@ export const createReactRouterDevRuntime = ({ if (state.kind === 'starting') { const { readiness } = state; state = { kind: 'failed', attemptId, error }; - Effect.runSync(EffectDeferred.fail(readiness, error)); + EffectDeferred.unsafeDone(readiness, Effect.fail(error)); } else if (state.kind === 'ready') { state = { ...state, pendingAttemptId: null }; } @@ -359,7 +375,7 @@ export const createReactRouterDevRuntime = ({ if (state.kind === 'starting') { const { readiness } = state; state = { kind: 'ready', committed, pendingAttemptId: null }; - Effect.runSync(EffectDeferred.succeed(readiness, committed)); + EffectDeferred.unsafeDone(readiness, Effect.succeed(committed)); } else if (state.kind === 'ready') { state = { kind: 'ready', committed, pendingAttemptId: null }; } @@ -513,7 +529,8 @@ export const createReactRouterDevRuntime = ({ webChanged && hasRouteManifestMetadataChanges( previous.web.manifestsByEntryName, - manifestsByEntryName + manifestsByEntryName, + true ); const reusePreviousNodeBuild = !!previous && @@ -587,7 +604,9 @@ export const createReactRouterDevRuntime = ({ reloadAfterCssRemoval = false; } if (routeManifestMetadataChanged) { - notifyRouteManifestChanged(); + notifyRouteManifestChanged( + web.manifestsByEntryName[buildPlan.defaultEntryName] + ); } return 'committed'; } catch (cause) { @@ -636,7 +655,7 @@ export const createReactRouterDevRuntime = ({ '[rsbuild-plugin-react-router] The development server closed before a React Router build was ready.' ); if (state.kind === 'starting') { - Effect.runSync(EffectDeferred.fail(state.readiness, closeError)); + EffectDeferred.unsafeDone(state.readiness, Effect.fail(closeError)); } state = { kind: 'closed', error: closeError }; }, diff --git a/src/dev-hmr.ts b/src/dev-hmr.ts index af8f5c5b..7131d424 100644 --- a/src/dev-hmr.ts +++ b/src/dev-hmr.ts @@ -3,9 +3,14 @@ import { createRequire } from 'node:module'; import type { Rspack } from '@rsbuild/core'; import { dirname, join } from 'pathe'; -import { HMR_PATCHABLE_ROUTE_FLAGS } from './route-artifacts.js'; - export const DEV_HMR_RUNTIME_MODULE_ID = 'virtual/react-router/hmr-runtime'; +export const DEV_MANIFEST_UPDATE_EVENT = 'react-router:manifest-update'; + +export type DevHmrPlanOptions = { + isEnabled: () => boolean; + runtimeModule: string; + onNodeRebuildCommitted: () => void; +}; type SwcLoaderOptions = { jsc?: { transform?: { react?: { refresh?: boolean } } }; @@ -17,30 +22,21 @@ const isObject = (value: unknown): value is Record => const isSwcLoader = (loader: unknown): boolean => typeof loader === 'string' && loader.includes('builtin:swc-loader'); -const hasReactRefresh = (options: unknown): boolean => - (options as SwcLoaderOptions | undefined)?.jsc?.transform?.react?.refresh === - true; - const readSwcLoaderRefresh = (value: unknown): boolean => { - if (Array.isArray(value)) { - return value.some(readSwcLoaderRefresh); - } - if (!isObject(value)) { - return false; - } + if (Array.isArray(value)) return value.some(readSwcLoaderRefresh); + if (!isObject(value)) return false; if (isSwcLoader(value.loader)) { - return hasReactRefresh(value.options); + return ( + (value.options as SwcLoaderOptions | undefined)?.jsc?.transform?.react + ?.refresh === true + ); } return Object.values(value).some(readSwcLoaderRefresh); }; const readRuleSwcRefresh = (rule: unknown): boolean => { - if (!isObject(rule)) { - return false; - } - if (isSwcLoader(rule.loader)) { - return hasReactRefresh(rule.options); - } + if (!isObject(rule)) return false; + if (isSwcLoader(rule.loader)) return readSwcLoaderRefresh(rule); return ( readSwcLoaderRefresh(rule.use) || readRuleSetSwcRefresh(rule.oneOf) || @@ -94,12 +90,11 @@ const hdrRevisionModuleContent = (revision: number): string => * whenever server code changes, which the client answers by revalidating * React Router loader data. */ -export const DEV_HDR_REVISION_RELATIVE_PATH = '.react-router/hdr-revision.mjs'; - export const getDevHdrRevisionFilePath = (rootPath: string): string => - join(rootPath, DEV_HDR_REVISION_RELATIVE_PATH); + join(rootPath, '.react-router', 'hdr-revision.mjs'); export type DevHdrRevisionSignal = { + filePath: string; /** Writes the initial revision module so the first compile can resolve it. */ ensure: () => void; /** Increments the revision, signaling hot data revalidation to the client. */ @@ -127,6 +122,7 @@ export const createDevHdrRevisionSignal = ({ } }; return { + filePath, ensure: write, bump() { revision += 1; @@ -138,7 +134,7 @@ export const createDevHdrRevisionSignal = ({ /** * Browser-side HMR runtime shared by all route client entries in development. * - * This mirrors React Router's Vite HMR contract (see `refresh-utils.mjs` in + * This mirrors React Router framework HMR contract (see `refresh-utils.mjs` in * `@react-router/dev`): route module updates are applied by patching * `window.__reactRouterRouteModules` while preserving the previous component * identities (React Fast Refresh swaps their implementations in place), @@ -156,7 +152,11 @@ import * as __refreshRuntimeModule from ${JSON.stringify(reactRefreshRuntimePath // Read revision so the import survives sideEffects: false tree-shaking. import __hdrRevision from ${JSON.stringify(hdrRevisionFilePath)}; -void __hdrRevision; +let latestHdrRevision = __hdrRevision; + +export function getReactRouterHdrRevision() { + return latestHdrRevision; +} const RefreshRuntime = __refreshRuntimeModule && __refreshRuntimeModule.performReactRefresh @@ -166,6 +166,7 @@ const RefreshRuntime = const pendingRouteUpdates = new Map(); let flushTimeout; let pendingRevalidation = false; +let pendingComponentRouteRevalidation = false; function getCurrentRouterPath(router) { const basename = router.basename || '/'; @@ -199,10 +200,10 @@ export function registerReactRouterRouteExports(routeId, moduleExports) { export function scheduleReactRouterRouteUpdate( routeId, - routeFlags, + routeMetadata, getRouteModuleExports ) { - pendingRouteUpdates.set(routeId, { routeFlags, getRouteModuleExports }); + pendingRouteUpdates.set(routeId, { routeMetadata, getRouteModuleExports }); scheduleFlush(); } @@ -228,16 +229,8 @@ function takePendingRouteUpdates() { return updates; } -function getRouteMetadata(routeFlags) { - return { -${HMR_PATCHABLE_ROUTE_FLAGS.map( - (flag, index) => ` ${flag}: Boolean(routeFlags & ${1 << index}),` -).join('\n')} - }; -} - function applyRouteModuleUpdate(routeId, update, routeEntry, routeModules) { - Object.assign(routeEntry, getRouteMetadata(update.routeFlags)); + Object.assign(routeEntry, update.routeMetadata); const imported = update.getRouteModuleExports(); registerReactRouterRouteExports(routeId, imported); const current = routeModules[routeId]; @@ -294,11 +287,7 @@ function patchCurrentRouteMatches(router, routes) { function applyPendingRouteUpdates(router, routeModules, manifest, context) { if (pendingRouteUpdates.size === 0) { - return { - nextManifest: undefined, - shouldRefreshRouteState: false, - routesToRevalidate: new Set(), - }; + return { nextManifest: undefined, hmrRoutes: undefined }; } // Clone only entries mutated before the manifest is committed in flush(). @@ -309,8 +298,7 @@ function applyPendingRouteUpdates(router, routeModules, manifest, context) { const existingEntry = nextManifest.routes[routeId]; if (!existingEntry) continue; - // Shallow clone is enough: only top-level flags are mutated below. - const routeEntry = { ...existingEntry }; + const routeEntry = JSON.parse(JSON.stringify(existingEntry)); nextManifest.routes[routeId] = routeEntry; applyRouteModuleUpdate(routeId, update, routeEntry, routeModules); if ( @@ -332,54 +320,52 @@ function applyPendingRouteUpdates(router, routeModules, manifest, context) { } } + let hmrRoutes; if ( typeof router.createRoutesForHMR === 'function' && typeof router._internalSetRoutes === 'function' ) { - const routes = router.createRoutesForHMR( + hmrRoutes = router.createRoutesForHMR( routesToRevalidate, nextManifest.routes, routeModules, context.ssr, context.isSpaMode ); - router._internalSetRoutes(routes); - patchCurrentRouteMatches(router, routes); + router._internalSetRoutes(hmrRoutes); } - return { nextManifest, shouldRefreshRouteState, routesToRevalidate }; + return { nextManifest, hmrRoutes, shouldRefreshRouteState }; } -async function withHdrActive(fn) { +async function revalidateRouter(router) { try { window.__reactRouterHdrActive = true; - await fn(); + if (typeof router.revalidate === 'function') { + await router.revalidate(); + return; + } + if (typeof router.navigate === 'function') { + await router.navigate(getCurrentRouterPath(router), { + replace: true, + preventScrollReset: true, + }); + } } finally { window.__reactRouterHdrActive = false; } } -async function revalidateRouter(router) { - if (typeof router.revalidate === 'function') { - await withHdrActive(() => router.revalidate()); +async function refreshRouteState(router) { + if (typeof router.revalidate !== 'function') { return; } - if (typeof router.navigate === 'function') { - await withHdrActive(() => - router.navigate(getCurrentRouterPath(router), { - replace: true, - preventScrollReset: true, - }) - ); - } -} - -async function refreshRouteState(router) { - if (typeof router.revalidate === 'function') { - await withHdrActive(() => router.revalidate()); - return true; + try { + window.__reactRouterHdrActive = true; + await router.revalidate(); + } finally { + window.__reactRouterHdrActive = false; } - return false; } function performReactRefresh() { @@ -391,6 +377,34 @@ function performReactRefresh() { } } +function applyManifestUpdate(nextRoutes) { + const router = window.__reactRouterDataRouter; + const routeModules = window.__reactRouterRouteModules; + const manifest = window.__reactRouterManifest; + const context = window.__reactRouterContext; + if ( + !router || + !routeModules || + !manifest || + !context || + !nextRoutes || + typeof router.createRoutesForHMR !== 'function' || + typeof router._internalSetRoutes !== 'function' + ) { + return; + } + const routes = router.createRoutesForHMR( + new Set(Object.keys(nextRoutes)), + nextRoutes, + routeModules, + context.ssr, + context.isSpaMode + ); + manifest.routes = nextRoutes; + router._internalSetRoutes(routes); + patchCurrentRouteMatches(router, routes); +} + async function flush() { const router = window.__reactRouterDataRouter; const routeModules = window.__reactRouterRouteModules; @@ -402,30 +416,48 @@ async function flush() { let shouldRevalidate = pendingRevalidation; pendingRevalidation = false; - const { nextManifest, shouldRefreshRouteState, routesToRevalidate } = + const { nextManifest, hmrRoutes, shouldRefreshRouteState } = applyPendingRouteUpdates(router, routeModules, manifest, context); - if (nextManifest) { + // Loader updates must be visible during revalidation. Component-only routes + // stay staged until revalidation completes, matching React Router's HMR flow. + if (nextManifest && shouldRefreshRouteState) { + pendingComponentRouteRevalidation = false; + if (hmrRoutes) { + patchCurrentRouteMatches(router, hmrRoutes); + } Object.assign(manifest, nextManifest); - } - // Component-only updates do not need a full loader revalidation. - if ( - shouldRefreshRouteState && - (routesToRevalidate.size > 0 || shouldRevalidate) - ) { - if (await refreshRouteState(router)) { - shouldRevalidate = false; + await refreshRouteState(router); + shouldRevalidate = false; + } else if (nextManifest) { + if (hmrRoutes) { + patchCurrentRouteMatches(router, hmrRoutes); + } + Object.assign(manifest, nextManifest); + // The node compiler also emits an HDR revision for this route edit. If it + // did not arrive in this flush, consume that redundant revalidation later. + pendingComponentRouteRevalidation = !shouldRevalidate; + shouldRevalidate = false; + } else if (shouldRevalidate) { + if (pendingComponentRouteRevalidation) { + pendingComponentRouteRevalidation = false; + } else { + await revalidateRouter(router); } - } - if (shouldRevalidate) { - await revalidateRouter(router); } performReactRefresh(); } if (typeof window !== 'undefined' && import.meta.webpackHot) { + import.meta.webpackHot.on( + ${JSON.stringify(DEV_MANIFEST_UPDATE_EVENT)}, + applyManifestUpdate + ); import.meta.webpackHot.accept( ${JSON.stringify(hdrRevisionFilePath)}, - scheduleReactRouterRevalidation + () => { + latestHdrRevision = __hdrRevision; + scheduleReactRouterRevalidation(); + } ); } `; diff --git a/src/dev-runtime-artifacts.ts b/src/dev-runtime-artifacts.ts index c82a3548..20be0f4a 100644 --- a/src/dev-runtime-artifacts.ts +++ b/src/dev-runtime-artifacts.ts @@ -5,7 +5,7 @@ import type { ServerBuild } from 'react-router'; import type { ReactRouterManifestForDev } from './manifest.js'; import { getCappedPluginConcurrency } from './concurrency.js'; import { runPluginEffect, tryPluginPromise } from './effect-runtime.js'; -import { resolveServerBuildModuleEffect } from './server-build-resolution.js'; +import { resolveServerBuildModule } from './server-build-resolution.js'; export type ReactRouterDevManifest = ReactRouterManifestForDev; @@ -137,14 +137,17 @@ export const getEnvironmentStats = ( const startServerBuildEvaluationEffect = ( server: RsbuildDevServer, entryName: string -): Effect.Effect => +): Effect.Effect => tryPluginPromise(() => server.environments.node.loadBundle(entryName)).pipe( Effect.flatMap(buildModule => - resolveServerBuildModuleEffect( - buildModule, - `Server entry ${JSON.stringify(entryName)}` + tryPluginPromise(() => + resolveServerBuildModule( + buildModule, + `Server entry ${JSON.stringify(entryName)}` + ) ) - ) + ), + Effect.map(build => [entryName, build] as const) ); const evaluateServerBuildsEffect = ( @@ -152,12 +155,8 @@ const evaluateServerBuildsEffect = ( entryNames: readonly string[] ): Effect.Effect => Effect.forEach( - entryNames.map(entryName => - startServerBuildEvaluationEffect(server, entryName).pipe( - Effect.map(build => [entryName, build] as const) - ) - ), - evaluation => evaluation, + entryNames, + entryName => startServerBuildEvaluationEffect(server, entryName), { concurrency: getCappedPluginConcurrency() } ).pipe( Effect.map( diff --git a/src/dev-runtime-controller.ts b/src/dev-runtime-controller.ts index 9202aa26..30f5cee2 100644 --- a/src/dev-runtime-controller.ts +++ b/src/dev-runtime-controller.ts @@ -1,7 +1,7 @@ import type { RsbuildConfig, RsbuildPluginAPI, Rspack } from '@rsbuild/core'; -import * as Effect from 'effect/Effect'; import type { ServerBuild } from 'react-router'; import { PLUGIN_NAME } from './constants.js'; +import { escapeHtml } from './plugin-utils.js'; import { beginDevCompilerAttempt, clearDevCompilerStart, @@ -19,6 +19,7 @@ import { registerReactRouterDevRuntime, unregisterReactRouterDevRuntime, } from './dev-generation.js'; +import { DEV_MANIFEST_UPDATE_EVENT } from './dev-hmr.js'; import { getEnvironmentStats, snapshotDevChangedFiles, @@ -27,17 +28,11 @@ import { type ReactRouterDevBuildPlan, type ReactRouterDevManifestSet, } from './dev-runtime-artifacts.js'; -import { DEV_HDR_REVISION_RELATIVE_PATH } from './dev-hmr.js'; import { createDevRuntimeSessionManager, type RuntimeBinding, } from './dev-runtime-session.js'; -import { - normalizeEffectError, - runPluginEffect, - tryPluginPromise, - tryPluginSync, -} from './effect-runtime.js'; +import { normalizeEffectError } from './effect-runtime.js'; type ServerSetup = Exclude< NonNullable['setup']>, @@ -56,6 +51,11 @@ type CreateControllerOptions = { api: RsbuildPluginAPI; isBuild: boolean; buildPlan: ReactRouterDevBuildPlan; + /** + * The browser HMR runtime patches route manifest metadata (loader/action + * flags) in place, so metadata-only changes no longer need a full reload. + */ + clientPatchesRouteMetadata?: boolean | (() => boolean); /** * Invoked after a development attempt commits a re-evaluated node build for * changed server files. Used to signal hot data revalidation to the client. @@ -63,36 +63,19 @@ type CreateControllerOptions = { onNodeRebuildCommitted?: () => void; }; -const escapeHtml = (value: string): string => - value - .replaceAll('&', '&') - .replaceAll('<', '<') - .replaceAll('>', '>'); - const CSS_SOURCE_RELOAD_DELAY_MS = 1000; const isHdrRevisionFile = (file: string): boolean => - file.includes(DEV_HDR_REVISION_RELATIVE_PATH); + file.includes('.react-router/hdr-revision.mjs'); const isCssSourceFile = (file: string): boolean => /\.css(?:\.[cm]?[jt]s)?$/.test(file); -// A change that should bump the HDR revision: anything except the revision -// file itself (the bump's own echo) and CSS sources (styling never changes -// loader data). -const hasHdrTriggeringChange = (files: Iterable): boolean => { - for (const file of files) { - if (!isHdrRevisionFile(file) && !isCssSourceFile(file)) { - return true; - } - } - return false; -}; - export const createReactRouterDevRuntimeController = ({ api, isBuild, buildPlan, + clientPatchesRouteMetadata, onNodeRebuildCommitted, }: CreateControllerOptions): ReactRouterDevRuntimeController => { if (isBuild) { @@ -165,56 +148,44 @@ export const createReactRouterDevRuntimeController = ({ NonNullable >(); - const finishRuntimeAttemptEffect = ( + const finishRuntimeAttempt = async ( binding: RuntimeBinding, pair: DevCompilerPair, stats: DevRuntimeStats, changes: Parameters[1], identity: Parameters[2] - ): Effect.Effect => - tryPluginPromise(() => - binding.runtime.finishAttempt(stats, changes, identity) - ).pipe( - Effect.flatMap(result => - tryPluginSync(() => { - if (sessions.getActiveBinding()?.id !== binding.id) { - return; - } - if (result === 'retry-node') { - pair.node.watching?.invalidate(); - return; - } - if ( - result === 'committed' && - changes.node.known && - identity.node !== undefined && - hdrSignaledNodeIdentity.get(pair) !== identity.node && - hasHdrTriggeringChange(changes.node.files) - ) { - hdrSignaledNodeIdentity.set(pair, identity.node); - onNodeRebuildCommitted?.(); - } - }) - ), - Effect.catchAll(cause => - tryPluginSync(() => { - if (sessions.getActiveBinding()?.id === binding.id) { - binding.runtime.failAttempt(normalizeEffectError(cause)); - } - }) - ) - ); - - const finishRuntimeAttempt = ( - binding: RuntimeBinding, - pair: DevCompilerPair, - stats: DevRuntimeStats, - changes: Parameters[1], - identity: Parameters[2] - ): Promise => - runPluginEffect( - finishRuntimeAttemptEffect(binding, pair, stats, changes, identity) - ); + ): Promise => { + try { + const result = await binding.runtime.finishAttempt( + stats, + changes, + identity + ); + if (sessions.getActiveBinding()?.id !== binding.id) { + return; + } + if (result === 'retry-node') { + pair.node.watching?.invalidate(); + return; + } + if ( + result === 'committed' && + changes.node.known && + identity.node !== undefined && + hdrSignaledNodeIdentity.get(pair) !== identity.node && + Array.from(changes.node.files).some( + file => !isHdrRevisionFile(file) && !isCssSourceFile(file) + ) + ) { + hdrSignaledNodeIdentity.set(pair, identity.node); + onNodeRebuildCommitted?.(); + } + } catch (cause) { + if (sessions.getActiveBinding()?.id === binding.id) { + binding.runtime.failAttempt(normalizeEffectError(cause)); + } + } + }; const flushSettledAttempt = ( binding: RuntimeBinding, @@ -306,11 +277,22 @@ export const createReactRouterDevRuntimeController = ({ reloadAfterCssAssetOwnershipRemoval = change === 'removed'; sendCssAssetOwnershipReload(); }, - onRouteManifestChanged() { + onRouteManifestChanged(manifest) { if (sessions.getActiveBinding()?.runtime !== runtime) { return; } - server.sockWrite('full-reload', { path: '*' }); + const patchesRouteMetadata = + typeof clientPatchesRouteMetadata === 'function' + ? clientPatchesRouteMetadata() + : clientPatchesRouteMetadata; + if (patchesRouteMetadata) { + server.sockWrite('custom', { + event: DEV_MANIFEST_UPDATE_EVENT, + data: manifest.routes, + }); + } else { + server.sockWrite('full-reload', { path: '*' }); + } }, onWarning: message => api.logger.warn(message), }); @@ -566,21 +548,29 @@ export const createReactRouterDevRuntimeController = ({ }, createBuildLoader(entryName?: string): () => Promise { - const server = sessions.getActiveBinding()?.server; - if (server) { - return () => loadReactRouterServerBuild(server, entryName); - } - const state = sessions.getState(); - if (state.status === 'terminal') { - const { error } = state; - return () => Promise.reject(error); - } - return () => - Promise.reject( + // Pin the loader to the dev-server session active at creation time. Once a + // loader is handed to React Router for session N it must keep serving N (or + // fail loudly with 'not registered' once N closes) and never silently migrate + // to a replacement session — this preserves SSR generation/session coherency. + // The live fallback below applies ONLY when no session exists yet at creation + // (boundServer === undefined), i.e. a loader built during config setup before + // the dev server has started; there is no session to stay coherent with yet. + const boundServer = sessions.getActiveBinding()?.server; + return () => { + const server = boundServer ?? sessions.getActiveBinding()?.server; + if (server) { + return loadReactRouterServerBuild(server, entryName); + } + const state = sessions.getState(); + if (state.status === 'terminal') { + return Promise.reject(state.error); + } + return Promise.reject( new Error( `[${PLUGIN_NAME}] The development server runtime is not ready.` ) ); + }; }, }; }; diff --git a/src/dev-runtime-session.ts b/src/dev-runtime-session.ts index 54dd4607..e6b10f6f 100644 --- a/src/dev-runtime-session.ts +++ b/src/dev-runtime-session.ts @@ -1,13 +1,8 @@ import type { RsbuildDevServer } from '@rsbuild/core'; -import * as Effect from 'effect/Effect'; import { PLUGIN_NAME } from './constants.js'; import type { ReactRouterDevRuntime } from './dev-generation.js'; import type { DevCompilerPair } from './dev-runtime-compilation.js'; -import { - runPluginEffect, - tryPluginPromise, - tryPluginSync, -} from './effect-runtime.js'; +import { normalizeEffectError } from './effect-runtime.js'; export type RuntimeBinding = { id: number; @@ -115,18 +110,19 @@ export const createDevRuntimeSessionManager = ( if (observation.promise) { return observation.promise; } - observation.promise = runPluginEffect( - tryPluginPromise(close).pipe( - Effect.tap(() => - tryPluginSync(() => applyCloseOutcome(observation, { ok: true })) - ), - Effect.catchAll(cause => - tryPluginSync(() => - applyCloseOutcome(observation, { ok: false, cause }) - ).pipe(Effect.zipRight(Effect.fail(cause))) - ) - ) - ); + let closePromise: Promise; + try { + closePromise = Promise.resolve(close()); + } catch (cause) { + closePromise = Promise.reject(normalizeEffectError(cause)); + } + observation.promise = closePromise + .then(() => applyCloseOutcome(observation, { ok: true })) + .catch(cause => { + const error = normalizeEffectError(cause); + applyCloseOutcome(observation, { ok: false, cause: error }); + throw error; + }); return observation.promise; }; closeObservationByServer.set(server, observation); diff --git a/src/dev-server.ts b/src/dev-server.ts index 3b0e3281..fe96c8c2 100644 --- a/src/dev-server.ts +++ b/src/dev-server.ts @@ -1,6 +1,7 @@ import type { IncomingMessage, ServerResponse } from 'node:http'; import type { RsbuildConfig } from '@rsbuild/core'; import type { ServerBuild } from 'react-router'; +import { installDevServerSourceMapSupport } from './dev-source-maps.js'; export type ServerSetup = Exclude< NonNullable['setup']>, @@ -83,6 +84,7 @@ export const createReactRouterDevServerSetup = ({ // middlewares, so static assets are served before the React Router // request handler. return () => { + installDevServerSourceMapSupport(); context.server.middlewares.use(createDevServerMiddleware({ loadBuild })); }; }; diff --git a/src/dev-source-maps.ts b/src/dev-source-maps.ts new file mode 100644 index 00000000..00cdeb4a --- /dev/null +++ b/src/dev-source-maps.ts @@ -0,0 +1,257 @@ +import { existsSync, readFileSync, statSync } from 'node:fs'; +import { SourceMap, type SourceMapPayload } from 'node:module'; +import { dirname, isAbsolute, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import type { Rspack } from '@rsbuild/core'; + +type CachedSourceMap = { + cacheKey: string; + sourceMap: SourceMap | null; +}; + +type InMemorySourceMap = { + load: () => SourceMapPayload | null; + sourceMap?: SourceMap | null; +}; + +const SOURCE_MAPPING_URL_MARKER = '//# sourceMappingURL='; +const GENERATED_JS_FRAME_RE = + /(file:\/\/\/[^\s()]+\.m?js|\/[^\s()]+\.m?js):(\d+):(\d+)/g; + +let installed = false; +let previousPrepareStackTrace: typeof Error.prepareStackTrace; +const sourceMapCache = new Map(); +const inMemorySourceMaps = new Map(); +const generatedFilesByOutputPath = new Map>(); + +const getGeneratedFilePath = (fileName: string): string => { + if (fileName.startsWith('file://')) { + return fileURLToPath(fileName); + } + return fileName; +}; + +const getCacheKey = (filePath: string): string | null => { + try { + const generatedStats = statSync(filePath); + const mapPath = `${filePath}.map`; + const mapStats = existsSync(mapPath) ? statSync(mapPath) : undefined; + return [ + generatedStats.mtimeMs, + generatedStats.size, + mapStats?.mtimeMs ?? 0, + mapStats?.size ?? 0, + ].join(':'); + } catch { + return null; + } +}; + +const parseSourceMapPayload = (sourceMapJson: string): SourceMapPayload => { + const payload = JSON.parse(sourceMapJson) as Partial; + return { + file: payload.file ?? '', + version: payload.version ?? 3, + sources: payload.sources ?? [], + sourcesContent: payload.sourcesContent ?? [], + names: payload.names ?? [], + mappings: payload.mappings ?? '', + sourceRoot: payload.sourceRoot ?? '', + }; +}; + +const toSourceMapPayload = (value: unknown): SourceMapPayload | null => { + if (!value || typeof value !== 'object') { + return null; + } + const payload = value as Partial; + return { + file: payload.file ?? '', + version: payload.version ?? 3, + sources: payload.sources ?? [], + sourcesContent: payload.sourcesContent ?? [], + names: payload.names ?? [], + mappings: payload.mappings ?? '', + sourceRoot: payload.sourceRoot ?? '', + }; +}; + +const readInlineSourceMap = (filePath: string): string | null => { + const source = readFileSync(filePath, 'utf8'); + const markerIndex = source.lastIndexOf(SOURCE_MAPPING_URL_MARKER); + if (markerIndex === -1) { + return null; + } + + const sourceMapUrlStart = markerIndex + SOURCE_MAPPING_URL_MARKER.length; + const nextLineIndex = source.indexOf('\n', sourceMapUrlStart); + const sourceMapUrl = source + .slice(sourceMapUrlStart, nextLineIndex === -1 ? undefined : nextLineIndex) + .trim(); + if (!sourceMapUrl.startsWith('data:')) { + return null; + } + + const commaIndex = sourceMapUrl.indexOf(','); + if (commaIndex === -1) { + return null; + } + + const metadata = sourceMapUrl.slice(0, commaIndex); + const data = sourceMapUrl.slice(commaIndex + 1); + return metadata.endsWith(';base64') + ? Buffer.from(data, 'base64').toString('utf8') + : decodeURIComponent(data); +}; + +const readSourceMapPayload = (filePath: string): SourceMapPayload | null => { + const inlineSourceMap = readInlineSourceMap(filePath); + if (inlineSourceMap) { + return parseSourceMapPayload(inlineSourceMap); + } + + const externalSourceMapPath = `${filePath}.map`; + if (!existsSync(externalSourceMapPath)) { + return null; + } + + return parseSourceMapPayload(readFileSync(externalSourceMapPath, 'utf8')); +}; + +const getSourceMap = (filePath: string): SourceMap | null => { + const inMemory = inMemorySourceMaps.get(filePath); + if (inMemory) { + if (inMemory.sourceMap !== undefined) { + return inMemory.sourceMap; + } + try { + const payload = inMemory.load(); + inMemory.sourceMap = payload ? new SourceMap(payload) : null; + } catch { + inMemory.sourceMap = null; + } + return inMemory.sourceMap; + } + + const cacheKey = getCacheKey(filePath); + if (!cacheKey) { + return null; + } + + const cached = sourceMapCache.get(filePath); + if (cached?.cacheKey === cacheKey) { + return cached.sourceMap; + } + + let sourceMap: SourceMap | null = null; + try { + const payload = readSourceMapPayload(filePath); + sourceMap = payload ? new SourceMap(payload) : null; + } catch { + sourceMap = null; + } + + sourceMapCache.set(filePath, { cacheKey, sourceMap }); + return sourceMap; +}; + +export const registerDevServerSourceMaps = ( + compilation: Pick +): void => { + const outputPath = compilation.outputOptions.path; + if (!outputPath) { + return; + } + const previousFiles = generatedFilesByOutputPath.get(outputPath); + if (previousFiles) { + for (const filePath of previousFiles) { + inMemorySourceMaps.delete(filePath); + } + } + + const assets = compilation.getAssets(); + const assetsByName = new Map(assets.map(asset => [asset.name, asset])); + const generatedFiles = new Set(); + + for (const asset of assets) { + if (!/\.m?js$/.test(asset.name)) { + continue; + } + + const generatedFilePath = resolve(outputPath, asset.name); + const externalMap = assetsByName.get(`${asset.name}.map`); + const load = externalMap + ? () => + parseSourceMapPayload(externalMap.source.buffer().toString('utf8')) + : () => toSourceMapPayload(asset.source.map()); + + generatedFiles.add(generatedFilePath); + inMemorySourceMaps.set(generatedFilePath, { load }); + } + + generatedFilesByOutputPath.set(outputPath, generatedFiles); +}; + +const resolveOriginalFileName = ( + generatedFilePath: string, + originalFileName: string +): string => { + if ( + isAbsolute(originalFileName) || + originalFileName.startsWith('node:') || + originalFileName.includes('://') + ) { + return originalFileName; + } + return resolve(dirname(generatedFilePath), originalFileName); +}; + +export const remapDevServerStack = (stack: string): string => + stack.replace(GENERATED_JS_FRAME_RE, (match, fileName, line, column) => { + const generatedFilePath = getGeneratedFilePath(fileName); + const sourceMap = getSourceMap(generatedFilePath); + const origin = sourceMap?.findOrigin(Number(line), Number(column)); + if (!origin || !('fileName' in origin)) { + return match; + } + + return `${resolveOriginalFileName( + generatedFilePath, + origin.fileName + )}:${origin.lineNumber}:${origin.columnNumber}`; + }); + +const formatStackTrace = ( + error: Error, + callSites: NodeJS.CallSite[] +): string => { + const message = error.message + ? `${error.name}: ${error.message}` + : error.name; + const frames = callSites.map(callSite => ` at ${callSite.toString()}`); + return [message, ...frames].join('\n'); +}; + +const prepareStackTrace: NonNullable = ( + error, + callSites +) => { + const formattedStack = + previousPrepareStackTrace && previousPrepareStackTrace !== prepareStackTrace + ? previousPrepareStackTrace(error, callSites) + : formatStackTrace(error, callSites); + + return typeof formattedStack === 'string' + ? remapDevServerStack(formattedStack) + : formattedStack; +}; + +export const installDevServerSourceMapSupport = (): void => { + if (installed) { + return; + } + + installed = true; + previousPrepareStackTrace = Error.prepareStackTrace; + Error.prepareStackTrace = prepareStackTrace; +}; diff --git a/src/effect-runtime.ts b/src/effect-runtime.ts index cf9aca3e..573713ce 100644 --- a/src/effect-runtime.ts +++ b/src/effect-runtime.ts @@ -1,9 +1,80 @@ import * as Cause from 'effect/Cause'; +import * as Context from 'effect/Context'; import * as Duration from 'effect/Duration'; import * as Effect from 'effect/Effect'; +import * as ExecutionStrategy from 'effect/ExecutionStrategy'; import * as Exit from 'effect/Exit'; import * as Fiber from 'effect/Fiber'; +import * as FiberSet from 'effect/FiberSet'; +import * as Layer from 'effect/Layer'; +import * as ManagedRuntime from 'effect/ManagedRuntime'; import * as Option from 'effect/Option'; +import * as Scope from 'effect/Scope'; + +const PluginFibers = Context.GenericTag( + 'rsbuild-plugin-react-router/PluginFibers' +); +type PluginResources = { readonly scope: Scope.CloseableScope }; +const PluginResources: Context.Tag = + Context.GenericTag( + 'rsbuild-plugin-react-router/PluginResources' + ); + +type PluginRuntimeContext = Scope.Scope | FiberSet.FiberSet | PluginResources; + +const PluginRuntimeLive = Layer.scopedContext( + Effect.gen(function* () { + const scope = yield* Effect.scope; + const resources = yield* Scope.fork(scope, ExecutionStrategy.sequential); + const fiberScope = yield* Scope.fork(scope, ExecutionStrategy.sequential); + const fibers = yield* FiberSet.make().pipe( + Effect.provideService(Scope.Scope, fiberScope) + ); + return Context.make(Scope.Scope, resources).pipe( + Context.add(PluginFibers, fibers), + Context.add(PluginResources, { scope: resources }) + ); + }) +); + +export type PluginEffectRuntime = Pick< + ManagedRuntime.ManagedRuntime, + 'runFork' | 'runPromise' +> & { + readonly dispose: () => Promise; +}; + +export const createPluginEffectRuntime = (): PluginEffectRuntime => { + const runtime = ManagedRuntime.make(PluginRuntimeLive); + let fiberRunFork: typeof runtime.runFork | undefined; + + let disposePromise: Promise | undefined; + + return { + runPromise: runtime.runPromise, + runFork: (effect, options) => { + if (disposePromise) return Effect.runFork(Effect.interrupt); + return (fiberRunFork ??= runtime.runSync( + Effect.flatMap(PluginFibers, fibers => + FiberSet.runtime(fibers)() + ) + ))(effect, options); + }, + // Resource finalizers cancel their owned fibers before runtime disposal + // closes the fiber scope and interrupts any stragglers. Deferring by one + // microtask also lets a managed fiber request its own shutdown safely. + dispose: (): Promise => + (disposePromise ??= Promise.resolve() + .then(() => + runtime.runPromise( + Effect.flatMap(PluginResources, ({ scope }) => + Scope.close(scope, Exit.void) + ) + ) + ) + .finally(() => runtime.dispose())), + }; +}; export const DEV_BACKGROUND_STARTUP_DELAY_MS = 3_000; @@ -27,14 +98,6 @@ export const runPluginEffect = async ( throw normalizeEffectCause(exit.cause); }; -export const tryPluginSync = ( - evaluate: () => A -): Effect.Effect => - Effect.try({ - try: evaluate, - catch: normalizeEffectError, - }); - export const tryPluginPromise = ( evaluate: () => PromiseLike | A ): Effect.Effect => @@ -46,45 +109,38 @@ export const tryPluginPromise = ( type DelayedPluginTask = { schedule(): void; reschedule(): void; - cancelEffect(): Effect.Effect; - cancel(): Promise; + cancelEffect(): Effect.Effect; }; export const createDelayedPluginTask = ({ + runtime, delayMs, run, onError, }: { + runtime: PluginEffectRuntime; delayMs: number; - run: () => Effect.Effect; + run: () => Effect.Effect; onError: (error: Error) => void; }): DelayedPluginTask => { - let activeFiber: ReturnType | undefined; - let version = 0; - - const cancelActiveEffect = (): Effect.Effect => - Effect.sync(() => { - const fiber = activeFiber; - activeFiber = undefined; - return fiber; - }).pipe( - Effect.flatMap(fiber => - fiber ? Fiber.interrupt(fiber).pipe(Effect.asVoid) : Effect.void - ) - ); - - const cancelEffect = (): Effect.Effect => - Effect.sync(() => { - version += 1; - }).pipe(Effect.zipRight(cancelActiveEffect())); - - const start = (taskVersion: number): void => { - if (activeFiber || version !== taskVersion) { + let fiber: ReturnType | null | undefined; + + const cancelEffect = (): Effect.Effect => + Effect.suspend(() => { + const activeFiber = fiber; + fiber = null; + return activeFiber + ? Fiber.interrupt(activeFiber).pipe(Effect.asVoid) + : Effect.void; + }); + + const start = (): void => { + if (fiber !== undefined) { return; } - let fiber: ReturnType; - fiber = Effect.runFork( + let activeFiber: ReturnType; + activeFiber = runtime.runFork( Effect.sleep(Duration.millis(delayMs)).pipe( Effect.zipRight(Effect.suspend(run)), Effect.catchAll(error => @@ -94,37 +150,29 @@ export const createDelayedPluginTask = ({ ), Effect.ensuring( Effect.sync(() => { - if (activeFiber === fiber) { - activeFiber = undefined; + if (fiber === activeFiber) { + fiber = undefined; } }) ) ) ); - activeFiber = fiber; + fiber = activeFiber; }; - return { - schedule(): void { - if (activeFiber) { - return; - } - version += 1; - start(version); - }, - - reschedule(): void { - version += 1; - const taskVersion = version; - void runPluginEffect(cancelActiveEffect()) - .then(() => start(taskVersion)) - .catch(onError); - }, + const reschedule = (): void => { + if (fiber) { + runtime.runFork( + Fiber.interrupt(fiber).pipe(Effect.ensuring(Effect.sync(start))) + ); + } else { + start(); + } + }; + return { + schedule: start, + reschedule, cancelEffect, - - async cancel(): Promise { - await runPluginEffect(cancelEffect()); - }, }; }; diff --git a/src/entry-paths.ts b/src/entry-paths.ts new file mode 100644 index 00000000..b0744e2a --- /dev/null +++ b/src/entry-paths.ts @@ -0,0 +1,80 @@ +import { existsSync } from 'node:fs'; +import { resolve } from 'pathe'; +import { findEntryFile } from './plugin-utils.js'; + +type ReactRouterEntryPathsOptions = { + appDirectory: string; + templatesDirectory: string; +}; + +export type ReactRouterEntryPaths = { + devServerBuildEntryName: string; + finalEntryClientPath: string; + finalEntryRscClientPath: string; + finalEntryRscPath: string; + finalEntryRscSsrPath: string; + finalEntryServerPath: string; + hasServerApp: boolean; + serverAppPath: string; +}; + +const resolveEntryWithTemplate = ({ + appDirectory, + entryName, + templateName, + templatesDirectory, +}: ReactRouterEntryPathsOptions & { + entryName: string; + templateName: string; +}): string => { + const userEntryPath = findEntryFile(resolve(appDirectory, entryName)); + return existsSync(userEntryPath) + ? userEntryPath + : resolve(templatesDirectory, templateName); +}; + +export const resolveReactRouterEntryPaths = ({ + appDirectory, + templatesDirectory, +}: ReactRouterEntryPathsOptions): ReactRouterEntryPaths => { + const serverAppPath = findEntryFile(resolve(appDirectory, '../server/index')); + const hasServerApp = existsSync(serverAppPath); + + return { + devServerBuildEntryName: hasServerApp + ? 'static/js/react-router-server-build' + : 'static/js/app', + finalEntryClientPath: resolveEntryWithTemplate({ + appDirectory, + entryName: 'entry.client', + templateName: 'entry.client.js', + templatesDirectory, + }), + finalEntryServerPath: resolveEntryWithTemplate({ + appDirectory, + entryName: 'entry.server', + templateName: 'entry.server.js', + templatesDirectory, + }), + finalEntryRscClientPath: resolveEntryWithTemplate({ + appDirectory, + entryName: 'entry.rsc.client', + templateName: 'entry.rsc.client.js', + templatesDirectory, + }), + finalEntryRscPath: resolveEntryWithTemplate({ + appDirectory, + entryName: 'entry.rsc', + templateName: 'entry.rsc.js', + templatesDirectory, + }), + finalEntryRscSsrPath: resolveEntryWithTemplate({ + appDirectory, + entryName: 'entry.ssr', + templateName: 'entry.rsc.ssr.js', + templatesDirectory, + }), + hasServerApp, + serverAppPath, + }; +}; diff --git a/src/environment-output.ts b/src/environment-output.ts new file mode 100644 index 00000000..1a4464a3 --- /dev/null +++ b/src/environment-output.ts @@ -0,0 +1,90 @@ +import type { RsbuildPluginAPI, Rspack } from '@rsbuild/core'; + +// Module Federation reads asyncStartup when applying the plugin, after the +// plugin instance has already been constructed. +const ensureFederationAsyncStartup = ( + rspackConfig: Rspack.Configuration | undefined +): void => { + if (!rspackConfig?.plugins?.length) { + return; + } + + for (const plugin of rspackConfig.plugins) { + if (!plugin || typeof plugin !== 'object') { + continue; + } + if (!('name' in plugin) || plugin.name !== 'ModuleFederationPlugin') { + continue; + } + + const pluginOptions = + ('_options' in plugin ? plugin._options : undefined) ?? + ('options' in plugin ? plugin.options : undefined); + if (!pluginOptions || typeof pluginOptions !== 'object') { + continue; + } + + const experiments = + 'experiments' in pluginOptions && + pluginOptions.experiments && + typeof pluginOptions.experiments === 'object' + ? pluginOptions.experiments + : undefined; + Object.assign(pluginOptions, { + experiments: { ...experiments, asyncStartup: true }, + }); + } +}; + +export const registerReactRouterEnvironmentOutput = ({ + api, + federation, + resolvedServerOutput, +}: { + api: RsbuildPluginAPI; + federation: boolean | undefined; + resolvedServerOutput: 'commonjs' | 'module'; +}): void => { + api.modifyEnvironmentConfig( + async (config, { name, mergeEnvironmentConfig }) => { + if (name !== 'web' && name !== 'node') { + return config; + } + + return mergeEnvironmentConfig(config, { + tools: { + rspack: rspackConfig => { + if (federation) { + ensureFederationAsyncStartup(rspackConfig); + } + + if (name === 'node') { + const output = rspackConfig.output; + if (output) { + const library = output.library; + const libraryOptions = + library && + typeof library === 'object' && + !Array.isArray(library) + ? library + : {}; + rspackConfig.output = { + ...output, + library: { + ...libraryOptions, + type: + resolvedServerOutput === 'module' + ? 'module' + : 'commonjs2', + }, + }; + } + } + + return rspackConfig; + }, + }, + }); + } + ); +}; diff --git a/src/export-utils.ts b/src/export-utils.ts index 9d66e307..3c690ee2 100644 --- a/src/export-utils.ts +++ b/src/export-utils.ts @@ -15,7 +15,7 @@ type ExportInfo = { readonly exportAllModules: readonly string[]; }; -type RouteModuleAnalysis = { +export type RouteModuleAnalysis = { readonly code: string; readonly exports: readonly string[]; readonly exportAllModules: readonly string[]; @@ -236,6 +236,18 @@ export const getExportNamesAndExportAll = async ( return trackedExportInfo; }; +export const analyzeRouteModuleCode = ( + code: string, + resourcePath?: string +): RouteModuleAnalysis => { + const program = parseProgram(code, resourcePath); + return { + code, + exports: collectProgramExportNames(program), + exportAllModules: collectExportAllModules(program), + }; +}; + export const getRouteModuleAnalysis = async ( resourcePath: string ): Promise => { @@ -247,12 +259,7 @@ export const getRouteModuleAnalysis = async ( const analysis = (async () => { const source = await readFile(resourcePath, 'utf8'); - const program = parseProgram(source, resourcePath); - return { - code: source, - exports: collectProgramExportNames(program), - exportAllModules: collectExportAllModules(program), - }; + return analyzeRouteModuleCode(source, resourcePath); })(); let trackedAnalysis: Promise; diff --git a/src/index.ts b/src/index.ts index f7abb10b..a22fca80 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,102 +1,106 @@ -import { existsSync, readFileSync } from 'node:fs'; +import { existsSync } from 'node:fs'; import fsExtra from 'fs-extra'; import type { Config } from './react-router-config.js'; import type { RouteConfigEntry } from '@react-router/dev/routes'; -import { - rspack, - type RsbuildEntryDescription, - type RsbuildPlugin, - type Rspack, -} from '@rsbuild/core'; -import { createJiti } from 'jiti'; +import { rspack, type RsbuildPlugin, type Rspack } from '@rsbuild/core'; import { relative, resolve } from 'pathe'; import { getDefaultConcurrency } from './concurrency.js'; import { - BUILD_CLIENT_ROUTE_QUERY_STRING, + DEFAULT_JS_DIST_PATH, JS_EXTENSIONS, PLUGIN_NAME, } from './constants.js'; import { guardReactRouterLazyCompilation } from './lazy-compilation.js'; -import { createReactRouterDevServerSetup } from './dev-server.js'; import { - generateWithProps, findEntryFile, - normalizeAssetPrefix, + resolveAppPackagePath, + resolveEffectiveAssetPrefix, } from './plugin-utils.js'; -import type { PluginOptions } from './types.js'; -import { - generateServerBuild, - resolveReactRouterServerBuild, -} from './server-utils.js'; -import { resolvePrerenderPaths, validatePrerenderConfig } from './prerender.js'; +import { resolveReactRouterEntryPaths } from './entry-paths.js'; +import { registerReactRouterEnvironmentOutput } from './environment-output.js'; +import type { PluginOptions, ReactRouterRSCPluginOptions } from './types.js'; +import { resolveReactRouterServerBuild } from './server-utils.js'; +import { validatePrerenderConfig } from './prerender.js'; import { runReactRouterPrerenderBuild } from './prerender-build.js'; +import { runReactRouterRscPrerenderBuild } from './rsc-prerender.js'; import { - resolveReactRouterConfig, + resolveReactRouterConfigEffect, + resolveRouteDiscoveryConfig, type ResolvedReactRouterConfig, } from './react-router-config.js'; import { - getReactRouterManifestForDev, configRoutesToRouteManifest, createReactRouterManifestStats, + type ReactRouterManifestForDev as ReactRouterManifest, type ReactRouterManifestStats, type RouteManifestModuleExports, } from './manifest.js'; +import type { RouteModuleAnalysis } from './export-utils.js'; import { registerModifyBrowserManifestAssets } from './modify-browser-manifest.js'; -import { registerBuildOutputTransforms } from './build-output-transforms.js'; -import { - getRouteChunkEntryName, - getRouteChunkModuleId, - routeChunkExportNames, - type RouteChunkCache, - type RouteChunkConfig, -} from './route-chunks.js'; import { - createRouteTransformExecutor, - shouldParallelizeRouteTransforms, -} from './parallel-route-transforms.js'; + registerBuildOutputTransforms, + registerSsrAssetRelocation, +} from './build-output-transforms.js'; +import { type RouteChunkCache } from './route-chunks.js'; import { getRouteRestartMarkerPath, mergeWatchFiles } from './route-watch.js'; import { validateRouteConfig } from './route-config.js'; -import { - getBuildManifest, - getRoutesByServerBundleId, -} from './build-manifest.js'; -import { - createReactRouterNodeEntries, - createReactRouterServerBuildPlan, -} from './server-build-plan.js'; import { warnOnClientSourceMaps } from './warnings/warn-on-client-source-maps.js'; import { validatePluginOrderFromConfig } from './validation/validate-plugin-order.js'; -import { getSsrExternals } from './ssr-externals.js'; import { createReactRouterPerformanceProfiler, roundMs, } from './performance.js'; import { mapVirtualModules } from './virtual-modules.js'; -import { createReactRouterDevRuntimeController } from './dev-runtime-controller.js'; -import { runPluginEffect, tryPluginPromise } from './effect-runtime.js'; -import { registerReactRouterTypegen } from './typegen.js'; -import { importConfigWithWatchPaths } from './config-imports.js'; -import { - createReactRouterRouteTopology, - createReactRouterRouteWatchFiles, - registerReactRouterDevBackgroundResources, -} from './dev-background-resources.js'; import { createDevHdrRevisionSignal, generateDevHmrRuntimeModule, getDevHdrRevisionFilePath, isRspackSwcReactRefreshEnabled, resolveReactRefreshRuntimePath, - DEV_HMR_RUNTIME_MODULE_ID, } from './dev-hmr.js'; +import { + createPluginEffectRuntime, + tryPluginPromise, +} from './effect-runtime.js'; +import { registerReactRouterTypegen } from './typegen.js'; +import { + createConfigImporter, + type ConfigImporter, + importConfigWithWatchPaths, +} from './config-imports.js'; +import { + createReactRouterRouteTopology, + createReactRouterRouteWatchFiles, + registerReactRouterDevBackgroundResources, +} from './dev-background-resources.js'; +import { + assertReactRouterRscConfigSupport, + assertReactRouterRscSupport, + registerReactRouterRscRouteTransforms, + setupReactRouterRscPlugin, +} from './rsc-support.js'; +import { createReactRouterModePlan } from './mode-plan.js'; +import { + acquireRouteTransformExecutor, + shouldParallelizeRouteTransforms, +} from './parallel-route-transforms.js'; +import { createQuerylessRouteImportPlugin } from './route-imports.js'; +import { registerDevServerSourceMaps } from './dev-source-maps.js'; export type { Config as ReactRouterRsbuildConfig } from './react-router-config.js'; export { loadReactRouterServerBuild } from './dev-generation.js'; export { resolveReactRouterServerBuild }; +export type { PluginOptions, ReactRouterRSCPluginOptions } from './types.js'; const MIN_PARALLEL_ENVIRONMENT_BUILD_SPARE_CORES = 4; +type ReactRouterPresetResolvedConfig = Parameters< + NonNullable< + NonNullable[number]['reactRouterConfigResolved'] + > +>[0]['reactRouterConfig']; + export const shouldParallelizeEnvironmentBuilds = ({ isBuild, spareCoreCount = getDefaultConcurrency(), @@ -106,42 +110,6 @@ export const shouldParallelizeEnvironmentBuilds = ({ }): boolean => !isBuild && spareCoreCount >= MIN_PARALLEL_ENVIRONMENT_BUILD_SPARE_CORES; -type ModuleFederationPluginLike = { - name?: string; - _options?: { experiments?: { asyncStartup?: boolean } }; - options?: { experiments?: { asyncStartup?: boolean } }; -}; - -const ensureFederationAsyncStartup = ( - rspackConfig: Rspack.Configuration | undefined -): void => { - if (!rspackConfig?.plugins?.length) { - return; - } - - for (const plugin of rspackConfig.plugins) { - if (!plugin || typeof plugin !== 'object') { - continue; - } - const pluginName = (plugin as ModuleFederationPluginLike).name; - if (pluginName !== 'ModuleFederationPlugin') { - continue; - } - - const pluginOptions = - (plugin as ModuleFederationPluginLike)._options ?? - (plugin as ModuleFederationPluginLike).options; - if (!pluginOptions) { - continue; - } - - pluginOptions.experiments = { - ...pluginOptions.experiments, - asyncStartup: true, - }; - } -}; - const cssUrlAssetExtensions = /\.(?:css|less|sass|scss|styl|stylus|pcss|postcss|sss)$/; const urlAssetResourceQuery = @@ -153,9 +121,15 @@ export const pluginReactRouter = ( name: PLUGIN_NAME, async setup(api) { + const effectRuntime = createPluginEffectRuntime(); + api.onCloseBuild(effectRuntime.dispose); + api.onCloseDevServer(effectRuntime.dispose); + api.onExit(effectRuntime.dispose); + const defaultOptions = { customServer: false, lazyCompilation: true, + rsc: false, serverOutput: 'module' as const, }; @@ -163,16 +137,13 @@ export const pluginReactRouter = ( ...defaultOptions, ...options, }; + const isRscMode = Boolean(pluginOptions.rsc); const logPerformance = pluginOptions.logPerformance === true; const setupStartMs = logPerformance ? performance.now() : 0; const performanceProfiler = createReactRouterPerformanceProfiler({ enabled: logPerformance, log: message => api.logger.info(message), }); - const nodeExternals = Array.from( - new Set(['express', ...getSsrExternals(process.cwd())]) - ); - let assetPrefix = '/'; // Best-effort configuration validation (upstream: validate-plugin-order). @@ -188,7 +159,6 @@ export const pluginReactRouter = ( } api.logger.warn(issue.message); } - assetPrefix = normalizeAssetPrefix(config.output?.assetPrefix); return config; }, }); @@ -198,6 +168,15 @@ export const pluginReactRouter = ( warnOnClientSourceMaps(normalized, msg => api.logger.warn(msg), 'web'); }); + api.onBeforeCreateCompiler(() => { + const normalized = api.getNormalizedConfig(); + assetPrefix = resolveEffectiveAssetPrefix({ + dev: normalized.dev, + output: normalized.output, + isBuild: api.context.action === 'build', + }); + }); + const configPath = findEntryFile(resolve('react-router.config')); const configExists = existsSync(configPath); let configWatchPaths: string | string[] = configExists @@ -230,9 +209,12 @@ export const pluginReactRouter = ( const { resolved: resolvedConfig, + userAndPresetConfig, presets: configPresets, hasConfiguredServerModuleFormat, - } = await resolveReactRouterConfig(reactRouterUserConfig); + } = await effectRuntime.runPromise( + resolveReactRouterConfigEffect(reactRouterUserConfig) + ); const { appDirectory, @@ -244,12 +226,17 @@ export const pluginReactRouter = ( ssr, prerender: prerenderConfig, serverBuildFile, + serverBundles, serverModuleFormat, splitRouteModules, + subResourceIntegrity, buildEnd, } = resolvedConfig; - registerReactRouterTypegen(api, { appDirectory }); + await registerReactRouterTypegen(api, { + runtime: effectRuntime, + appDirectory, + }); const hasExplicitServerOutput = Object.prototype.hasOwnProperty.call( options, @@ -290,57 +277,57 @@ export const pluginReactRouter = ( throw new Error(prerenderConfigError); } - // React Router defaults to "lazy" route discovery, but "ssr:false" builds - // have no runtime server to serve manifest patch requests, so we force - // `mode:"initial"` in SPA mode to avoid any `/__manifest` fetches. - let routeDiscovery: Config['routeDiscovery']; - if (!userRouteDiscovery) { - routeDiscovery = ssr - ? ({ mode: 'lazy', manifestPath: '/__manifest' } as const) - : ({ mode: 'initial' } as const); - } else if (userRouteDiscovery.mode === 'initial') { - routeDiscovery = userRouteDiscovery; - } else if (userRouteDiscovery.mode === 'lazy') { - if (!ssr) { - throw new Error( - 'The `routeDiscovery.mode` config cannot be set to "lazy" when setting `ssr:false`' - ); - } - const manifestPath = userRouteDiscovery.manifestPath; - if (manifestPath && !manifestPath.startsWith('/')) { - throw new Error( - 'The `routeDiscovery.manifestPath` config must be a root-relative pathname beginning with a slash (i.e., "/__manifest")' - ); - } - routeDiscovery = userRouteDiscovery; - } + const routeDiscovery = resolveRouteDiscoveryConfig({ + ssr, + userRouteDiscovery, + }); (globalThis as any).__reactRouterAppDirectory = resolve(appDirectory); const routesPath = findEntryFile(resolve(appDirectory, 'routes')); if (!existsSync(routesPath)) { - throw new Error( - `Route config file not found at "${relative( - process.cwd(), - routesPath - )}".` + const missingRoutesPath = relative( + process.cwd(), + resolve(appDirectory, 'routes.ts') ); + throw new Error(`Route config file not found at "${missingRoutesPath}".`); } - const jiti = createJiti(process.cwd(), { + const routeConfigDefine = + typeof api.getRsbuildConfig === 'function' + ? api.getRsbuildConfig().source?.define + : undefined; + const jiti = createConfigImporter({ + define: routeConfigDefine, moduleCache: false, }); const importRouteConfig = async ( - importer: Pick + importer: ConfigImporter ): Promise => { - const routeConfigExport = await importer.import( - routesPath, - { - default: true, - } - ); - const routeConfigValue = await routeConfigExport; + const routeConfigFile = relative(resolve(appDirectory), routesPath); + let routeConfigValue: RouteConfigEntry[]; + try { + const routeConfigExport = await importer.import( + routesPath, + { + default: true, + } + ); + routeConfigValue = await routeConfigExport; + } catch (error) { + // Match upstream: import/evaluation failures (e.g. syntax errors) are + // reported as an invalid route config rather than a raw loader error. + throw new Error( + [ + `Route config in "${routeConfigFile}" is invalid.`, + '', + error instanceof Error + ? (error.stack ?? error.message) + : String(error), + ].join('\n') + ); + } const validation = validateRouteConfig({ - routeConfigFile: relative(process.cwd(), routesPath), + routeConfigFile, routeConfig: routeConfigValue, }); if (!validation.valid) { @@ -350,33 +337,41 @@ export const pluginReactRouter = ( }; const loadRouteConfig = () => importRouteConfig(jiti); const { value: routeConfig, watchPaths: routeConfigWatchPaths } = - await importConfigWithWatchPaths(routesPath, importRouteConfig); - - const entryClientPath = findEntryFile( - resolve(appDirectory, 'entry.client') - ); - const entryServerPath = findEntryFile( - resolve(appDirectory, 'entry.server') - ); - - const serverAppPath = findEntryFile( - resolve(appDirectory, '../server/index') - ); - const hasServerApp = existsSync(serverAppPath); - const devServerBuildEntryName = hasServerApp - ? 'static/js/react-router-server-build' - : 'static/js/app'; + await importConfigWithWatchPaths(routesPath, importRouteConfig, { + define: routeConfigDefine, + }); - const templateDir = resolve(__dirname, 'templates'); - const templateClientPath = resolve(templateDir, 'entry.client.js'); - const templateServerPath = resolve(templateDir, 'entry.server.js'); + const { + devServerBuildEntryName, + finalEntryClientPath, + finalEntryRscClientPath, + finalEntryRscPath, + finalEntryRscSsrPath, + finalEntryServerPath, + hasServerApp, + serverAppPath, + } = resolveReactRouterEntryPaths({ + appDirectory, + templatesDirectory: resolve(__dirname, 'templates'), + }); - const finalEntryClientPath = existsSync(entryClientPath) - ? entryClientPath - : templateClientPath; - const finalEntryServerPath = existsSync(entryServerPath) - ? entryServerPath - : templateServerPath; + if (isRscMode) { + assertReactRouterRscSupport({ + pluginName: PLUGIN_NAME, + resolvePackagePath: resolveAppPackagePath, + }); + assertReactRouterRscConfigSupport({ + pluginName: PLUGIN_NAME, + userConfig: resolvedConfig, + }); + await setupReactRouterRscPlugin({ + api, + entryRscPath: finalEntryRscPath, + entrySsrPath: finalEntryRscSsrPath, + pluginName: PLUGIN_NAME, + rsc: typeof pluginOptions.rsc === 'object' ? pluginOptions.rsc : {}, + }); + } const getRootRoutePath = () => findEntryFile(resolve(appDirectory, 'root')); const rootRoutePath = getRootRoutePath(); @@ -397,25 +392,45 @@ export const pluginReactRouter = ( }; const resolvedConfigWithRoutes: ResolvedReactRouterConfig = { - ...resolvedConfig, appDirectory: resolve(appDirectory), + basename, buildDirectory: resolve(buildDirectory), - routeDiscovery, + buildEnd, + future, prerender: prerenderConfig, routes, - unstable_routeConfig: routeConfig, + routeDiscovery, + serverBuildFile, + serverBundles, + serverModuleFormat, + ssr, + splitRouteModules, + subResourceIntegrity, allowedActionOrigins: allowedActionOrigins ?? false, + unstable_routeConfig: routeConfig, }; const { buildEnd: _buildEnd, ...resolvedConfigForPreset } = resolvedConfigWithRoutes; for (const preset of configPresets) { await preset.reactRouterConfigResolved?.({ - reactRouterConfig: resolvedConfigForPreset, + reactRouterConfig: + resolvedConfigForPreset as ReactRouterPresetResolvedConfig, }); } + const buildEndReactRouterConfig: ResolvedReactRouterConfig = { + ...resolvedConfigWithRoutes, + future: userAndPresetConfig.future ?? {}, + } as ResolvedReactRouterConfig; const isBuild = api.context.action === 'build'; + if (!isBuild) { + api.onAfterEnvironmentCompile(({ environment, stats }) => { + if (environment.name === 'node' && stats && !stats.hasErrors()) { + registerDevServerSourceMaps(stats.compilation); + } + }); + } const shouldDependOnWebCompiler = !shouldParallelizeEnvironmentBuilds({ isBuild, }); @@ -423,57 +438,202 @@ export const pluginReactRouter = ( prerenderConfig !== undefined && prerenderConfig !== false; const isSpaMode = !ssr && !isPrerenderEnabled; const routeCount = Object.keys(routes).length; - const routeChunkConfig: RouteChunkConfig = { - splitRouteModules, - appDirectory, - rootRouteFile, - }; const routeChunkCache: RouteChunkCache = new Map(); - const routeTransformExecutor = createRouteTransformExecutor({ - parallelRouteTransform: - pluginOptions.parallelRouteTransform ?? - shouldParallelizeRouteTransforms(routeCount), - routeChunkCache, - splitRouteModules: Boolean(splitRouteModules), - isBuild, - }); - const routeChunkOptions = { - splitRouteModules, - rootRouteFile, - isBuild, - cache: routeChunkCache, + const transformedRouteModuleAnalyses = new Map< + string, + RouteModuleAnalysis + >(); + const rememberRouteModuleAnalysis = ( + resourcePath: string, + analysis: RouteModuleAnalysis + ) => { + transformedRouteModuleAnalyses.set(resolve(resourcePath), analysis); }; + const routeModuleAnalysis = async (routeFilePath: string) => + transformedRouteModuleAnalyses.get(resolve(routeFilePath)); const outputClientPath = resolve(buildDirectory, 'client'); const assetsBuildDirectory = relative(process.cwd(), outputClientPath); const watchDirectory = resolve(appDirectory); - const routeRestartMarkerPath = getRouteRestartMarkerPath(outputClientPath); + const routeRestartMarkerPath = getRouteRestartMarkerPath(appDirectory); const routeWatchFiles = createReactRouterRouteWatchFiles({ configWatchPaths, routeConfigWatchPaths, routeRestartMarkerPath, onRouteTopologyChange: pluginOptions.onRouteTopologyChange, }); - const devBackgroundResources = registerReactRouterDevBackgroundResources({ - api, - isBuild, - lazyCompilationPrewarm: pluginOptions.unstableLazyCompilationPrewarm, - routeTransformExecutor, - routeRestartMarkerPath, - watchDirectory, - getRouteTopology: routeTopology.getRouteTopology, - initialRouteTopology: routeTopology.initialRouteTopology, - onRouteTopologyChange: pluginOptions.onRouteTopologyChange, - }); - - type ReactRouterManifest = Awaited< - ReturnType - >; let latestBrowserManifest: ReactRouterManifest | null = null; let latestBrowserManifestModuleExports: RouteManifestModuleExports = {}; let latestServerManifest: ReactRouterManifest | null = null; const latestServerManifestsByBundleId: Record = {}; + const routeByFilePath = new Map( + Object.values(routes).map(route => [ + resolve(appDirectory, route.file), + route, + ]) + ); + const allowedActionOriginsForBuild = + allowedActionOrigins === false ? undefined : allowedActionOrigins; + + const devHmrRefreshRuntimePath = + isBuild || isRscMode + ? undefined + : resolveReactRefreshRuntimePath(api.context.rootPath); + const devHdrSignal = devHmrRefreshRuntimePath + ? createDevHdrRevisionSignal({ + filePath: getDevHdrRevisionFilePath(api.context.rootPath), + onError: error => + api.logger.debug( + `[${PLUGIN_NAME}] Failed to signal hot data revalidation: ${error.message}` + ), + }) + : undefined; + let devHmrEnabled = false; + if (devHmrRefreshRuntimePath && devHdrSignal) { + api.modifyEnvironmentConfig( + async (environmentConfig, { name, mergeEnvironmentConfig }) => { + if (name !== 'web') return environmentConfig; + return mergeEnvironmentConfig(environmentConfig, { + tools: { + rspack: rspackConfig => { + devHmrEnabled = isRspackSwcReactRefreshEnabled(rspackConfig); + if (devHmrEnabled) devHdrSignal.ensure(); + return rspackConfig; + }, + }, + }); + } + ); + } + + const commonModeOptions = { + api, + allowedActionOriginsForBuild, + appDirectory, + basename, + customServer: pluginOptions.customServer, + isBuild, + isSpaMode, + prerenderConfig, + routeConfig, + routeDiscovery, + routes, + rootRouteFile, + splitRouteModules, + ssr, + }; + const modePlan = await (isRscMode + ? createReactRouterModePlan({ + ...commonModeOptions, + isRscMode: true, + buildDirectory, + finalEntryRscClientPath, + finalEntryRscPath, + outputClientPath, + pluginName: PLUGIN_NAME, + serverBuildFile, + }) + : createReactRouterModePlan({ + ...commonModeOptions, + isRscMode: false, + assetsBuildDirectory, + defaultEntryName: devServerBuildEntryName, + entryServerPath: finalEntryServerPath, + finalEntryClientPath, + future, + hasServerApp, + reactRouterConfig: resolvedConfigWithRoutes, + routeChunkCache, + serverAppPath, + shouldDependOnWebCompiler, + devHmr: + devHmrRefreshRuntimePath && devHdrSignal + ? { + isEnabled: () => devHmrEnabled, + runtimeModule: generateDevHmrRuntimeModule({ + reactRefreshRuntimePath: devHmrRefreshRuntimePath, + hdrRevisionFilePath: devHdrSignal.filePath, + }), + onNodeRebuildCommitted: () => { + if (devHmrEnabled) devHdrSignal.bump(); + }, + } + : undefined, + })); + + const routeTransformExecutor = + modePlan.kind === 'classic' + ? await effectRuntime.runPromise( + acquireRouteTransformExecutor({ + parallelRouteTransform: + pluginOptions.parallelRouteTransform ?? + shouldParallelizeRouteTransforms(routeCount), + routeChunkCache, + splitRouteModules: Boolean(splitRouteModules), + isBuild, + }) + ) + : undefined; + + const { manifestChunkNames } = modePlan; + + let sendRscDevUpdate: (() => void) | undefined; + let scheduledRscDevUpdate: ReturnType | undefined; + let hasPendingRscNodeUpdate = false; + if (isRscMode && !isBuild) { + api.onBeforeStartDevServer(({ server }) => { + sendRscDevUpdate = () => + server.sockWrite('custom', { + event: 'rsc:update', + }); + }); + api.onCloseDevServer(() => { + if (scheduledRscDevUpdate) { + clearTimeout(scheduledRscDevUpdate); + scheduledRscDevUpdate = undefined; + } + hasPendingRscNodeUpdate = false; + sendRscDevUpdate = undefined; + }); + api.onAfterEnvironmentCompile(({ environment, stats }) => { + if ( + (environment.name !== 'node' && environment.name !== 'web') || + stats?.hasErrors() + ) { + return; + } + if (environment.name === 'node') { + hasPendingRscNodeUpdate = true; + } + if (!hasPendingRscNodeUpdate) { + return; + } + if (scheduledRscDevUpdate) { + clearTimeout(scheduledRscDevUpdate); + } + scheduledRscDevUpdate = setTimeout(() => { + scheduledRscDevUpdate = undefined; + hasPendingRscNodeUpdate = false; + sendRscDevUpdate?.(); + }, 1000); + }); + } + + const devBackgroundResources = + await registerReactRouterDevBackgroundResources({ + api, + runtime: effectRuntime, + isBuild, + lazyCompilationPrewarm: pluginOptions.unstableLazyCompilationPrewarm, + routeTransformExecutor, + routeRestartMarkerPath, + watchDirectory, + getRouteTopology: routeTopology.getRouteTopology, + initialRouteTopology: routeTopology.initialRouteTopology, + onRouteTopologyChange: pluginOptions.onRouteTopologyChange, + }); + const stageLatestManifests = ( manifest: ReactRouterManifest, sri: ReactRouterManifest['sri'], @@ -497,8 +657,14 @@ export const pluginReactRouter = ( [devServerBuildEntryName]: baseServerManifest, }; - for (const { bundleId, entryName } of serverBundleEntries) { - const bundleRoutes = routesByServerBundleId[bundleId]; + if (modePlan.kind !== 'classic') { + return; + } + + for (const { bundleId, entryName } of modePlan.artifacts + .serverBundleEntries) { + const bundleRoutes = + modePlan.artifacts.routesByServerBundleId[bundleId]; if (!bundleRoutes) { continue; } @@ -518,94 +684,15 @@ export const pluginReactRouter = ( } if (!isBuild) { - devRuntime.captureWeb(compilation, manifestsByEntryName); + modePlan.artifacts.devRuntime.captureWeb( + compilation, + manifestsByEntryName + ); } } ); }; - const routeByFilePath = new Map( - Object.values(routes).map(route => [ - resolve(appDirectory, route.file), - route, - ]) - ); - - const manifestChunkNames = new Set(['entry.client']); - const webRouteEntries = Object.values(routes).reduce( - (acc, route) => { - const entryName = route.file.slice(0, route.file.lastIndexOf('.')); - const routeFilePath = resolve(appDirectory, route.file); - manifestChunkNames.add(entryName); - acc[entryName] = { - import: `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`, - html: false, - }; - - if (isBuild && splitRouteModules && route.id !== 'root') { - let source: string; - try { - source = readFileSync(routeFilePath, 'utf8'); - } catch (error) { - if ((error as NodeJS.ErrnoException).code === 'ENOENT') { - return acc; - } - throw error; - } - for (const exportName of routeChunkExportNames) { - if (!source.includes(exportName)) { - continue; - } - const chunkEntryName = getRouteChunkEntryName(route.id, exportName); - manifestChunkNames.add(chunkEntryName); - acc[chunkEntryName] = { - import: getRouteChunkModuleId(routeFilePath, exportName), - html: false, - }; - } - } - - return acc; - }, - {} as Record - ); - const buildManifest = await getBuildManifest({ - reactRouterConfig: resolvedConfigWithRoutes, - routes, - rootDirectory: process.cwd(), - }); - const routesByServerBundleId = getRoutesByServerBundleId( - buildManifest, - routes - ); - const serverBuildPlan = createReactRouterServerBuildPlan({ - routesByServerBundleId, - serverBuildFile, - defaultEntryName: devServerBuildEntryName, - }); - const { serverBundleEntries } = serverBuildPlan; - - const devHmrRefreshRuntimePath = isBuild - ? undefined - : resolveReactRefreshRuntimePath(api.context.rootPath); - const devHdrSignal = devHmrRefreshRuntimePath - ? createDevHdrRevisionSignal({ - filePath: getDevHdrRevisionFilePath(api.context.rootPath), - onError: error => - api.logger.debug( - `[${PLUGIN_NAME}] Failed to signal hot data revalidation: ${error.message}` - ), - }) - : undefined; - let devHmrEnabled = false; - - const devRuntime = createReactRouterDevRuntimeController({ - api, - isBuild, - buildPlan: serverBuildPlan, - onNodeRebuildCommitted: () => devHdrSignal?.bump(), - }); - let clientStats: ReactRouterManifestStats | undefined; api.onAfterEnvironmentCompile(({ stats, environment }) => { if (environment.name === 'web') { @@ -629,121 +716,82 @@ export const pluginReactRouter = ( } }); - const prerenderPaths = await resolvePrerenderPaths( - prerenderConfig, - ssr, - routeConfig, - { - logWarning: true, - warn: message => api.logger.warn(message), - } - ); - - api.onAfterBuild(({ environments }) => - runPluginEffect( - tryPluginPromise(() => - runReactRouterPrerenderBuild({ - api, - hasWebEnvironment: Boolean(environments.web), - buildDirectory, - serverBuildFile, - ssr, - isPrerenderEnabled, - prerenderConfig, - prerenderPaths, - basename, - future, - routes, - latestBrowserManifest, - latestBrowserManifestModuleExports, - clientStats, - pluginOptions, - appDirectory, - assetPrefix, - routeChunkOptions, - buildManifest, - resolvedConfigWithRoutes, - buildEnd, - }) - ) - ) - ); - - const allowedActionOriginsForBuild = - allowedActionOrigins === false ? undefined : allowedActionOrigins; - - // Public requests stay bare while Rspack resolves seeded virtual files. - const createVirtualModulePlugin = (publicPath: string) => { - const bundleVirtualModules = Object.fromEntries( - Object.entries(routesByServerBundleId).map( - ([bundleId, bundleRoutes]) => [ - `virtual/react-router/server-build-${bundleId}`, - generateServerBuild(bundleRoutes, { - entryServerPath: finalEntryServerPath, - assetsBuildDirectory, - basename, - appDirectory, + if (modePlan.kind === 'classic') { + api.onAfterBuild(({ environments }) => + effectRuntime.runPromise( + tryPluginPromise(() => + runReactRouterPrerenderBuild({ + api, + hasWebEnvironment: Boolean(environments.web), + buildDirectory, + serverBuildFile, ssr, - federation: options.federation, + isPrerenderEnabled, + prerenderConfig, + prerenderPaths: modePlan.artifacts.prerenderPaths, + basename, future, - allowedActionOrigins: allowedActionOriginsForBuild, - prerender: prerenderPaths, - routeDiscovery, - publicPath, - serverManifestId: `virtual/react-router/server-manifest-${bundleId}`, - }), - ] + routes, + latestBrowserManifest, + latestBrowserManifestModuleExports, + clientStats, + pluginOptions, + appDirectory, + assetPrefix, + routeChunkOptions: modePlan.routeChunkOptions, + routeModuleAnalysis, + buildManifest: modePlan.artifacts.buildManifest, + buildEndReactRouterConfig, + buildEnd, + }) + ) ) ); - const bundleManifestModules = Object.fromEntries( - Object.entries(routesByServerBundleId) - .filter( - ([, bundleRoutes]) => - bundleRoutes && Object.keys(bundleRoutes).length > 0 + } else { + api.onAfterBuild(({ environments }) => + effectRuntime.runPromise( + tryPluginPromise(() => + runReactRouterRscPrerenderBuild({ + api, + hasWebEnvironment: Boolean(environments.web), + buildDirectory, + serverBuildFile, + ssr, + prerenderConfig, + prerenderPaths: modePlan.prerenderPaths, + basename, + }) ) - .map(([bundleId]) => [ - `virtual/react-router/server-manifest-${bundleId}`, - 'export default {};', - ]) + ) ); + } + // Public requests stay bare while Rspack resolves seeded virtual files. + const createVirtualModulePlugin = ( + publicPath: string, + jsDistPath: string + ) => { return new rspack.experiments.VirtualModulesPlugin( - mapVirtualModules({ - 'virtual/react-router/browser-manifest': 'export default {};', - 'virtual/react-router/server-manifest': 'export default {};', - 'virtual/react-router/server-build': generateServerBuild(routes, { - entryServerPath: finalEntryServerPath, - assetsBuildDirectory, - basename, - appDirectory, - ssr, - federation: options.federation, - future, - allowedActionOrigins: allowedActionOriginsForBuild, - prerender: prerenderPaths, - routeDiscovery, - publicPath, - }), - ...bundleVirtualModules, - ...bundleManifestModules, - 'virtual/react-router/with-props': generateWithProps(), - ...(devHmrRefreshRuntimePath - ? { - [DEV_HMR_RUNTIME_MODULE_ID]: generateDevHmrRuntimeModule({ - reactRefreshRuntimePath: devHmrRefreshRuntimePath, - hdrRevisionFilePath: getDevHdrRevisionFilePath( - api.context.rootPath - ), - }), - } - : {}), - }) + mapVirtualModules(modePlan.createVirtualModules(publicPath, jsDistPath)) ); }; api.modifyRsbuildConfig(async (config, { mergeRsbuildConfig }) => { - assetPrefix = normalizeAssetPrefix(config.output?.assetPrefix); - const vmodPlugin = createVirtualModulePlugin(assetPrefix); + // The RSC bootstrap script URL must reflect the user's web js distPath; + // the entry filename itself is deterministic because the plugin forces + // web `output.filename.js` to '[name].js' below. + const webDistPath = config.environments?.web?.output?.distPath; + const rootDistPath = config.output?.distPath; + const jsDistPath = + (typeof webDistPath === 'object' ? webDistPath.js : undefined) ?? + (typeof rootDistPath === 'object' ? rootDistPath.js : undefined) ?? + DEFAULT_JS_DIST_PATH; + const assetPrefix = resolveEffectiveAssetPrefix({ + dev: config.dev, + output: config.output, + isBuild, + }); + const vmodPlugin = createVirtualModulePlugin(assetPrefix, jsDistPath); const useAsyncNodeChunkLoading = options.federation && resolvedServerOutput === 'commonjs'; let nodeChunkLoading: 'import' | 'async-node' | 'require' = 'require'; @@ -752,15 +800,6 @@ export const pluginReactRouter = ( } else if (useAsyncNodeChunkLoading) { nodeChunkLoading = 'async-node'; } - const nodeEntries = createReactRouterNodeEntries({ - hasServerApp, - isBuild, - serverAppPath, - entryServerPath: finalEntryServerPath, - defaultEntryName: devServerBuildEntryName, - serverBundleEntries, - }); - const configuredLazyCompilation = Object.prototype.hasOwnProperty.call( options, 'lazyCompilation' @@ -769,7 +808,9 @@ export const pluginReactRouter = ( : (config.dev?.lazyCompilation ?? pluginOptions.lazyCompilation); const guardedLazyCompilation = guardReactRouterLazyCompilation({ lazyCompilation: configuredLazyCompilation, - entryClientPath: finalEntryClientPath, + entryClientPath: isRscMode + ? finalEntryRscClientPath + : finalEntryClientPath, prewarmReactRouterModules: Boolean( pluginOptions.unstableLazyCompilationPrewarm ), @@ -783,6 +824,27 @@ export const pluginReactRouter = ( routeCount >= 256 && (config.performance?.printFileSize === undefined || config.performance.printFileSize === true); + const resolveConfig = modePlan.createResolveConfig(api.context.rootPath); + + // Browser code (React itself) reads `process.env.NODE_ENV`. Rsbuild only + // emits the define for its recognized modes; an unrecognized NODE_ENV + // (e.g. the string "undefined" leaking from a misconfigured shell) + // resolves mode 'none' and leaves the bare reference in the web bundle, + // which throws `process is not defined` at runtime. Always define it for + // the web environment — mirroring the Vite plugin — unless the user + // supplies their own define. + const userDefinesNodeEnv = + config.source?.define?.['process.env.NODE_ENV'] !== undefined || + config.environments?.web?.source?.define?.['process.env.NODE_ENV'] !== + undefined; + const webNodeEnv = + process.env.NODE_ENV === 'production' || + process.env.NODE_ENV === 'development' || + process.env.NODE_ENV === 'test' + ? process.env.NODE_ENV + : isBuild + ? 'production' + : 'development'; return mergeRsbuildConfig(config, { ...(shouldCompactFileSizeReport @@ -799,29 +861,20 @@ export const pluginReactRouter = ( output: { assetPrefix: config.output?.assetPrefix || '/', }, + server: modePlan.server, dev: { ...lazyCompilation, watchFiles: mergeWatchFiles(config.dev?.watchFiles, routeWatchFiles), }, - // React Router's request handler natively supports `ssr:false` - // builds (it renders the SPA shell for document requests), so the - // middleware is registered for SPA mode too — without it, dev - // requests would 404 because no HTML entry exists. - ...(pluginOptions.customServer - ? {} - : { - server: { - setup: [ - createReactRouterDevServerSetup({ - // Lazy: the dev runtime binding does not exist yet here. - loadBuild: () => devRuntime.createBuildLoader()(), - }), - ], - }, - }), tools: { rspack: { - plugins: [vmodPlugin], + resolve: resolveConfig, + plugins: [ + vmodPlugin, + createQuerylessRouteImportPlugin(routeByFilePath, { + rsc: isRscMode, + }), + ], }, }, environments: { @@ -836,15 +889,14 @@ export const pluginReactRouter = ( } : {}), source: { - entry: { - // no query needed when federation is disabled - 'entry.client': finalEntryClientPath, - 'virtual/react-router/browser-manifest': { - import: 'virtual/react-router/browser-manifest', - html: false, - }, - ...webRouteEntries, - }, + entry: modePlan.webEntries, + ...(userDefinesNodeEnv + ? {} + : { + define: { + 'process.env.NODE_ENV': JSON.stringify(webNodeEnv), + }, + }), }, output: { filename: { @@ -856,6 +908,7 @@ export const pluginReactRouter = ( }, tools: { rspack: { + resolve: resolveConfig, name: 'web', module: { rules: [ @@ -866,26 +919,17 @@ export const pluginReactRouter = ( }, ], }, - ...(options.federation - ? { - output: { - chunkLoading: 'import', - }, - } - : {}), - externalsType: 'module', + externalsType: modePlan.webExternalsType, output: { - chunkFormat: 'module', - chunkLoading: 'import', - workerChunkLoading: 'import', - wasmLoading: 'fetch', - library: { type: 'module' }, - module: true, - }, - optimization: { - avoidEntryIife: true, - runtimeChunk: 'single', + ...modePlan.webOutput, + publicPath: assetPrefix, + ...(options.federation + ? { + chunkLoading: 'import', + } + : {}), }, + optimization: modePlan.webOptimization, }, }, }, @@ -894,7 +938,7 @@ export const pluginReactRouter = ( // root route into a hydratable `index.html` at build time. node: { source: { - entry: nodeEntries, + entry: modePlan.nodeEntries, }, output: { distPath: { @@ -903,6 +947,15 @@ export const pluginReactRouter = ( target: config.environments?.node?.output?.target || 'node', filename: { js: '[name].js', + css: (pathData: Rspack.PathData) => { + const sourceName = pathData.chunk?.name ?? '[name]'; + if (!isBuild) { + return `${sourceName}.css`; + } + const baseName = + sourceName.split(/[\\/]/).pop() || sourceName; + return `../assets/${baseName}.[contenthash:10].css`; + }, }, }, tools: { @@ -917,15 +970,19 @@ export const pluginReactRouter = ( }, ], }, - externals: nodeExternals, - ...(shouldDependOnWebCompiler ? { dependencies: ['web'] } : {}), + externals: modePlan.nodeExternals, + ...modePlan.nodeDependencies, externalsType: resolvedServerOutput, output: { chunkFormat: resolvedServerOutput, chunkLoading: nodeChunkLoading, + devtoolModuleFilenameTemplate: '[absolute-resource-path]', + devtoolFallbackModuleFilenameTemplate: + '[absolute-resource-path]?[hash]', workerChunkLoading: nodeChunkLoading, wasmLoading: 'fetch', module: resolvedServerOutput === 'module', + chunkFilename: 'static/js/async/[name].js', }, }, }, @@ -934,104 +991,92 @@ export const pluginReactRouter = ( }); }); - api.modifyEnvironmentConfig( - async (config, { name, mergeEnvironmentConfig }) => { - if (name !== 'web' && name !== 'node') { - return config; - } - - return mergeEnvironmentConfig(config, { - tools: { - rspack: rspackConfig => { - if (pluginOptions.federation) { - ensureFederationAsyncStartup(rspackConfig); - } - - if (name === 'web') { - devHmrEnabled = - !isBuild && - devHmrRefreshRuntimePath !== undefined && - config.mode === 'development' && - config.dev?.hmr !== false && - isRspackSwcReactRefreshEnabled(rspackConfig); - if (devHmrEnabled) { - devHdrSignal?.ensure(); - } - } + registerReactRouterEnvironmentOutput({ + api, + federation: pluginOptions.federation, + resolvedServerOutput, + }); - if (name === 'node') { - const output = rspackConfig.output; - if (output) { - const library = output.library; - const libraryOptions = - library && - typeof library === 'object' && - !Array.isArray(library) - ? library - : {}; - rspackConfig.output = { - ...output, - library: { - ...libraryOptions, - type: - resolvedServerOutput === 'module' - ? 'module' - : 'commonjs2', - }, - }; - } - } + if (modePlan.kind === 'rsc') { + registerReactRouterRscRouteTransforms({ + api, + isBuild, + performanceProfiler, + routeByFilePath, + routeChunkCache, + routeChunkConfig: modePlan.routeChunkConfig, + }); - return rspackConfig; - }, - }, - }); + // RSC mode has no `registerBuildOutputTransforms` pass, so relocate the + // node-emitted `?url`/`.css?url` static assets into the client build here. + // Without this the href baked into `links()` (resolved in the node env) + // 404s in the browser because the file only exists under `build/server`. + registerSsrAssetRelocation({ + api, + outputClientPath, + performanceProfiler, + }); + } else { + if (!routeTransformExecutor) { + throw new Error('Route transform executor was not initialized.'); } - ); - registerModifyBrowserManifestAssets( - api, - routes, - pluginOptions, - appDirectory, - () => assetPrefix, - routeChunkOptions, - { - subResourceIntegrity: resolvedConfigWithRoutes.subResourceIntegrity, - future, - manifestChunkNames, - onManifest: (manifest, sri, moduleExportsByRouteId, context) => - stageLatestManifests( - manifest, - sri, - moduleExportsByRouteId, - context.compilation - ), - } - ); + registerModifyBrowserManifestAssets( + api, + routes, + pluginOptions, + appDirectory, + () => assetPrefix, + modePlan.routeChunkOptions, + { + subResourceIntegrity: resolvedConfigWithRoutes.subResourceIntegrity, + future, + manifestChunkNames, + routeModuleAnalysis, + onManifest: (manifest, sri, moduleExportsByRouteId, context) => + stageLatestManifests( + manifest, + sri, + moduleExportsByRouteId, + context.compilation + ), + } + ); - registerBuildOutputTransforms({ - api, - resolvedServerOutput, - performanceProfiler, - getLatestServerManifest: () => latestServerManifest, - getLatestServerManifestByBundleId: bundleId => - latestServerManifestsByBundleId[bundleId], - routes, - pluginOptions, - getClientStats: () => clientStats, - appDirectory, - getAssetPrefix: () => assetPrefix, - routeChunkOptions, - routeTransformExecutor, - routeByFilePath, - routeChunkConfig, - isBuild, - splitRouteModules: Boolean(splitRouteModules), - ssr, - isSpaMode, - rootRoutePath, - isDevHmrEnabled: () => devHmrEnabled, - }); + registerBuildOutputTransforms({ + api, + resolvedServerOutput, + performanceProfiler, + getLatestServerManifest: () => latestServerManifest, + getLatestServerManifestByBundleId: bundleId => + latestServerManifestsByBundleId[bundleId], + routes, + pluginOptions, + getClientStats: () => clientStats, + appDirectory, + getAssetPrefix: () => assetPrefix, + routeChunkOptions: modePlan.routeChunkOptions, + routeModuleAnalysis, + routeTransformExecutor, + routeByFilePath, + routeChunkConfig: modePlan.routeChunkConfig, + isBuild, + splitRouteModules: Boolean(modePlan.routeChunkConfig.splitRouteModules), + ssr, + isSpaMode, + rootRoutePath, + outputClientPath, + isDevHmrEnabled: () => devHmrEnabled, + onRouteModuleAnalysis: rememberRouteModuleAnalysis, + }); + } }, }); + +export const pluginReactRouterRSC = ( + options: ReactRouterRSCPluginOptions = {} +): RsbuildPlugin => + pluginReactRouter({ + ...options, + rsc: options.rsc ?? true, + }); diff --git a/src/lazy-compilation-prewarm.ts b/src/lazy-compilation-prewarm.ts index 78ccbe03..4bc3d073 100644 --- a/src/lazy-compilation-prewarm.ts +++ b/src/lazy-compilation-prewarm.ts @@ -1,7 +1,12 @@ import * as Effect from 'effect/Effect'; +import type * as Scope from 'effect/Scope'; import type { ReactRouterManifestForDev } from './manifest.js'; import type { RouteManifestItem } from './types.js'; -import { createDelayedPluginTask, tryPluginPromise } from './effect-runtime.js'; +import { + createDelayedPluginTask, + type PluginEffectRuntime, + tryPluginPromise, +} from './effect-runtime.js'; const DEFAULT_LAZY_COMPILATION_TRIGGER_PREFIX = '/_rspack/lazy/trigger'; const DEFAULT_PREWARM_DELAY_MS = 0; @@ -9,17 +14,17 @@ const DEFAULT_ROUTE_PREWARM_LIMIT = 8; const PREWARM_FETCH_CONCURRENCY = 8; const PREWARM_TRIGGER_CANDIDATES = 4; -type LazyCompilationPrewarmConfig = { +export type LazyCompilationPrewarmConfig = { entry: boolean; routeLimit: number; delayMs: number; }; -type LazyCompilationPrewarmController = { +export type LazyCompilationPrewarmController = { setServerOrigin(origin: string): void; setManifest(manifest: ReactRouterManifestForDev | null): void; schedule(): void; - cancelEffect(): Effect.Effect; + cancelEffect(): Effect.Effect; }; export type RspackLazyCompilationTriggerClient = { @@ -221,9 +226,11 @@ const prewarmLazyCompilation = ({ }); export const createLazyCompilationPrewarmController = ({ + runtime, config, onError, }: { + runtime: PluginEffectRuntime; config: LazyCompilationPrewarmConfig; onError: (error: Error) => void; }): LazyCompilationPrewarmController => { @@ -231,6 +238,7 @@ export const createLazyCompilationPrewarmController = ({ let manifest: ReactRouterManifestForDev | null = null; let lastPrewarmAssetsKey: string | undefined; const task = createDelayedPluginTask({ + runtime, delayMs: config.delayMs, run: () => Effect.gen(function* () { @@ -277,3 +285,11 @@ export const createLazyCompilationPrewarmController = ({ }, }; }; + +export const acquireLazyCompilationPrewarm = ( + options: Parameters[0] +): Effect.Effect => + Effect.acquireRelease( + Effect.sync(() => createLazyCompilationPrewarmController(options)), + controller => controller.cancelEffect() + ); diff --git a/src/manifest.ts b/src/manifest.ts index 314c51c9..3d6cd61f 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -3,7 +3,11 @@ import { dirname, isAbsolute, relative, resolve } from 'pathe'; import * as Effect from 'effect/Effect'; import type { Route, PluginOptions, RouteManifestItem } from './types.js'; import { combineURLs, createRouteId } from './plugin-utils.js'; -import { SERVER_EXPORTS, CLIENT_EXPORTS } from './constants.js'; +import { + CLIENT_EXPORTS, + DEFAULT_JS_DIST_PATH, + SERVER_EXPORTS, +} from './constants.js'; import { buildManifestChunkValidity, createEmptyRouteChunkByExportName, @@ -13,8 +17,12 @@ import { validateRouteChunks, type RouteChunkCache, type RouteChunkConfig, + type RouteChunkExportName, } from './route-chunks.js'; -import { getRouteModuleAnalysis } from './export-utils.js'; +import { + getRouteModuleAnalysis, + type RouteModuleAnalysis, +} from './export-utils.js'; import { getCappedPluginConcurrency } from './concurrency.js'; import { runPluginEffect, tryPluginPromise } from './effect-runtime.js'; @@ -87,13 +95,38 @@ export function configRoutesToRouteManifestEntries( return routeManifestEntries; } -type RouteChunkManifestOptions = { +export type RouteChunkManifestOptions = { splitRouteModules?: boolean | 'enforce'; rootRouteFile?: string; isBuild?: boolean; cache?: RouteChunkCache; }; +export type RouteModuleAnalysisProvider = ( + routeFilePath: string, + route: Route +) => Promise; + +export type ReactRouterManifestOptions = RouteChunkManifestOptions & { + routeModuleAnalysis?: RouteModuleAnalysisProvider; +}; + +export const createReactRouterManifestOptions = ({ + routeChunks, + routeModuleAnalysis, +}: { + routeChunks?: RouteChunkManifestOptions; + routeModuleAnalysis?: RouteModuleAnalysisProvider; +}): ReactRouterManifestOptions | undefined => { + if (!routeChunks && !routeModuleAnalysis) { + return undefined; + } + return { + ...routeChunks, + ...(routeModuleAnalysis ? { routeModuleAnalysis } : {}), + }; +}; + export type ReactRouterManifestForDev = { version: string; url: string; @@ -122,8 +155,10 @@ type ReactRouterManifestStatsEntrypoint = { getFiles?: () => Iterable; }; -type ReactRouterManifestStatsLookup = Iterable<[string, T]> & { - get?: (name: string) => T | undefined; +type ReactRouterManifestStatsLookup = Iterable< + [string, T | null | undefined] +> & { + get?: (name: string) => T | null | undefined; }; type ReactRouterManifestStatsCompilation = { @@ -153,6 +188,9 @@ const collectManifestFilesByName = ( const filesByName: Record = {}; if (!names) { for (const [name, item] of items) { + if (item == null) { + continue; + } filesByName[name] = getFiles(name, item); } return filesByName; @@ -162,7 +200,7 @@ const collectManifestFilesByName = ( if (typeof items.get === 'function') { for (const name of names) { const item = items.get(name); - if (!item) { + if (item == null) { continue; } filesByName[name] = getFiles(name, item); @@ -178,6 +216,9 @@ const collectManifestFilesByName = ( if (!missingNames.has(name)) { continue; } + if (item == null) { + continue; + } filesByName[name] = getFiles(name, item); missingNames.delete(name); if (missingNames.size === 0) { @@ -236,11 +277,12 @@ type RouteManifestAnalysis = { > | null; }; -const DEFAULT_MANIFEST_DIR = 'static/js'; +const DEFAULT_MANIFEST_DIR = DEFAULT_JS_DIST_PATH; const CSS_IMPORT_RE = /\.(?:css|less|sass|scss)(?:\?[^'"`]+)?['"`]/; const createChunkAssetResolver = ( - clientStats: ReactRouterManifestStats | undefined + clientStats: ReactRouterManifestStats | undefined, + includeEntrypointJs: boolean ): ((chunkName: string) => ChunkAssets) => { const chunkAssetsByName = new Map(); @@ -259,24 +301,26 @@ const createChunkAssetResolver = ( } const cssAssets = new Set(); - const jsAssets: string[] = []; + const jsAssets = new Set(); for (const asset of assets) { if (asset.endsWith('.css')) { cssAssets.add(asset); } else if (asset.endsWith('.js')) { - jsAssets.push(asset); + jsAssets.add(asset); } } for (const asset of clientStats?.entrypointFilesByName?.[chunkName] ?? []) { if (asset.endsWith('.css')) { cssAssets.add(asset); + } else if (includeEntrypointJs && asset.endsWith('.js')) { + jsAssets.add(asset); } } - if (jsAssets.length === 0) { - jsAssets.push(`${DEFAULT_MANIFEST_DIR}/${chunkName}.js`); + if (jsAssets.size === 0) { + jsAssets.add(`${DEFAULT_MANIFEST_DIR}/${chunkName}.js`); } - const result = { js: jsAssets, css: [...cssAssets] }; + const result = { js: [...jsAssets], css: [...cssAssets] }; chunkAssetsByName.set(chunkName, result); return result; }; @@ -289,6 +333,8 @@ const analyzeRouteForManifestEffect = ({ routeChunkConfig, routeEntryName, routeFilePath, + route, + routeModuleAnalysis, }: { discoveredCssAssets: string[]; isBuild: boolean; @@ -296,10 +342,13 @@ const analyzeRouteForManifestEffect = ({ routeChunkConfig: RouteChunkConfig | null; routeEntryName: string; routeFilePath: string; + route: Route; + routeModuleAnalysis?: RouteModuleAnalysisProvider; }): Effect.Effect => tryPluginPromise(async () => { const { code, exports: exportNames } = - await getRouteModuleAnalysis(routeFilePath); + (await routeModuleAnalysis?.(routeFilePath, route)) ?? + (await getRouteModuleAnalysis(routeFilePath)); const cssAssets = !isBuild && discoveredCssAssets.length === 0 && CSS_IMPORT_RE.test(code) ? [ @@ -398,33 +447,94 @@ export const getReactRouterManifestChunkNames = ( return chunkNames; }; +const createRouteManifestItem = ({ + route, + assetPrefix, + jsAssets, + routeAnalysis, + getModulePathForChunk, + getCssAssetsForChunk, +}: { + route: Route; + assetPrefix: string; + jsAssets: string[]; + routeAnalysis: RouteManifestAnalysis; + getModulePathForChunk: (chunkName: string) => string | undefined; + getCssAssetsForChunk: (chunkName: string) => string[]; +}): RouteManifestItem => { + const routeChunkMap = routeAnalysis.hasRouteChunkByExportName; + const chunkModulePath = (exportName: RouteChunkExportName) => + routeChunkMap?.[exportName] + ? getModulePathForChunk(getRouteChunkEntryName(route.id, exportName)) + : undefined; + const cssAssets = [ + ...routeAnalysis.cssAssets, + ...routeChunkExportNames.flatMap(exportName => + routeChunkMap?.[exportName] + ? getCssAssetsForChunk(getRouteChunkEntryName(route.id, exportName)) + : [] + ), + ]; + + return { + id: route.id, + parentId: route.parentId, + path: route.path, + index: route.index, + caseSensitive: route.caseSensitive, + module: combineURLs(assetPrefix, jsAssets[0] || ''), + clientActionModule: chunkModulePath('clientAction'), + clientLoaderModule: chunkModulePath('clientLoader'), + clientMiddlewareModule: chunkModulePath('clientMiddleware'), + hydrateFallbackModule: chunkModulePath('HydrateFallback'), + hasAction: routeAnalysis.exports.has(SERVER_EXPORTS.action), + hasLoader: routeAnalysis.exports.has(SERVER_EXPORTS.loader), + hasClientAction: routeAnalysis.exports.has(CLIENT_EXPORTS.clientAction), + hasClientLoader: routeAnalysis.exports.has(CLIENT_EXPORTS.clientLoader), + hasClientMiddleware: routeAnalysis.exports.has( + CLIENT_EXPORTS.clientMiddleware + ), + hasDefaultExport: routeAnalysis.exports.has('default'), + hasErrorBoundary: routeAnalysis.exports.has(CLIENT_EXPORTS.ErrorBoundary), + // `module` is `jsAssets[0]`; exclude it from `imports` so the browser + // manifest does not list the route's own module twice (upstream excludes + // the entry chunk's own file from its imports list). Otherwise prefetch + // link computations produce duplicate modulepreload hrefs. + imports: jsAssets.slice(1).map(asset => combineURLs(assetPrefix, asset)), + css: Array.from(new Set(cssAssets)).map(asset => + combineURLs(assetPrefix, asset) + ), + }; +}; + function generateReactRouterManifestForDevEffect( routes: Record, - _options: PluginOptions, clientStats: ReactRouterManifestStats | undefined, context: string, assetPrefix: string, - routeChunkOptions?: RouteChunkManifestOptions + manifestOptions?: ReactRouterManifestOptions ): Effect.Effect { return Effect.gen(function* () { const result: Record = {}; - const splitRouteModules = routeChunkOptions?.splitRouteModules ?? false; + const splitRouteModules = manifestOptions?.splitRouteModules ?? false; const enforceSplitRouteModules = splitRouteModules === 'enforce'; - const isBuild = routeChunkOptions?.isBuild ?? false; + const isBuild = manifestOptions?.isBuild ?? false; const routeChunkConfig: RouteChunkConfig | null = - splitRouteModules && routeChunkOptions?.rootRouteFile + splitRouteModules && manifestOptions?.rootRouteFile ? { splitRouteModules, appDirectory: context, - rootRouteFile: routeChunkOptions.rootRouteFile, + rootRouteFile: manifestOptions.rootRouteFile, } : null; - const getAssetsForChunk = createChunkAssetResolver(clientStats); + const getAssetsForChunk = createChunkAssetResolver(clientStats, isBuild); const getModulePathForChunk = (chunkName: string): string | undefined => { const { js: jsAssets } = getAssetsForChunk(chunkName); return jsAssets[0] ? combineURLs(assetPrefix, jsAssets[0]) : undefined; }; + const getCssAssetsForChunk = (chunkName: string): string[] => + getAssetsForChunk(chunkName).css; const manifestEntries = yield* Effect.forEach( Object.entries(routes), @@ -437,78 +547,36 @@ function generateReactRouterManifestForDevEffect( const routeAnalysis = yield* analyzeRouteForManifestEffect({ discoveredCssAssets, isBuild, - routeChunkCache: routeChunkOptions?.cache, + routeChunkCache: manifestOptions?.cache, routeChunkConfig, routeEntryName, routeFilePath, + route, + routeModuleAnalysis: manifestOptions?.routeModuleAnalysis, }); - const hasClientAction = routeAnalysis.exports.has( - CLIENT_EXPORTS.clientAction - ); - const hasClientLoader = routeAnalysis.exports.has( - CLIENT_EXPORTS.clientLoader - ); - const hasClientMiddleware = routeAnalysis.exports.has( - CLIENT_EXPORTS.clientMiddleware - ); - const hasDefaultExport = routeAnalysis.exports.has('default'); - const routeChunkMap = routeAnalysis.hasRouteChunkByExportName; - if (isBuild && enforceSplitRouteModules && routeChunkConfig) { validateRouteChunks({ config: routeChunkConfig, id: routeFilePath, valid: buildManifestChunkValidity( routeAnalysis.exports, - routeChunkMap ?? createEmptyRouteChunkByExportName() + routeAnalysis.hasRouteChunkByExportName ?? + createEmptyRouteChunkByExportName() ), }); } return [ key, - { - id: route.id, - parentId: route.parentId, - path: route.path, - index: route.index, - caseSensitive: route.caseSensitive, - module: combineURLs(assetPrefix, jsAssets[0] || ''), - clientActionModule: routeChunkMap?.clientAction - ? getModulePathForChunk( - getRouteChunkEntryName(route.id, 'clientAction') - ) - : undefined, - clientLoaderModule: routeChunkMap?.clientLoader - ? getModulePathForChunk( - getRouteChunkEntryName(route.id, 'clientLoader') - ) - : undefined, - clientMiddlewareModule: routeChunkMap?.clientMiddleware - ? getModulePathForChunk( - getRouteChunkEntryName(route.id, 'clientMiddleware') - ) - : undefined, - hydrateFallbackModule: routeChunkMap?.HydrateFallback - ? getModulePathForChunk( - getRouteChunkEntryName(route.id, 'HydrateFallback') - ) - : undefined, - hasAction: routeAnalysis.exports.has(SERVER_EXPORTS.action), - hasLoader: routeAnalysis.exports.has(SERVER_EXPORTS.loader), - hasClientAction, - hasClientLoader, - hasClientMiddleware, - hasDefaultExport, - hasErrorBoundary: routeAnalysis.exports.has( - CLIENT_EXPORTS.ErrorBoundary - ), - imports: jsAssets.map(asset => combineURLs(assetPrefix, asset)), - css: routeAnalysis.cssAssets.map(asset => - combineURLs(assetPrefix, asset) - ), - }, + createRouteManifestItem({ + route, + assetPrefix, + jsAssets, + routeAnalysis, + getModulePathForChunk, + getCssAssetsForChunk, + }), routeAnalysis.routeModuleExports, ] as const; }), @@ -536,7 +604,12 @@ function generateReactRouterManifestForDevEffect( const fingerprintedValues = { entry: { module: combineURLs(assetPrefix, entryJsAssets[0] || ''), - imports: entryJsAssets.map(asset => combineURLs(assetPrefix, asset)), + // Exclude the entry's own module (`entryJsAssets[0]`) from imports so it + // is not listed twice, matching upstream where `imports` holds only the + // chunk's dependency imports. + imports: entryJsAssets + .slice(1) + .map(asset => combineURLs(assetPrefix, asset)), css: entryCssAssets.map(asset => combineURLs(assetPrefix, asset)), }, routes: result, @@ -550,7 +623,10 @@ function generateReactRouterManifestForDevEffect( const manifest = { version, - url: combineURLs(assetPrefix, manifestPath), + url: combineURLs( + assetPrefix, + isBuild ? manifestPath : `${manifestPath}?v=${version}` + ), hmr: undefined, entry: fingerprintedValues.entry, sri: undefined, @@ -566,20 +642,19 @@ function generateReactRouterManifestForDevEffect( export async function generateReactRouterManifestForDev( routes: Record, - options: PluginOptions, + _options: PluginOptions, clientStats: ReactRouterManifestStats | undefined, context: string, assetPrefix = '/', - routeChunkOptions?: RouteChunkManifestOptions + manifestOptions?: ReactRouterManifestOptions ): Promise { return runPluginEffect( generateReactRouterManifestForDevEffect( routes, - options, clientStats, context, assetPrefix, - routeChunkOptions + manifestOptions ) ); } diff --git a/src/mode-plan.ts b/src/mode-plan.ts new file mode 100644 index 00000000..5ab3c398 --- /dev/null +++ b/src/mode-plan.ts @@ -0,0 +1,364 @@ +import type { + RsbuildConfig, + RsbuildEntryDescription, + RsbuildPluginAPI, + Rspack, +} from '@rsbuild/core'; +import { rspack } from '@rsbuild/core'; +import type { RouteConfigEntry } from '@react-router/dev/routes'; +import { resolve } from 'pathe'; +import type { + Config, + ResolvedReactRouterConfig, +} from './react-router-config.js'; +import type { RouteChunkCache, RouteChunkConfig } from './route-chunks.js'; +import type { DevHmrPlanOptions } from './dev-hmr.js'; +import type { Route } from './types.js'; +import { createReactRouterDevServerSetup } from './dev-server.js'; +import { resolveAppPackagePath } from './plugin-utils.js'; +import { resolvePrerenderPaths } from './prerender.js'; +import { createReactRouterNodeEntries } from './server-build-plan.js'; +import { getSsrExternals } from './ssr-externals.js'; +import { + createClassicBuildArtifacts, + createClassicVirtualModules, + createClassicWebRouteEntries, + type ClassicBuildArtifacts, +} from './classic-mode.js'; +import { + createReactRouterRscDevServerSetup, + createReactRouterRscResolveAliases, + createReactRouterRscVirtualModules, +} from './rsc-support.js'; + +type CommonModePlan = { + routeChunkConfig: RouteChunkConfig; + manifestChunkNames: Set; + webEntries: Record; + nodeEntries: Record; + createVirtualModules( + publicPath: string, + jsDistPath: string + ): Record; + createResolveConfig(rootPath: string): Rspack.Configuration['resolve']; + server: RsbuildConfig['server'] | undefined; + webExternalsType: 'module' | undefined; + webOutput: NonNullable; + webOptimization: NonNullable; + nodeExternals: string[] | undefined; + nodeDependencies: { dependencies?: string[] }; +}; + +export type ClassicModePlan = CommonModePlan & { + kind: 'classic'; + artifacts: ClassicBuildArtifacts; + routeChunkOptions: { + splitRouteModules: Config['splitRouteModules']; + rootRouteFile: string; + isBuild: boolean; + cache: RouteChunkCache; + }; +}; + +export type RscModePlan = CommonModePlan & { + kind: 'rsc'; + prerenderPaths: string[]; +}; + +export type ReactRouterModePlan = ClassicModePlan | RscModePlan; + +// Fields both mode planners need. Mode-specific inputs live on the +// per-mode option types below so each planner owns only what it uses. +type ModePlanContext = { + allowedActionOriginsForBuild: string[] | undefined; + api: RsbuildPluginAPI; + appDirectory: string; + basename: string; + customServer: boolean; + splitRouteModules: Config['splitRouteModules']; + isBuild: boolean; + isSpaMode: boolean; + prerenderConfig: Config['prerender']; + routeConfig: RouteConfigEntry[]; + routeDiscovery: Config['routeDiscovery']; + routes: Record; + rootRouteFile: string; + ssr: boolean; +}; + +type CreateClassicModePlanOptions = ModePlanContext & { + assetsBuildDirectory: string; + devHmr?: DevHmrPlanOptions; + defaultEntryName: string; + entryServerPath: string; + finalEntryClientPath: string; + future: Config['future']; + hasServerApp: boolean; + reactRouterConfig: ResolvedReactRouterConfig; + routeChunkCache: RouteChunkCache; + serverAppPath: string; + shouldDependOnWebCompiler: boolean; +}; + +type CreateRscModePlanOptions = ModePlanContext & { + buildDirectory: string; + finalEntryRscClientPath: string; + finalEntryRscPath: string; + outputClientPath: string; + pluginName: string; + serverBuildFile: string | undefined; +}; + +type CreateReactRouterModePlanOptions = + | ({ isRscMode: true } & CreateRscModePlanOptions) + | ({ isRscMode: false } & CreateClassicModePlanOptions); + +const RSC_LAYERS = rspack.experiments.rsc.Layers; + +const createReactRouterPackageAliases = (): Record => { + const reactRouterPath = resolveAppPackagePath('react-router'); + const reactRouterDomPath = resolveAppPackagePath('react-router/dom'); + return { + ...(reactRouterPath ? { 'react-router$': reactRouterPath } : {}), + ...(reactRouterDomPath ? { 'react-router/dom$': reactRouterDomPath } : {}), + }; +}; + +const createRscModePlan = async ({ + allowedActionOriginsForBuild, + api, + appDirectory, + basename, + buildDirectory, + customServer, + finalEntryRscClientPath, + finalEntryRscPath, + isBuild, + outputClientPath, + pluginName, + prerenderConfig, + routeConfig, + routeDiscovery, + routes, + rootRouteFile, + serverBuildFile, + splitRouteModules, + ssr, +}: CreateRscModePlanOptions): Promise => { + const rscServerEntryName = (serverBuildFile || 'index.js').replace( + /\.js$/, + '' + ); + const prerenderPaths = await resolvePrerenderPaths( + prerenderConfig, + ssr, + routeConfig, + { + logWarning: true, + warn: message => api.logger.warn(message), + } + ); + return { + kind: 'rsc', + prerenderPaths, + // RSC route chunking is content-detected; the config value only gates + // 'enforce' validation, matching upstream's RSC Vite plugin. + routeChunkConfig: { + splitRouteModules, + appDirectory, + rootRouteFile, + }, + manifestChunkNames: new Set(['index']), + webEntries: { + index: { + import: finalEntryRscClientPath, + html: false, + }, + }, + nodeEntries: { + [rscServerEntryName]: { + import: finalEntryRscPath, + layer: RSC_LAYERS.rsc, + }, + }, + createVirtualModules: (publicPath: string, jsDistPath: string) => + createReactRouterRscVirtualModules({ + allowedActionOrigins: allowedActionOriginsForBuild, + appDirectory, + basename, + buildDirectory, + isBuild, + jsDistPath, + outputClientPath, + publicPath, + routeDiscovery, + routes, + ssr, + }), + createResolveConfig: (rootPath: string) => ({ + modules: [resolve(rootPath, 'node_modules'), 'node_modules'], + alias: createReactRouterRscResolveAliases(rootPath), + }), + server: !customServer + ? { + setup: createReactRouterRscDevServerSetup({ + entryName: rscServerEntryName, + pluginName, + }), + } + : undefined, + webExternalsType: undefined, + webOutput: { + chunkFormat: 'array-push', + chunkLoading: 'jsonp', + workerChunkLoading: 'import-scripts', + wasmLoading: 'fetch', + module: false, + }, + webOptimization: { + mangleExports: false, + splitChunks: false, + usedExports: false, + runtimeChunk: false, + }, + nodeExternals: undefined, + nodeDependencies: {}, + }; +}; + +const createClassicModePlan = async ({ + api, + allowedActionOriginsForBuild, + appDirectory, + assetsBuildDirectory, + basename, + customServer, + defaultEntryName, + devHmr, + entryServerPath, + finalEntryClientPath, + future, + hasServerApp, + isBuild, + isSpaMode, + prerenderConfig, + reactRouterConfig, + routeChunkCache, + routeConfig, + routeDiscovery, + routes, + rootRouteFile, + serverAppPath, + shouldDependOnWebCompiler, + splitRouteModules, + ssr, +}: CreateClassicModePlanOptions): Promise => { + const routeChunkConfig: RouteChunkConfig = { + splitRouteModules, + appDirectory, + rootRouteFile, + }; + const routeChunkOptions = { + splitRouteModules, + rootRouteFile, + isBuild, + cache: routeChunkCache, + }; + const { manifestChunkNames, webRouteEntries } = createClassicWebRouteEntries({ + appDirectory, + isBuild, + routes, + splitRouteModules: Boolean(splitRouteModules), + }); + const artifacts = await createClassicBuildArtifacts({ + api, + defaultEntryName, + isBuild, + prerenderConfig, + reactRouterConfig, + routeConfig, + routes, + rootDirectory: process.cwd(), + ssr, + devHmr, + }); + const reactRouterAliases = createReactRouterPackageAliases(); + return { + kind: 'classic', + artifacts, + routeChunkConfig, + routeChunkOptions, + manifestChunkNames, + webEntries: { + // `html: false` prevents rsbuild from emitting a stray entry.client.html + // into build/client; React Router renders HTML itself. + 'entry.client': { import: finalEntryClientPath, html: false }, + 'virtual/react-router/browser-manifest': { + import: 'virtual/react-router/browser-manifest', + html: false, + }, + ...webRouteEntries, + }, + nodeEntries: createReactRouterNodeEntries({ + hasServerApp, + isBuild, + serverAppPath, + entryServerPath, + defaultEntryName, + serverBundleEntries: artifacts.serverBundleEntries, + }), + createVirtualModules: (publicPath: string, _jsDistPath: string) => + createClassicVirtualModules({ + allowedActionOrigins: allowedActionOriginsForBuild, + appDirectory, + assetsBuildDirectory, + basename, + devHmrRuntimeModule: devHmr?.runtimeModule, + entryServerPath, + future, + isSpaMode, + prerenderPaths: artifacts.prerenderPaths, + publicPath, + routeDiscovery, + routes, + routesByServerBundleId: artifacts.routesByServerBundleId, + ssr, + }), + createResolveConfig: () => ({ alias: reactRouterAliases }), + server: customServer + ? undefined + : { + setup: [ + createReactRouterDevServerSetup({ + loadBuild: artifacts.devRuntime.createBuildLoader(), + }), + ], + }, + webExternalsType: 'module', + webOutput: { + chunkFormat: 'module', + chunkLoading: 'import', + workerChunkLoading: 'import', + wasmLoading: 'fetch', + library: { type: 'module' }, + module: true, + }, + webOptimization: { + avoidEntryIife: true, + runtimeChunk: 'single', + }, + nodeExternals: Array.from( + new Set(['express', ...getSsrExternals(process.cwd())]) + ), + nodeDependencies: shouldDependOnWebCompiler + ? { dependencies: ['web'] } + : {}, + }; +}; + +export const createReactRouterModePlan = ( + options: CreateReactRouterModePlanOptions +): Promise => + options.isRscMode + ? createRscModePlan(options) + : createClassicModePlan(options); diff --git a/src/modify-browser-manifest.ts b/src/modify-browser-manifest.ts index 21c6d99d..f85026ff 100644 --- a/src/modify-browser-manifest.ts +++ b/src/modify-browser-manifest.ts @@ -1,11 +1,16 @@ +import { createHash } from 'node:crypto'; import type { Route, PluginOptions } from './types.js'; import type { RsbuildPluginAPI, Rspack } from '@rsbuild/core'; import { + createReactRouterManifestOptions, createReactRouterManifestStats, generateReactRouterManifestForDev, getReactRouterManifestChunkNames, - getReactRouterManifestForDev, getReactRouterManifestPath, + type ReactRouterManifestForDev as ReactRouterManifest, + type RouteChunkManifestOptions, + type RouteManifestModuleExports, + type RouteModuleAnalysisProvider, } from './manifest.js'; import { combineURLs } from './plugin-utils.js'; import jsesc from 'jsesc'; @@ -21,6 +26,7 @@ type StatsWithIntegrity = { type CompilationAssetWithIntegrity = { name: string; + source?: { source(): string | Buffer }; info?: { integrity?: unknown; }; @@ -33,15 +39,14 @@ type CompilationWithIntegrityAssets = | Pick; type ModifyBrowserManifestOptions = { - future?: { unstable_subResourceIntegrity?: boolean }; subResourceIntegrity?: boolean; + future?: { unstable_subResourceIntegrity?: boolean }; manifestChunkNames?: ReadonlySet; + routeModuleAnalysis?: RouteModuleAnalysisProvider; onManifest?: ( - manifest: Awaited>, + manifest: ReactRouterManifest, sri: Record | true | undefined, - moduleExportsByRouteId: Awaited< - ReturnType - >['moduleExportsByRouteId'], + moduleExportsByRouteId: RouteManifestModuleExports, context: { compilation: Rspack.Compilation; manifestStats: ReturnType; @@ -51,18 +56,9 @@ type ModifyBrowserManifestOptions = { type ProcessAssetsApi = Pick; type AssetPrefixInput = string | (() => string); -type ReactRouterManifest = Awaited< - ReturnType ->; -type RouteManifestModuleExports = Awaited< - ReturnType ->['moduleExportsByRouteId']; -type GeneratedManifest = { - manifest: ReactRouterManifest; - moduleExportsByRouteId: RouteManifestModuleExports; - manifestStats: ReturnType; - assetPrefix: string; -}; +type ManifestProcessAssetsContext = Parameters< + Parameters[1] +>[0]; const BROWSER_MANIFEST_ASSET = 'static/js/virtual/react-router/browser-manifest.js'; @@ -85,12 +81,27 @@ const addIntegrity = ( assetName: unknown, integrity: unknown ) => { - if (typeof assetName !== 'string' || typeof integrity !== 'string') { + if ( + typeof assetName !== 'string' || + !assetName.endsWith('.js') || + typeof integrity !== 'string' + ) { return; } sri[toManifestAssetUrl(assetPrefix, assetName)] = integrity; }; +const computeSubresourceIntegrity = ( + source: CompilationAssetWithIntegrity['source'] +): string | undefined => { + if (!source) { + return undefined; + } + return `sha384-${createHash('sha384') + .update(source.source()) + .digest('base64')}`; +}; + export const collectSubresourceIntegrity = ( stats: StatsWithIntegrity | undefined, compilation: CompilationWithIntegrityAssets | undefined, @@ -106,7 +117,15 @@ export const collectSubresourceIntegrity = ( const assets = compilation.getAssets() as readonly CompilationAssetWithIntegrity[]; for (const asset of assets) { - addIntegrity(sri, assetPrefix, asset.name, asset.info?.integrity); + if (!asset.name.endsWith('.js')) { + continue; + } + addIntegrity( + sri, + assetPrefix, + asset.name, + computeSubresourceIntegrity(asset.source) ?? asset.info?.integrity + ); } } @@ -119,7 +138,7 @@ export function registerModifyBrowserManifestAssets( pluginOptions: PluginOptions, appDirectory: string, assetPrefix: AssetPrefixInput = '/', - routeChunkOptions?: Parameters[5], + routeChunkOptions?: RouteChunkManifestOptions, options?: ModifyBrowserManifestOptions ): void { const getAssetPrefix = @@ -130,117 +149,104 @@ export function registerModifyBrowserManifestAssets( routes, routeChunkOptions?.splitRouteModules ); + const isBuild = Boolean(routeChunkOptions?.isBuild); const finalizeSri = Boolean( - routeChunkOptions?.isBuild && + isBuild && (options?.subResourceIntegrity ?? options?.future?.unstable_subResourceIntegrity) ); - const generatedManifests = finalizeSri - ? new WeakMap() - : undefined; - - api.processAssets( - { stage: 'additions', environments: ['web'] }, - async ({ assets, sources, compilation }) => { - const currentAssetPrefix = getAssetPrefix(); - const stats = createReactRouterManifestStats( - compilation, - manifestChunkNames + + // Build the React Router manifest from the compilation's current asset names, + // emit the build-mode `manifest-.js` asset (or replace the dev + // browser-manifest placeholder), and fire the `onManifest` callback. + const buildAndEmitManifest = async ( + { assets, sources, compilation }: ManifestProcessAssetsContext, + { withSri }: { withSri: boolean } + ): Promise => { + const currentAssetPrefix = getAssetPrefix(); + const stats = createReactRouterManifestStats( + compilation, + manifestChunkNames + ); + const { manifest, moduleExportsByRouteId } = + await generateReactRouterManifestForDev( + routes, + pluginOptions, + stats, + appDirectory, + currentAssetPrefix, + createReactRouterManifestOptions({ + routeChunks: routeChunkOptions, + routeModuleAnalysis: options?.routeModuleAnalysis, + }) ); - const { manifest, moduleExportsByRouteId } = - await generateReactRouterManifestForDev( - routes, - pluginOptions, - stats, - appDirectory, - currentAssetPrefix, - routeChunkOptions - ); - const manifestForBrowser = finalizeSri - ? { ...manifest, sri: true as const } - : manifest; - - const browserManifestAsset = assets[BROWSER_MANIFEST_ASSET]; - if (browserManifestAsset) { - const originalSource = browserManifestAsset.source().toString(); - const serializedManifest = jsesc(manifestForBrowser, { es6: true }); - const newSource = originalSource.replace( - /["'`]PLACEHOLDER["'`]/, - () => serializedManifest - ); - compilation.updateAsset( - BROWSER_MANIFEST_ASSET, - new sources.RawSource(newSource) - ); - } + const browserManifest = { ...manifest }; + delete browserManifest.sri; + + const browserManifestAsset = assets[BROWSER_MANIFEST_ASSET]; + if (browserManifestAsset) { + const originalSource = browserManifestAsset.source().toString(); + const serializedManifest = jsesc(browserManifest, { es6: true }); + const newSource = originalSource.replace( + /["'`]PLACEHOLDER["'`]/, + () => serializedManifest + ); + compilation.updateAsset( + BROWSER_MANIFEST_ASSET, + new sources.RawSource(newSource) + ); + } - if (routeChunkOptions?.isBuild) { - const entryAssets = stats?.assetsByChunkName?.['entry.client']; - const entryJsAssets = - entryAssets?.filter(asset => asset.endsWith('.js')) || []; - const manifestPath = getReactRouterManifestPath({ - version: manifest.version, - isBuild: true, - entryModulePath: entryJsAssets[0], - }); - const manifestSource = `window.__reactRouterManifest=${jsesc( - manifestForBrowser, - { es6: true } - )};`; - const source = new sources.RawSource(manifestSource); - if (compilation.getAsset(manifestPath)) { - compilation.updateAsset(manifestPath, source); - } else { - compilation.emitAsset(manifestPath, source); - } + if (isBuild) { + const entryAssets = stats?.assetsByChunkName?.['entry.client']; + const entryJsAssets = + entryAssets?.filter(asset => asset.endsWith('.js')) || []; + const manifestPath = getReactRouterManifestPath({ + version: manifest.version, + isBuild: true, + entryModulePath: entryJsAssets[0], + }); + const manifestSource = `window.__reactRouterManifest=${jsesc( + browserManifest, + { es6: true } + )};`; + const source = new sources.RawSource(manifestSource); + if (compilation.getAsset(manifestPath)) { + compilation.updateAsset(manifestPath, source); + } else { + compilation.emitAsset(manifestPath, source); } + } - if (generatedManifests) { - generatedManifests.set(compilation, { - manifest, - moduleExportsByRouteId, - manifestStats: stats, - assetPrefix: currentAssetPrefix, - }); - return; - } + // Rspack's SRI stats are finalized by a later report-stage hook. Hash the + // finalized JavaScript sources here as a fallback, matching React Router's + // Vite integration. Browser manifests intentionally omit `sri`; hydration + // receives it from the server-rendered import map. + const sri = withSri + ? (collectSubresourceIntegrity( + compilation.getStats().toJson({ + all: false, + assets: true, + }) as StatsWithIntegrity, + compilation, + currentAssetPrefix + ) ?? true) + : undefined; + + options?.onManifest?.(manifest, sri, moduleExportsByRouteId, { + compilation, + manifestStats: stats, + }); + }; - options?.onManifest?.(manifest, undefined, moduleExportsByRouteId, { - compilation, - manifestStats: stats, - }); - } + // In production, Rspack renames content-hashed assets during + // `realContentHash` (PROCESS_ASSETS_STAGE_OPTIMIZE_HASH = 2500). Reading + // asset names before that stage yields pre-rename hashes that never appear + // in the output, so CSS/JS URLs in the manifest 404. In dev, CSS extraction + // can also attach entry CSS after `additions`. Defer manifest generation and + // emission to the `report` stage (PROCESS_ASSETS_STAGE_REPORT = 5000), which + // runs after the rename, late CSS attachment, and SRI integrity finalization. + api.processAssets({ stage: 'report', environments: ['web'] }, context => + buildAndEmitManifest(context, { withSri: finalizeSri }) ); - - if (generatedManifests) { - api.processAssets( - { stage: 'report', environments: ['web'] }, - ({ compilation }) => { - const generatedManifest = generatedManifests.get(compilation); - if (!generatedManifest) { - return; - } - - generatedManifests.delete(compilation); - const sri = - collectSubresourceIntegrity( - undefined, - compilation, - generatedManifest.assetPrefix - ) ?? true; - options?.onManifest?.( - { - ...generatedManifest.manifest, - sri, - }, - sri, - generatedManifest.moduleExportsByRouteId, - { - compilation, - manifestStats: generatedManifest.manifestStats, - } - ); - } - ); - } } diff --git a/src/parallel-route-transforms.ts b/src/parallel-route-transforms.ts index 06adae3e..21a2a4f9 100644 --- a/src/parallel-route-transforms.ts +++ b/src/parallel-route-transforms.ts @@ -1,7 +1,12 @@ import { Worker } from 'node:worker_threads'; +import * as Effect from 'effect/Effect'; +import type * as Scope from 'effect/Scope'; import { setBoundedCacheEntry } from './bounded-cache.js'; import { getAvailableCpuCount, getDefaultConcurrency } from './concurrency.js'; -import { normalizeEffectError } from './effect-runtime.js'; +import { + normalizeEffectError, + tryPluginPromise, +} from './effect-runtime.js'; import { executeRouteTransformTask, type RouteTransformResult, @@ -401,22 +406,10 @@ class ParallelRouteTransformExecutor implements RouteTransformExecutor { } } -export const createRouteTransformExecutor = ({ - parallelRouteTransform, - routeChunkCache, - splitRouteModules, - isBuild, -}: RouteTransformExecutorOptions = {}): RouteTransformExecutor => { - return createRouteTransformExecutorWithWorkerFactory( - { - parallelRouteTransform, - routeChunkCache, - splitRouteModules, - isBuild, - }, - createDefaultWorker - ); -}; +export const createRouteTransformExecutor = ( + options: RouteTransformExecutorOptions = {} +): RouteTransformExecutor => + createRouteTransformExecutorWithWorkerFactory(options, createDefaultWorker); const createRouteTransformExecutorWithWorkerFactory = ( { @@ -456,3 +449,28 @@ export const createRouteTransformExecutorForTesting = ( createWorker: RouteTransformWorkerFactory ): RouteTransformExecutor => createRouteTransformExecutorWithWorkerFactory(options, createWorker); + +const acquireRouteTransformExecutorWithWorkerFactory = Effect.fn( + 'RouteTransformExecutor.acquire' +)(function* ( + options: RouteTransformExecutorOptions, + createWorker: RouteTransformWorkerFactory +) { + return yield* Effect.acquireRelease( + Effect.sync(() => + createRouteTransformExecutorWithWorkerFactory(options, createWorker) + ), + executor => Effect.ignore(tryPluginPromise(() => executor.close())) + ); +}); + +export const acquireRouteTransformExecutor = ( + options: RouteTransformExecutorOptions = {} +): Effect.Effect => + acquireRouteTransformExecutorWithWorkerFactory(options, createDefaultWorker); + +export const acquireRouteTransformExecutorForTesting = ( + options: RouteTransformExecutorOptions, + createWorker: RouteTransformWorkerFactory +): Effect.Effect => + acquireRouteTransformExecutorWithWorkerFactory(options, createWorker); diff --git a/src/plugin-utils.ts b/src/plugin-utils.ts index 51561985..db6d343e 100644 --- a/src/plugin-utils.ts +++ b/src/plugin-utils.ts @@ -1,7 +1,71 @@ -import { normalize } from 'pathe'; -import { existsSync } from 'node:fs'; +import { normalize, resolve } from 'pathe'; +import { existsSync, readFileSync } from 'node:fs'; +import { createRequire } from 'node:module'; import { JS_EXTENSIONS } from './constants.js'; +const requireFromApp = createRequire(resolve(process.cwd(), 'package.json')); + +export const resolveAppPackagePath = ( + specifier: string +): string | undefined => { + try { + return requireFromApp.resolve(specifier); + } catch { + return undefined; + } +}; + +export const parseVersionMajorMinor = ( + version: string | undefined +): { major: number; minor: number } | undefined => { + const match = version?.match(/^(\d+)\.(\d+)\./); + if (!match) { + return undefined; + } + return { major: Number(match[1]), minor: Number(match[2]) }; +}; + +const packageVersionCache = new Map(); + +export const getPackageVersion = ( + packageName: string, + resolvePackagePath: ( + specifier: string + ) => string | undefined = resolveAppPackagePath +): string | undefined => { + // Only default resolution is cached; injected resolvers (tests) stay live. + const cacheable = resolvePackagePath === resolveAppPackagePath; + if (cacheable && packageVersionCache.has(packageName)) { + return packageVersionCache.get(packageName); + } + const packageJsonPath = resolvePackagePath(`${packageName}/package.json`); + if (!packageJsonPath) { + return undefined; + } + try { + const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { + version?: unknown; + }; + const version = + typeof packageJson.version === 'string' ? packageJson.version : undefined; + if (cacheable) { + packageVersionCache.set(packageName, version); + } + return version; + } catch { + if (cacheable) { + packageVersionCache.set(packageName, undefined); + } + return undefined; + } +}; + +export const escapeHtml = (value: string): string => + value + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>'); + export function combineURLs(baseURL: string, relativeURL: string): string { return relativeURL ? `${baseURL.replace(/\/+$/, '')}/${relativeURL.replace(/^\/+/, '')}` @@ -15,12 +79,38 @@ export function normalizeAssetPrefix(assetPrefix?: string): string { return assetPrefix.endsWith('/') ? assetPrefix : `${assetPrefix}/`; } -export function stripFileExtension(file: string): string { - return file.replace(/\.[^/.]+$/, ''); +/** + * Resolve the asset prefix Rsbuild applies to emitted asset URLs for the given + * mode. In development the effective prefix is `dev.assetPrefix` (which Rsbuild + * defaults from `server.base`, falling back to `output.assetPrefix`); in a + * production build it is `output.assetPrefix`. Both fields are already resolved + * on the normalized config, so this mirrors Rsbuild's own precedence rather than + * re-deriving it from `server.base`. + * + * `dev.assetPrefix` may be a boolean on the raw config (`false` disables it); + * boolean/`'auto'`/empty values normalize to the root prefix `'/'`. + */ +export function resolveEffectiveAssetPrefix(config: { + dev?: { assetPrefix?: unknown }; + output?: { assetPrefix?: unknown }; + isBuild: boolean; +}): string { + const outputPrefix = + typeof config.output?.assetPrefix === 'string' + ? config.output.assetPrefix + : undefined; + if (config.isBuild) { + return normalizeAssetPrefix(outputPrefix); + } + const devPrefix = + typeof config.dev?.assetPrefix === 'string' + ? config.dev.assetPrefix + : undefined; + return normalizeAssetPrefix(devPrefix ?? outputPrefix); } export function createRouteId(file: string): string { - return normalize(stripFileExtension(file)); + return normalize(file.replace(/\.[^/.]+$/, '')); } export function findEntryFile(basePath: string): string { @@ -35,41 +125,15 @@ export function findEntryFile(basePath: string): string { export function generateWithProps() { return ` - import { createElement as h } from "react"; - import { useActionData, useLoaderData, useMatches, useParams, useRouteError } from "react-router"; + import { + UNSAFE_withComponentProps, + UNSAFE_withErrorBoundaryProps, + UNSAFE_withHydrateFallbackProps, + } from "react-router"; - export function withComponentProps(Component) { - return function Wrapped() { - const props = { - params: useParams(), - loaderData: useLoaderData(), - actionData: useActionData(), - matches: useMatches(), - }; - return h(Component, props); - }; - } - - export function withHydrateFallbackProps(HydrateFallback) { - return function Wrapped() { - const props = { - params: useParams(), - }; - return h(HydrateFallback, props); - }; - } - - export function withErrorBoundaryProps(ErrorBoundary) { - return function Wrapped() { - const props = { - params: useParams(), - loaderData: useLoaderData(), - actionData: useActionData(), - error: useRouteError(), - }; - return h(ErrorBoundary, props); - }; - } + export const withComponentProps = UNSAFE_withComponentProps; + export const withHydrateFallbackProps = UNSAFE_withHydrateFallbackProps; + export const withErrorBoundaryProps = UNSAFE_withErrorBoundaryProps; `; } diff --git a/src/prerender-build.ts b/src/prerender-build.ts index d6845998..7c6e18e2 100644 --- a/src/prerender-build.ts +++ b/src/prerender-build.ts @@ -10,12 +10,15 @@ import { type ServerBuild, } from 'react-router'; import { dirname, relative, resolve } from 'pathe'; -import { PLUGIN_NAME } from './constants.js'; +import { PLUGIN_NAME, SPA_FALLBACK_HTML_FILE } from './constants.js'; import { getBuildManifest } from './build-manifest.js'; import { + createReactRouterManifestOptions, generateReactRouterManifestForDev, - getReactRouterManifestForDev, + type ReactRouterManifestForDev as ReactRouterManifest, type ReactRouterManifestStats, + type RouteChunkManifestOptions, + type RouteModuleAnalysisProvider, type RouteManifestModuleExports, } from './manifest.js'; import { @@ -32,10 +35,6 @@ import { resolveServerBuildModule } from './server-utils.js'; import type { PluginOptions, Route } from './types.js'; import { runPluginEffect, tryPluginPromise } from './effect-runtime.js'; -type ReactRouterManifest = Awaited< - ReturnType ->; - type BuildRouteModule = { loader?: unknown; default?: unknown; @@ -73,9 +72,10 @@ type RunReactRouterPrerenderBuildOptions = { pluginOptions: PluginOptions; appDirectory: string; assetPrefix: string; - routeChunkOptions: Parameters[5]; + routeChunkOptions: RouteChunkManifestOptions | undefined; + routeModuleAnalysis?: RouteModuleAnalysisProvider; buildManifest: Awaited>; - resolvedConfigWithRoutes: ResolvedReactRouterConfig; + buildEndReactRouterConfig: ResolvedReactRouterConfig; buildEnd: Config['buildEnd']; }; @@ -160,7 +160,7 @@ export const withBuildRequest = ( handle: (request: Request) => Promise ): Promise => runPluginEffect(createBuildRequestEffect(input, init, handle)); -const prerenderData = async ({ +const prerenderDataEffect = ({ handler, prerenderPath, onlyRoutes, @@ -178,7 +178,7 @@ const prerenderData = async ({ trailingSlashAwareDataRequests: boolean; api: PrerenderBuildApi; requestInit?: RequestInit; -}): Promise => { +}) => { const dataOutputPath = createDataRequestPath( prerenderPath, trailingSlashAwareDataRequests @@ -199,7 +199,7 @@ const prerenderData = async ({ url.searchParams.set('_routes', onlyRoutes.join(',')); } - return withBuildRequest(url, requestInit, async request => { + return createBuildRequestEffect(url, requestInit, async request => { const response = await handler(request); const data = await response.text(); @@ -227,7 +227,7 @@ const prerenderData = async ({ }); }; -const prerenderRoute = async ({ +const prerenderRouteEffect = ({ handler, prerenderPath, clientBuildDir, @@ -241,9 +241,9 @@ const prerenderRoute = async ({ basename: string; api: PrerenderBuildApi; requestInit?: RequestInit; -}): Promise => { +}) => { const normalizedPath = `${basename}${prerenderPath}/`.replace(/\/\/+/g, '/'); - await withBuildRequest( + return createBuildRequestEffect( `http://localhost${normalizedPath}`, requestInit, async request => { @@ -290,7 +290,7 @@ const prerenderRoute = async ({ ); }; -const prerenderResourceRoute = async ({ +const prerenderResourceRouteEffect = ({ handler, prerenderPath, clientBuildDir, @@ -304,11 +304,11 @@ const prerenderResourceRoute = async ({ basename: string; api: PrerenderBuildApi; requestInit?: RequestInit; -}): Promise => { +}) => { const normalizedPath = `${basename}${prerenderPath}/` .replace(/\/\/+/g, '/') .replace(/\/$/g, ''); - await withBuildRequest( + return createBuildRequestEffect( `http://localhost${normalizedPath}`, requestInit, async request => { @@ -361,7 +361,7 @@ const handleSpaMode = async ({ const html = await response.text(); const isPrerenderSpaFallback = build.prerender?.includes('/'); const filename = isPrerenderSpaFallback - ? '__spa-fallback.html' + ? SPA_FALLBACK_HTML_FILE : 'index.html'; if (response.status !== 200) { @@ -374,7 +374,7 @@ const handleSpaMode = async ({ } throw new Error( `SPA Mode: Received a ${response.status} status code from ` + - `\`entry.server.tsx\` while prerendering your \`${filename}\` file.\n` + + '`entry.server.tsx` while prerendering your SPA Fallback HTML file.\n' + html ); } @@ -443,10 +443,10 @@ const validatePrerenderPathMatches = ( } }; -export const createBoundedPrerenderTasksEffect = ( - prerenderPaths: string[], +export const createBoundedPrerenderTasksEffect = ( + prerenderPaths: T[], concurrency: number, - renderPath: (path: string) => Effect.Effect + renderPath: (path: T) => Effect.Effect ): Effect.Effect => Effect.forEach(prerenderPaths, renderPath, { concurrency, discard: true }); @@ -480,27 +480,23 @@ const createPrerenderPathEffect = ({ if (isResourceRoute) { if (manifestRoute.loader && routeId) { - yield* tryPluginPromise(() => - prerenderData({ - handler: requestHandler, - prerenderPath: path, - onlyRoutes: [routeId], - clientBuildDir, - basename, - trailingSlashAwareDataRequests: - future.unstable_trailingSlashAwareDataRequests, - api, - }) - ); - yield* tryPluginPromise(() => - prerenderResourceRoute({ - handler: requestHandler, - prerenderPath: path, - clientBuildDir, - basename, - api, - }) - ); + yield* prerenderDataEffect({ + handler: requestHandler, + prerenderPath: path, + onlyRoutes: [routeId], + clientBuildDir, + basename, + trailingSlashAwareDataRequests: + future.unstable_trailingSlashAwareDataRequests, + api, + }); + yield* prerenderResourceRouteEffect({ + handler: requestHandler, + prerenderPath: path, + clientBuildDir, + basename, + api, + }); } else { yield* Effect.sync(() => { api.logger.warn( @@ -519,30 +515,26 @@ const createPrerenderPathEffect = ({ return build.assets?.routes?.[matchedRouteId]?.hasLoader; }); const data = hasLoaders - ? yield* tryPluginPromise(() => - prerenderData({ - handler: requestHandler, - prerenderPath: path, - onlyRoutes: null, - clientBuildDir, - basename, - trailingSlashAwareDataRequests: - future.unstable_trailingSlashAwareDataRequests, - api, - }) - ) + ? yield* prerenderDataEffect({ + handler: requestHandler, + prerenderPath: path, + onlyRoutes: null, + clientBuildDir, + basename, + trailingSlashAwareDataRequests: + future.unstable_trailingSlashAwareDataRequests, + api, + }) : undefined; - yield* tryPluginPromise(() => - prerenderRoute({ - handler: requestHandler, - prerenderPath: path, - clientBuildDir, - basename, - api, - requestInit: data ? createPrerenderDataRequestInit(data) : undefined, - }) - ); + yield* prerenderRouteEffect({ + handler: requestHandler, + prerenderPath: path, + clientBuildDir, + basename, + api, + requestInit: data ? createPrerenderDataRequestInit(data) : undefined, + }); }); const createPrerenderDataRequestInit = ( @@ -558,35 +550,6 @@ const createPrerenderDataRequestInit = ( : undefined; }; -const runPrerenderPaths = async ({ - build, - requestHandler, - clientBuildDir, - options, -}: { - build: PrerenderServerBuild; - requestHandler: (request: Request) => Promise; - clientBuildDir: string; - options: RunReactRouterPrerenderBuildOptions; -}): Promise => { - const { prerenderConfig, prerenderPaths } = options; - const buildRoutes = createPrerenderRoutes(build.routes); - const concurrency = getPrerenderConcurrency(prerenderConfig); - - await runPluginEffect( - createBoundedPrerenderTasksEffect(prerenderPaths, concurrency, path => - createPrerenderPathEffect({ - path, - build, - buildRoutes, - requestHandler, - clientBuildDir, - options, - }) - ) - ); -}; - export const runReactRouterPrerenderBuild = async ( options: RunReactRouterPrerenderBuildOptions ): Promise => { @@ -600,6 +563,7 @@ export const runReactRouterPrerenderBuild = async ( serverBuildFile, ssr, isPrerenderEnabled, + prerenderConfig, prerenderPaths, routes, latestBrowserManifest, @@ -609,8 +573,9 @@ export const runReactRouterPrerenderBuild = async ( appDirectory, assetPrefix, routeChunkOptions, + routeModuleAnalysis, buildManifest, - resolvedConfigWithRoutes, + buildEndReactRouterConfig, buildEnd, basename, } = options; @@ -652,7 +617,10 @@ export const runReactRouterPrerenderBuild = async ( clientStats, appDirectory, assetPrefix, - routeChunkOptions + createReactRouterManifestOptions({ + routeChunks: routeChunkOptions, + routeModuleAnalysis, + }) ); assertValidSsrFalsePrerenderExports({ routes, @@ -671,12 +639,22 @@ export const runReactRouterPrerenderBuild = async ( ); } - await runPrerenderPaths({ - build, - requestHandler, - clientBuildDir, - options, - }); + const buildRoutes = createPrerenderRoutes(build.routes); + await runPluginEffect( + createBoundedPrerenderTasksEffect( + prerenderPaths, + getPrerenderConcurrency(prerenderConfig), + path => + createPrerenderPathEffect({ + path, + build, + buildRoutes, + requestHandler, + clientBuildDir, + options, + }) + ) + ); } if (!ssr) { @@ -700,8 +678,8 @@ export const runReactRouterPrerenderBuild = async ( if (buildEnd) { await buildEnd({ buildManifest, - reactRouterConfig: resolvedConfigWithRoutes, - viteConfig: api.getNormalizedConfig(), + reactRouterConfig: buildEndReactRouterConfig, + rsbuildConfig: api.getNormalizedConfig(), }); } }; diff --git a/src/prerender.ts b/src/prerender.ts index 3b3813f7..86b85a27 100644 --- a/src/prerender.ts +++ b/src/prerender.ts @@ -1,4 +1,5 @@ import type { Config } from './react-router-config.js'; +import type { PrerenderConfigObject, PrerenderPathsConfig } from './types.js'; import type { RouteConfigEntry } from '@react-router/dev/routes'; import { matchRoutes } from 'react-router'; @@ -6,20 +7,6 @@ type ReactRouterPrerenderConfig = Config['prerender']; type MatchRouteObject = Parameters[0] extends Array ? R : never; -type PrerenderPathsConfig = - | boolean - | string[] - | ((args: { - getStaticPaths: () => string[]; - }) => boolean | string[] | Promise); - -type PrerenderConfigObject = Extract< - NonNullable, - { paths: unknown } -> & { - unstable_concurrency?: number; -}; - type PrerenderConfig = ReactRouterPrerenderConfig | PrerenderConfigObject; type PrerenderConcurrencyConfig = | { @@ -78,7 +65,11 @@ export const createPrerenderRoutes = ( grouped: Record = groupRoutesByParentId(manifest) ): MatchRouteObject[] => { return (grouped[parentId] || []).map(route => { - const common = { id: route.id, path: route.path }; + const common = { + id: route.id, + path: route.path, + caseSensitive: route.caseSensitive, + }; if (route.index) { return { index: true, ...common } as MatchRouteObject; } @@ -120,10 +111,9 @@ export const getSsrFalsePrerenderExportErrors = ({ const errors: string[] = []; for (const [routeId, route] of Object.entries(manifestRoutes)) { const exports = routeExports[routeId] ?? []; - const invalidApis: string[] = []; - - if (exports.includes('headers')) invalidApis.push('headers'); - if (exports.includes('action')) invalidApis.push('action'); + const invalidApis = ['headers', 'action', 'middleware'].filter(api => + exports.includes(api) + ); if (invalidApis.length > 0) { errors.push( @@ -143,11 +133,17 @@ export const getSsrFalsePrerenderExportErrors = ({ ); } + // A loader on an ancestor route is only invalid when the ancestor is not + // itself fully prerendered. The root route is exempt: its loader always + // runs to render the app shell and it is always part of the prerender set + // whenever any path is prerendered, so upstream never flags it here. let parentRoute = - route.parentId && manifestRoutes[route.parentId] + route.parentId && + route.parentId !== 'root' && + manifestRoutes[route.parentId] ? manifestRoutes[route.parentId] : null; - while (parentRoute) { + while (parentRoute && parentRoute.id !== 'root') { if (parentRoute.hasLoader && !parentRoute.hasClientLoader) { errors.push( `Prerender: 1 invalid route export in \`${parentRoute.id}\` when ` + @@ -156,7 +152,9 @@ export const getSsrFalsePrerenderExportErrors = ({ ); } parentRoute = - parentRoute.parentId && manifestRoutes[parentRoute.parentId] + parentRoute.parentId && + parentRoute.parentId !== 'root' && + manifestRoutes[parentRoute.parentId] ? manifestRoutes[parentRoute.parentId] : null; } @@ -247,8 +245,9 @@ export const resolvePrerenderPaths = async ( }); warn( [ - 'Warning: Paths with dynamic/splat params cannot be prerendered when using `prerender: true`.', - 'You may want to use the `prerender()` API to prerender the following paths:', + '⚠️ Paths with dynamic/splat params cannot be prerendered when ' + + 'using `prerender: true`. You may want to use the `prerender()` ' + + 'API to prerender the following paths:', ...paramRoutes.map(path => ` - ${path.replace(/^\/(?=[:*])/, '')}`), ].join('\n') ); diff --git a/src/react-router-config.ts b/src/react-router-config.ts index eb8d79bf..c969b270 100644 --- a/src/react-router-config.ts +++ b/src/react-router-config.ts @@ -6,13 +6,15 @@ import type { NormalizedConfig } from '@rsbuild/core'; import type { RouteConfigEntry } from '@react-router/dev/routes'; import * as Effect from 'effect/Effect'; import { getCappedPluginConcurrency } from './concurrency.js'; -import { runPluginEffect, tryPluginPromise } from './effect-runtime.js'; +import { normalizeEffectError, tryPluginPromise } from './effect-runtime.js'; +import { getPackageVersion, parseVersionMajorMinor } from './plugin-utils.js'; +import type { PrerenderConfigObject, PrerenderPathsConfig } from './types.js'; export type BuildEndHook = { bivarianceHack(args: { buildManifest: ReactRouterBuildManifest | undefined; reactRouterConfig: ResolvedReactRouterConfig; - viteConfig: NormalizedConfig; + rsbuildConfig: NormalizedConfig; }): void | Promise; }['bivarianceHack']; @@ -20,10 +22,15 @@ type SplitRouteModulesConfig = boolean | 'enforce'; export type Config = Omit< ReactRouterConfig, - 'buildEnd' | 'future' | 'splitRouteModules' | 'subResourceIntegrity' + | 'buildEnd' + | 'future' + | 'prerender' + | 'splitRouteModules' + | 'subResourceIntegrity' > & { buildEnd?: BuildEndHook; future?: Partial; + prerender?: PrerenderConfig; splitRouteModules?: SplitRouteModulesConfig; subResourceIntegrity?: boolean; }; @@ -37,6 +44,8 @@ type FutureConfig = { v8_viteEnvironmentApi: boolean; }; +type PrerenderConfig = PrerenderPathsConfig | PrerenderConfigObject | undefined; + type RouteManifestEntry = { id: string; parentId?: string; @@ -50,10 +59,45 @@ type RouteManifest = Record; type ResolveReactRouterConfigResult = { resolved: ResolvedReactRouterConfig; + userAndPresetConfig: Config; presets: NonNullable; hasConfiguredServerModuleFormat: boolean; }; +export const getDefaultTrailingSlashAwareDataRequests = ( + reactRouterVersion: string | undefined = getPackageVersion('react-router') +): boolean => (parseVersionMajorMinor(reactRouterVersion)?.major ?? 0) >= 8; + +export const resolveRouteDiscoveryConfig = ({ + ssr, + userRouteDiscovery, +}: { + ssr: boolean; + userRouteDiscovery: Config['routeDiscovery']; +}): Config['routeDiscovery'] => { + if (!userRouteDiscovery) { + return ssr + ? ({ mode: 'lazy', manifestPath: '/__manifest' } as const) + : ({ mode: 'initial' } as const); + } + if (userRouteDiscovery.mode === 'initial') { + return userRouteDiscovery; + } + + if (!ssr) { + throw new Error( + 'The `routeDiscovery.mode` config cannot be set to "lazy" when setting `ssr:false`' + ); + } + const manifestPath = userRouteDiscovery.manifestPath; + if (manifestPath && !manifestPath.startsWith('/')) { + throw new Error( + 'The `routeDiscovery.manifestPath` config must be a root-relative pathname beginning with a slash (i.e., "/__manifest")' + ); + } + return userRouteDiscovery; +}; + export type ResolvedReactRouterConfig = Readonly<{ appDirectory: string; basename: string; @@ -73,6 +117,16 @@ export type ResolvedReactRouterConfig = Readonly<{ unstable_routeConfig: RouteConfigEntry[]; }>; +const createDefaultFutureConfig = (): FutureConfig => ({ + unstable_optimizeDeps: false, + unstable_subResourceIntegrity: false, + unstable_trailingSlashAwareDataRequests: + getDefaultTrailingSlashAwareDataRequests(), + v8_middleware: false, + v8_splitRouteModules: false, + v8_viteEnvironmentApi: false, +}); + const DEFAULT_CONFIG = { appDirectory: 'app', basename: '/', @@ -82,14 +136,7 @@ const DEFAULT_CONFIG = { splitRouteModules: true, subResourceIntegrity: false, ssr: true, - future: { - unstable_optimizeDeps: false, - unstable_subResourceIntegrity: false, - unstable_trailingSlashAwareDataRequests: false, - v8_middleware: false, - v8_splitRouteModules: false, - v8_viteEnvironmentApi: false, - } satisfies FutureConfig, + future: createDefaultFutureConfig(), routeDiscovery: undefined, prerender: undefined, serverBundles: undefined, @@ -111,15 +158,12 @@ const mergeReactRouterConfig = (...configs: Config[]): Config => { buildEnd: async ( ...args: Parameters> ) => { - await runPluginEffect( - Effect.all( - [ - tryPluginPromise(() => configA.buildEnd?.(...args)), - tryPluginPromise(() => configB.buildEnd?.(...args)), - ], - { discard: true } - ) - ); + try { + await configA.buildEnd?.(...args); + await configB.buildEnd?.(...args); + } catch (cause) { + throw normalizeEffectError(cause); + } }, } : {}), @@ -181,7 +225,8 @@ export const resolveReactRouterConfigEffect = ( reactRouterUserConfig; const presetConfig = yield* tryPluginPromise(() => preset.reactRouterConfig?.({ - reactRouterUserConfig: reactRouterUserConfigForPreset, + reactRouterUserConfig: + reactRouterUserConfigForPreset as ReactRouterConfig, }) ); if (!presetConfig) return null; @@ -233,13 +278,9 @@ export const resolveReactRouterConfigEffect = ( return { resolved, + userAndPresetConfig: userAndPresetConfigs, presets: reactRouterUserConfig.presets ?? [], hasConfiguredServerModuleFormat: userAndPresetConfigs.serverModuleFormat !== undefined, }; }); - -export const resolveReactRouterConfig = ( - reactRouterUserConfig: Config -): Promise => - runPluginEffect(resolveReactRouterConfigEffect(reactRouterUserConfig)); diff --git a/src/route-artifacts.ts b/src/route-artifacts.ts index 2c186dd8..575fc7e2 100644 --- a/src/route-artifacts.ts +++ b/src/route-artifacts.ts @@ -1,5 +1,3 @@ -import { basename } from 'pathe'; - import { CLIENT_EXPORTS, CLIENT_ROUTE_EXPORTS_SET, @@ -12,6 +10,7 @@ import { detectRouteChunksIfEnabled, emptyRouteChunkSnippet, getRouteChunkIfEnabled, + getRouteChunkModuleId, getRouteChunkNameFromModuleId, shouldAnalyzeRouteChunks, validateRouteChunks, @@ -50,9 +49,8 @@ type RouteChunkArtifact = { map: null; }; -// Exactly the route-manifest flags the client HMR runtime can patch in place -// without a full reload. Array order defines the bit layout shared by the -// encoder below and the decoder emitted in `generateDevHmrRuntimeModule`. +// Shared with the dev manifest differ: exactly the route flags the client HMR +// runtime can patch in place without a full reload. export const HMR_PATCHABLE_ROUTE_FLAGS = [ 'hasAction', 'hasClientAction', @@ -62,88 +60,104 @@ export const HMR_PATCHABLE_ROUTE_FLAGS = [ 'hasLoader', ] as const; -const HMR_FLAG_EXPORT_NAME: Record< +type RouteHmrMetadata = Record< (typeof HMR_PATCHABLE_ROUTE_FLAGS)[number], - string -> = { - hasAction: SERVER_EXPORTS.action, - hasClientAction: CLIENT_EXPORTS.clientAction, - hasClientLoader: CLIENT_EXPORTS.clientLoader, - hasClientMiddleware: CLIENT_EXPORTS.clientMiddleware, - hasErrorBoundary: CLIENT_EXPORTS.ErrorBoundary, - hasLoader: SERVER_EXPORTS.loader, -}; + boolean +>; -export const buildRouteHmrFlags = (exportNames: readonly string[]): number => { +export const buildRouteHmrMetadata = ( + exportNames: readonly string[] +): RouteHmrMetadata => { const exports = new Set(exportNames); - let flags = 0; - HMR_PATCHABLE_ROUTE_FLAGS.forEach((flag, index) => { - if (exports.has(HMR_FLAG_EXPORT_NAME[flag])) flags |= 1 << index; - }); - return flags; + return { + hasAction: exports.has(SERVER_EXPORTS.action), + hasClientAction: exports.has(CLIENT_EXPORTS.clientAction), + hasClientLoader: exports.has(CLIENT_EXPORTS.clientLoader), + hasClientMiddleware: exports.has(CLIENT_EXPORTS.clientMiddleware), + hasErrorBoundary: exports.has(CLIENT_EXPORTS.ErrorBoundary), + hasLoader: exports.has(SERVER_EXPORTS.loader), + }; }; /** * Development-only HMR glue for a web route client entry. * * The route client entry is the webpack entry for a route, so it must - * self-accept hot updates to stop them from bubbling into a full reload. It - * also accepts updates of the underlying route module and forwards fresh - * exports plus route metadata (loader/action flags derived from the current - * export names) to the shared HMR runtime, which applies the React Router - * route-module update contract and revalidates loader data. + * self-accept hot updates to stop them from bubbling into a full reload. The + * route entry itself must re-run on updates so route metadata (loader/action + * flags derived from current export names) does not get stuck in an old accept + * callback while the underlying route module changes. */ const buildRouteClientEntryHmrCode = ({ routeId, target, - acceptTarget, - flags, + metadata, }: { routeId: string; target: string; - acceptTarget: string; - flags: number; + metadata: RouteHmrMetadata; }): string => { const targetJson = JSON.stringify(target); - const acceptTargetJson = JSON.stringify(acceptTarget); return ` -import * as __rrm from ${targetJson}; +import * as __reactRouterRouteModule from ${targetJson}; import { - registerReactRouterRouteExports as __rrr, - scheduleReactRouterRouteUpdate as __rru, + registerReactRouterRouteExports as __reactRouterRegisterRouteExports, + scheduleReactRouterRouteUpdate as __reactRouterScheduleRouteUpdate, } from "virtual/react-router/hmr-runtime"; -const __rrid = ${JSON.stringify(routeId)}; -const __rrf = ${flags}; -const __rrg = () => __rrm; -const __rru0 = () => { - __rrr(__rrid, __rrm); - __rru(__rrid, __rrf, __rrg); -}; +const __reactRouterRouteId = ${JSON.stringify(routeId)}; +const __reactRouterRouteMetadata = ${JSON.stringify(metadata)}; +const __reactRouterGetRouteModule = () => __reactRouterRouteModule; -__rrr(__rrid, __rrm); +__reactRouterRegisterRouteExports( + __reactRouterRouteId, + __reactRouterRouteModule +); if (import.meta.webpackHot) { - const __rrh = import.meta.webpackHot; - __rrh.accept(${acceptTargetJson}, __rru0); - __rrh.accept(); - __rrh.dispose(data => { data.__rr = true; }); - if (__rrh.data && __rrh.data.__rr) __rru0(); + import.meta.webpackHot.accept(); + import.meta.webpackHot.dispose(data => { + data.__reactRouterRouteShim = true; + }); + if ( + import.meta.webpackHot.data && + import.meta.webpackHot.data.__reactRouterRouteShim + ) { + __reactRouterScheduleRouteUpdate( + __reactRouterRouteId, + __reactRouterRouteMetadata, + __reactRouterGetRouteModule + ); + } } `; }; -// The accept target is spelled relative (`./name?react-router-route`) while -// the import above uses the absolute resource path; both resolve to the same -// module because the client entry replaces the route file in place, so its -// resolution context is the route's own directory. -const createRouteHmrAcceptTarget = (resourcePath: string): string => { - return `./${basename(resourcePath)}?react-router-route`; +const shouldReexportFromRouteEntry = ({ + chunkedExportSet, + exportName, + isServer, +}: { + chunkedExportSet?: ReadonlySet; + exportName: string; + isServer: boolean; +}): boolean => { + if (chunkedExportSet?.has(exportName)) { + return false; + } + if (isServer) { + return ( + CLIENT_ROUTE_EXPORTS_SET.has(exportName) || + SERVER_ONLY_ROUTE_EXPORTS_SET.has(exportName) + ); + } + return CLIENT_ROUTE_EXPORTS_SET.has(exportName); }; export const buildRouteClientEntryCode = ({ exportNames, chunkedExports, + sharedChunkedExports = [], isServer, resourcePath, routeId, @@ -151,26 +165,39 @@ export const buildRouteClientEntryCode = ({ }: { exportNames: readonly string[]; chunkedExports: readonly string[]; + sharedChunkedExports?: readonly string[]; isServer: boolean; resourcePath: string; routeId?: string; devHmr?: boolean; }): string => { const chunkedExportSet = - chunkedExports.length > 0 ? new Set(chunkedExports) : undefined; + chunkedExports.length > 0 || sharedChunkedExports.length > 0 + ? new Set([...chunkedExports, ...sharedChunkedExports]) + : undefined; + const target = isServer + ? resourcePath + : chunkedExports.length > 0 + ? getRouteChunkModuleId(resourcePath, 'main') + : `${resourcePath}?react-router-route`; const reexports = exportNames - .filter(exp => { - if (chunkedExportSet?.has(exp)) { - return false; - } - return ( - CLIENT_ROUTE_EXPORTS_SET.has(exp) || - (isServer && SERVER_ONLY_ROUTE_EXPORTS_SET.has(exp)) - ); - }) + .filter(exportName => + shouldReexportFromRouteEntry({ chunkedExportSet, exportName, isServer }) + ) .sort(); - const target = `${resourcePath}?react-router-route`; - const reexportCode = `export { ${reexports.join(', ')} } from ${JSON.stringify(target)};`; + const reexportCode = [ + reexports.length > 0 + ? `export { ${reexports.join(', ')} } from ${JSON.stringify(target)};` + : null, + ...sharedChunkedExports.map( + exportName => + `export { ${exportName} } from ${JSON.stringify( + getRouteChunkModuleId(resourcePath, exportName) + )};` + ), + ] + .filter(Boolean) + .join('\n'); if (!devHmr || isServer || routeId === undefined) { return reexportCode; } @@ -179,8 +206,7 @@ export const buildRouteClientEntryCode = ({ buildRouteClientEntryHmrCode({ routeId, target, - acceptTarget: createRouteHmrAcceptTarget(resourcePath), - flags: buildRouteHmrFlags(exportNames), + metadata: buildRouteHmrMetadata(exportNames), }) ); }; @@ -211,10 +237,12 @@ export const createRouteClientEntryArtifact = async ({ const exportNames = routeChunkInfo?.exportNames ?? (await getExportNames(code, resourcePath)); const chunkedExports = routeChunkInfo?.chunkedExports ?? []; + const sharedChunkedExports = routeChunkInfo?.sharedChunkedExports ?? []; return { code: buildRouteClientEntryCode({ exportNames, chunkedExports, + sharedChunkedExports, isServer, resourcePath, routeId, diff --git a/src/route-chunks.ts b/src/route-chunks.ts index ce0be88b..fc0d140c 100644 --- a/src/route-chunks.ts +++ b/src/route-chunks.ts @@ -5,7 +5,8 @@ import { } from 'yuku-analyzer'; import { print } from 'yuku-codegen'; import { walk } from 'yuku-parser'; -import { normalize, relative, resolve } from 'pathe'; +import { dirname, normalize, relative, resolve } from 'pathe'; +import { SERVER_ONLY_ROUTE_EXPORTS_SET } from './constants.js'; type AnyNode = Record; @@ -15,7 +16,7 @@ export type RouteChunkExportName = | 'clientMiddleware' | 'HydrateFallback'; -export type RouteChunkName = 'main' | RouteChunkExportName; +export type RouteChunkName = 'main' | RouteChunkExportName | (string & {}); export type RouteChunkConfig = { splitRouteModules?: boolean | 'enforce'; @@ -35,6 +36,7 @@ export type RouteChunkInfo = { hasRouteChunks: boolean; hasRouteChunkByExportName: Record; chunkedExports: RouteChunkExportName[]; + sharedChunkedExports: string[]; }; export const routeChunkExportNames: RouteChunkExportName[] = [ @@ -63,7 +65,7 @@ export const emptyRouteChunkSnippet = (): string => 'export {};'; const routeChunkQueryStringPrefix = '?route-chunk='; -const routeChunkQueryStrings: Record = { +const routeChunkQueryStrings: Record = { main: `${routeChunkQueryStringPrefix}main`, clientAction: `${routeChunkQueryStringPrefix}clientAction`, clientLoader: `${routeChunkQueryStringPrefix}clientLoader`, @@ -141,6 +143,7 @@ type ExportDependencies = { topLevelStatements: Set; topLevelNonModuleStatements: Set; importedIdentifierNames: Set; + importSources: Set; exportedVariableDeclarators: Set; }; @@ -172,6 +175,18 @@ const getVariableDeclaratorForNode = ( return null; }; +const isTopLevelExportedVariableDeclarator = ( + module: Module, + node: AnyNode +): boolean => { + const declaration = module.parentOf(node as never) as AnyNode | null; + if (declaration?.type !== 'VariableDeclaration') { + return false; + } + const statement = module.parentOf(declaration as never) as AnyNode | null; + return statement?.type === 'ExportNamedDeclaration'; +}; + const getExportedName = (exported: AnyNode): string => { if (exported.type === 'Identifier') { return exported.name; @@ -194,6 +209,26 @@ const setsIntersect = (set1: Set, set2: Set) => { return false; }; +const isEntryClientImport = (source: string, importer: string): boolean => { + if (!source.startsWith('.') && !source.startsWith('/')) { + return false; + } + const resolved = normalize(resolve('/', dirname(importer), source)); + return /(?:^|\/)entry\.client(?:\.[cm]?[jt]sx?)?$/.test(resolved); +}; + +const hasEntryClientImport = ( + dependencies: ExportDependencies, + importer: string +): boolean => { + for (const source of dependencies.importSources) { + if (isEntryClientImport(source, importer)) { + return true; + } + } + return false; +}; + const getExportDependencies = ( code: string, cache: RouteChunkCache, @@ -253,6 +288,7 @@ const getExportDependencies = ( topLevelStatements: new Set(), topLevelNonModuleStatements: new Set(), importedIdentifierNames: new Set(), + importSources: new Set(), exportedVariableDeclarators: new Set(), }; const visitedSymbols = new Set(); @@ -278,6 +314,9 @@ const getExportDependencies = ( return; } visitedSymbols.add(symbol); + if (symbol.declarations.length === 0) { + return; + } for (const declaration of symbol.declarations as AnyNode[]) { const statement = addCachedTopLevelStatement( @@ -286,12 +325,15 @@ const getExportDependencies = ( ); if (statement.type === 'ImportDeclaration') { dependencies.importedIdentifierNames.add(symbol.name); + if (typeof statement.source?.value === 'string') { + dependencies.importSources.add(statement.source.value); + } + return; } const declarator = getCachedVariableDeclaratorForNode(declaration); if ( declarator && - getCachedTopLevelStatementForNode(declarator).type === - 'ExportNamedDeclaration' + isTopLevelExportedVariableDeclarator(module, declarator) ) { dependencies.exportedVariableDeclarators.add(declarator); } @@ -336,12 +378,22 @@ const getExportDependencies = ( const isExportChunkable = ( exportName: string, - exportDependencies: Map + exportDependencies: Map, + importer: string ) => { const dependencies = exportDependencies.get(exportName); if (!dependencies) { return false; } + if (exportName === 'clientLoader' && hasHydrateAssignment(dependencies)) { + return false; + } + if ( + exportName === 'clientLoader' && + hasEntryClientImport(dependencies, importer) + ) { + return false; + } for (const [currentExportName, currentDependencies] of exportDependencies) { if (currentExportName === exportName) { continue; @@ -376,6 +428,21 @@ const isExportChunkable = ( return true; }; +const hasHydrateAssignment = (dependencies: ExportDependencies): boolean => + Array.from(dependencies.topLevelNonModuleStatements).some(statement => { + const expression = statement.expression; + const left = expression?.left; + return ( + statement.type === 'ExpressionStatement' && + expression?.type === 'AssignmentExpression' && + left?.type === 'MemberExpression' && + left.object?.type === 'Identifier' && + left.object.name === 'clientLoader' && + left.property?.type === 'Identifier' && + left.property.name === 'hydrate' + ); + }); + const getChunkableExportMap = ( code: string, cache: RouteChunkCache, @@ -384,7 +451,7 @@ const getChunkableExportMap = ( getOrSetFromCache(cache, `${cacheKey}::getChunkableExportMap`, code, () => { const exportDependencies = getExportDependencies(code, cache, cacheKey); return createRouteChunkExportMap(exportName => - isExportChunkable(exportName, exportDependencies) + isExportChunkable(exportName, exportDependencies, cacheKey) ); }); @@ -393,12 +460,18 @@ const hasChunkableExport = ( exportName: string, cache: RouteChunkCache, cacheKey: string -) => - (routeChunkExportNames as string[]).includes(exportName) - ? getChunkableExportMap(code, cache, cacheKey)[ - exportName as RouteChunkExportName - ] - : false; +) => { + if ((routeChunkExportNames as string[]).includes(exportName)) { + return getChunkableExportMap(code, cache, cacheKey)[ + exportName as RouteChunkExportName + ]; + } + return isExportChunkable( + exportName, + getExportDependencies(code, cache, cacheKey), + cacheKey + ); +}; const generateCode = (program: AnyNode): string | undefined => { if (program.body.length === 0) { @@ -501,14 +574,12 @@ const getChunkedExport = ( ); }; -const getChunkedExportCacheKey = ( - cacheKey: string, - exportName: RouteChunkExportName -) => `${cacheKey}::getChunkedExport::${exportName}`; +const getChunkedExportCacheKey = (cacheKey: string, exportName: string) => + `${cacheKey}::getChunkedExport::${exportName}`; const hasCachedChunkedExport = ( code: string, - exportName: RouteChunkExportName, + exportName: string, cache: RouteChunkCache, cacheKey: string ): boolean => @@ -525,11 +596,10 @@ const omitChunkedExports = ( `${cacheKey}::omitChunkedExports::${exportNames.join(',')}`, code, () => { - const chunkableExportMap = getChunkableExportMap(code, cache, cacheKey); const exportNameSet = new Set(exportNames); const isOmitted = (exportName: string) => exportNameSet.has(exportName) && - Boolean(chunkableExportMap[exportName as RouteChunkExportName]); + hasChunkableExport(code, exportName, cache, cacheKey); const isRetained = (exportName: string) => !isOmitted(exportName); const exportDependencies = getExportDependencies(code, cache, cacheKey); @@ -633,17 +703,57 @@ const precomputeChunkedExports = ( cache: RouteChunkCache, cacheKey: string ) => { - const chunkableExportMap = getChunkableExportMap(code, cache, cacheKey); - for (const exportName of routeChunkExportNames) { - if (!chunkableExportMap[exportName]) { + for (const exportName of getChunkedExportNames(code, cache, cacheKey)) { + if (!hasChunkableExport(code, exportName, cache, cacheKey)) { + continue; + } + if (hasCachedChunkedExport(code, exportName, cache, cacheKey)) { continue; } - if (!hasCachedChunkedExport(code, exportName, cache, cacheKey)) { + if ( + !hasCachedValue( + cache, + getChunkedExportCacheKey(cacheKey, exportName), + code + ) + ) { getChunkedExport(code, exportName, cache, cacheKey); } } }; +const getSharedChunkedExports = ( + exportDependencies: Map, + cacheKey: string +): string[] => + Array.from(exportDependencies.keys()) + .filter( + exportName => + exportName !== 'default' && + !(routeChunkExportNames as string[]).includes(exportName) && + !SERVER_ONLY_ROUTE_EXPORTS_SET.has(exportName) && + isExportChunkable(exportName, exportDependencies, cacheKey) + ) + .sort(); + +const getChunkedExportNames = ( + code: string, + cache: RouteChunkCache, + cacheKey: string +): string[] => { + const exportDependencies = getExportDependencies(code, cache, cacheKey); + const routeChunkedExports = routeChunkExportNames.filter(exportName => + isExportChunkable(exportName, exportDependencies, cacheKey) + ); + if (routeChunkedExports.length === 0) { + return []; + } + return [ + ...routeChunkedExports, + ...getSharedChunkedExports(exportDependencies, cacheKey), + ]; +}; + export const detectRouteChunks = ( code: string, cache: RouteChunkCache | undefined, @@ -663,31 +773,43 @@ export const detectRouteChunks = ( const chunkedExports = Object.entries(hasRouteChunkByExportName) .filter(([, isChunked]) => isChunked) .map(([exportName]) => exportName as RouteChunkExportName); - const hasRouteChunks = chunkedExports.length > 0; + const sharedChunkedExports = + chunkedExports.length > 0 + ? getSharedChunkedExports(exportDependencies, cacheKey) + : []; + const hasRouteChunks = + chunkedExports.length > 0 || sharedChunkedExports.length > 0; return { exportNames: Array.from(exportDependencies.keys()), hasRouteChunks, hasRouteChunkByExportName, chunkedExports, + sharedChunkedExports, }; }; -export const getRouteChunkCode: ( - code: string, - chunkName: RouteChunkName, - cache: RouteChunkCache | undefined, - cacheKey: string -) => string | undefined = ( +export const getRouteChunkCode = ( code: string, chunkName: RouteChunkName, cache: RouteChunkCache | undefined, cacheKey: string -) => { +): string | undefined => { const analysisCache = cache ?? new Map(); if (chunkName === 'main') { + const exportDependencies = getExportDependencies( + code, + analysisCache, + cacheKey + ); + const serverOnlyExports = Array.from(exportDependencies.keys()).filter( + exportName => SERVER_ONLY_ROUTE_EXPORTS_SET.has(exportName) + ); return omitChunkedExports( code, - routeChunkExportNames, + [ + ...getChunkedExportNames(code, analysisCache, cacheKey), + ...serverOnlyExports, + ], analysisCache, cacheKey ); @@ -701,13 +823,17 @@ export const getRouteChunkCode: ( export const getRouteChunkModuleId = ( filePath: string, chunkName: RouteChunkName -) => `${filePath}${routeChunkQueryStrings[chunkName]}`; +) => + `${filePath}${ + routeChunkQueryStrings[chunkName as RouteChunkExportName | 'main'] ?? + `${routeChunkQueryStringPrefix}${encodeURIComponent(chunkName)}` + }`; -export const isRouteChunkModuleId: (id: string) => boolean = (id: string) => +export const isRouteChunkModuleId = (id: string): boolean => getRouteChunkNameFromModuleId(id) !== null; const isRouteChunkName = (name: string): name is RouteChunkName => - name === 'main' || (routeChunkExportNames as string[]).includes(name); + name === 'main' || /^[A-Za-z_$][\w$]*$/.test(name); export const getRouteChunkNameFromModuleId = ( id: string @@ -769,20 +895,16 @@ export const buildManifestChunkValidity = ( !exportNames.has(exportName) || hasRouteChunkByExportName[exportName] ); -export const detectRouteChunksIfEnabled: ( +export const detectRouteChunksIfEnabled = async ( cache: RouteChunkCache | undefined, config: RouteChunkConfig, id: string, code: string -) => Promise = async ( - cache: RouteChunkCache | undefined, - config: RouteChunkConfig, - id: string, - code: string -) => { +): Promise => { const noRouteChunks = (): RouteChunkInfo => ({ exportNames: [], chunkedExports: [] as RouteChunkExportName[], + sharedChunkedExports: [], hasRouteChunks: false, hasRouteChunkByExportName: createEmptyRouteChunkByExportName(), }); @@ -795,19 +917,13 @@ export const detectRouteChunksIfEnabled: ( return detectRouteChunks(code, cache, cacheKey); }; -export const getRouteChunkIfEnabled: ( - cache: RouteChunkCache | undefined, - config: RouteChunkConfig, - id: string, - chunkName: RouteChunkName, - code: string -) => Promise = async ( +export const getRouteChunkIfEnabled = async ( cache: RouteChunkCache | undefined, config: RouteChunkConfig, id: string, chunkName: RouteChunkName, code: string -) => { +): Promise => { if (!config.splitRouteModules) { return null; } diff --git a/src/route-component-transform.ts b/src/route-component-transform.ts index c1dbea68..68fc5c57 100644 --- a/src/route-component-transform.ts +++ b/src/route-component-transform.ts @@ -16,21 +16,14 @@ import { type AnyNode, } from './route-ast.js'; -export function toFunctionExpression(decl: AnyNode): AnyNode { - return { - ...decl, - type: 'FunctionExpression', - declare: undefined, - }; -} - -export function toClassExpression(decl: AnyNode): AnyNode { - return { - ...decl, - type: 'ClassExpression', - declare: undefined, - }; -} +const toExpression = ( + decl: AnyNode, + type: 'FunctionExpression' | 'ClassExpression' +): AnyNode => ({ + ...decl, + type, + declare: undefined, +}); const getComponentExportName = (exportedName: string): string | null => { if (exportedName === 'default') { @@ -124,7 +117,7 @@ export const transformRoute = (ast: ParseResult | AnyNode): void => { function getHocUid(hocName: string) { const uid = getUid(hocName); - hocs.push([hocName, uid]); + hocs.push([`UNSAFE_${hocName}`, uid]); return identifier(uid); } @@ -132,9 +125,9 @@ export const transformRoute = (ast: ParseResult | AnyNode): void => { const uid = getHocUid(`with${name}Props`); const expression = declaration.type === 'FunctionDeclaration' - ? toFunctionExpression(declaration) + ? toExpression(declaration, 'FunctionExpression') : declaration.type === 'ClassDeclaration' - ? toClassExpression(declaration) + ? toExpression(declaration, 'ClassExpression') : declaration; return variableDeclaration(name, callExpression(uid, [expression])); } @@ -166,9 +159,9 @@ export const transformRoute = (ast: ParseResult | AnyNode): void => { } const expression = declaration.type === 'FunctionDeclaration' - ? toFunctionExpression(declaration) + ? toExpression(declaration, 'FunctionExpression') : declaration.type === 'ClassDeclaration' - ? toClassExpression(declaration) + ? toExpression(declaration, 'ClassExpression') : declaration; statement.declaration = callExpression(uid, [expression]); continue; @@ -301,7 +294,7 @@ export const transformRoute = (ast: ParseResult | AnyNode): void => { 0, importDeclaration( hocs.map(([name, local]) => ({ imported: name, local })), - 'virtual/react-router/with-props' + 'react-router' ) ); } diff --git a/src/route-export-pruning.ts b/src/route-export-pruning.ts index d3e3762d..44fb8041 100644 --- a/src/route-export-pruning.ts +++ b/src/route-export-pruning.ts @@ -198,7 +198,7 @@ const isNonReferenceIdentifier = (node: AnyNode, parent: AnyNode | null) => { const isUppercaseName = (name: string): boolean => /^[A-Z]/.test(name); -const collectReferencedNames = (node: AnyNode): Set => { +export const collectReferencedNames = (node: AnyNode): Set => { const referenced = new Set(); walk(node as never, { Identifier(node, ctx) { @@ -489,7 +489,8 @@ const hasRemovableExport = ( export const removeExports = ( ast: ParseResult | AnyNode, exportsToRemove: readonly string[], - exportsToRemoveSet: ReadonlySet = new Set(exportsToRemove) + exportsToRemoveSet: ReadonlySet = new Set(exportsToRemove), + options: { pruneDeadDeclarations?: boolean } = {} ): boolean => { const program = getProgram(ast); if (!hasRemovableExport(program, exportsToRemoveSet)) { @@ -631,7 +632,7 @@ export const removeExports = ( } } - if (exportsChanged) { + if (exportsChanged && options.pruneDeadDeclarations !== false) { removeNewlyDeadTopLevelDeclarations( program, declarationGraph, diff --git a/src/route-imports.ts b/src/route-imports.ts new file mode 100644 index 00000000..d20ae47a --- /dev/null +++ b/src/route-imports.ts @@ -0,0 +1,100 @@ +import type { Rspack } from '@rsbuild/core'; +import { resolve } from 'pathe'; + +import { JS_EXTENSIONS, PLUGIN_NAME } from './constants.js'; +import type { Route } from './types.js'; + +type QuerylessRouteImportPlugin = { + name: string; + apply(compiler: Rspack.Compiler): void; +}; + +const RSC_CLIENT_ROUTE_MODULE_QUERY_PREFIX = '?client-route-module='; +const RSC_SHARED_CLIENT_ROUTE_MODULE_QUERY = '?client-route-module=shared'; +const RSC_SERVER_ROUTE_MODULE_QUERY = '?server-route-module='; +const CLASSIC_CLIENT_ROUTE_MODULE_QUERY = '?react-router-route'; + +const isRscClientRouteModuleIssuer = (issuer: string): boolean => + issuer.includes(RSC_CLIENT_ROUTE_MODULE_QUERY_PREFIX); + +export const resolveQuerylessRouteImportRequest = ({ + compilerName, + context, + issuer, + rsc = false, + request, + routeByFilePath, +}: { + compilerName?: string; + context?: string; + issuer?: string; + rsc?: boolean; + request?: string; + routeByFilePath: ReadonlyMap; +}): string | undefined => { + if ( + typeof request !== 'string' || + typeof context !== 'string' || + typeof issuer !== 'string' || + request.includes('?') || + (!request.startsWith('.') && !request.startsWith('/')) + ) { + return; + } + + const issuerPath = issuer.split('?')[0]; + if (!routeByFilePath.has(issuerPath)) { + return; + } + + const isRscClientIssuer = isRscClientRouteModuleIssuer(issuer); + const isWebCompiler = compilerName === 'web'; + if (!rsc && !isWebCompiler && !isRscClientIssuer) { + return; + } + + const candidate = resolve(context, request); + const routeFilePath = routeByFilePath.has(candidate) + ? candidate + : JS_EXTENSIONS.map(extension => `${candidate}${extension}`).find( + candidate => routeByFilePath.has(candidate) + ); + + if (!routeFilePath) { + return; + } + + if (!rsc && isWebCompiler) { + return `${routeFilePath}${CLASSIC_CLIENT_ROUTE_MODULE_QUERY}`; + } + + if (isWebCompiler || isRscClientIssuer) { + return `${routeFilePath}${RSC_SHARED_CLIENT_ROUTE_MODULE_QUERY}`; + } + + return `${routeFilePath}${RSC_SERVER_ROUTE_MODULE_QUERY}`; +}; + +export const createQuerylessRouteImportPlugin = ( + routeByFilePath: ReadonlyMap, + options: { rsc?: boolean } = {} +): QuerylessRouteImportPlugin => ({ + name: `${PLUGIN_NAME}:queryless-route-imports`, + apply(compiler: Rspack.Compiler) { + compiler.hooks.normalModuleFactory.tap(PLUGIN_NAME, factory => { + factory.hooks.beforeResolve.tap(PLUGIN_NAME, data => { + const resolvedRequest = resolveQuerylessRouteImportRequest({ + compilerName: compiler.options?.name, + context: data?.context ?? data?.contextInfo?.issuer, + issuer: data?.contextInfo?.issuer, + rsc: options.rsc, + request: data?.request, + routeByFilePath, + }); + if (resolvedRequest) { + data.request = resolvedRequest; + } + }); + }); + }, +}); diff --git a/src/route-transform-tasks.ts b/src/route-transform-tasks.ts index 8deb9b7f..fbb36849 100644 --- a/src/route-transform-tasks.ts +++ b/src/route-transform-tasks.ts @@ -24,10 +24,7 @@ import { type RouteChunkCache, type RouteChunkConfig, } from './route-chunks.js'; -import { - getProgram, - type AnyNode, -} from './route-ast.js'; +import { getProgram } from './route-ast.js'; export type RouteTransformResult = { code: string; @@ -159,6 +156,14 @@ const createClientOnlyStub = async ( const isComponentishName = (name: string): boolean => /^[A-Z]/.test(name); +type AnyNode = { + type?: string; + name?: string; + body?: { type?: string }; + callee?: { type?: string; name?: string }; + arguments?: Array; +}; + /** * Whether an expression that appears as the first argument of a wrapping call * resolves to a component, mirroring react-refresh/babel's @@ -226,7 +231,14 @@ const initResolvesToComponent = (init: AnyNode): boolean => { }; const collectDeclaredComponentNames = ( - declaration: AnyNode, + declaration: { + type?: string; + id?: { name?: string }; + declarations?: Array<{ + id?: { type?: string; name?: string }; + init?: AnyNode; + }>; + }, names: Set ): void => { if ( @@ -269,7 +281,15 @@ const collectDeclaredComponentNames = ( * subtree instead of updating it in place. */ const collectUnregisteredComponentNames = (program: { - body?: AnyNode[]; + body?: Array<{ + type?: string; + declaration?: Parameters[0]; + expression?: { + type?: string; + callee?: { type?: string; name?: string }; + arguments?: Array<{ type?: string; value?: unknown }>; + }; + }>; }): string[] => { const declared = new Set(); const registered = new Set(); @@ -290,7 +310,10 @@ const collectUnregisteredComponentNames = (program: { } continue; } - collectDeclaredComponentNames(statement, declared); + collectDeclaredComponentNames( + statement as Parameters[0], + declared + ); } return [...declared].filter(name => !registered.has(name)); }; @@ -309,6 +332,41 @@ const buildComponentRefreshRegistrations = (names: string[]): string => { return `\nif (typeof $RefreshReg$ === 'function') {\n${registrations}\n}\n`; }; +export const validateSpaModeRouteExports = ({ + exportNames, + resourcePath, + rootRoutePath, +}: { + exportNames: readonly string[]; + resourcePath: string; + rootRoutePath: string | null; +}): void => { + const isRootRoute = resourcePath === rootRoutePath; + const relativePath = relative(process.cwd(), resourcePath); + const invalidServerOnly = exportNames.filter(exp => { + if (isRootRoute && exp === 'loader') return false; + return SERVER_ONLY_ROUTE_EXPORTS_SET.has(exp); + }); + + if (invalidServerOnly.length > 0) { + const list = invalidServerOnly.map(exp => `\`${exp}\``).join(', '); + throw new Error( + `SPA Mode: ${invalidServerOnly.length} invalid route export(s) in ` + + `\`${relativePath}\`: ${list}. ` + + `See https://reactrouter.com/how-to/spa for more information.` + ); + } + + if (!isRootRoute && exportNames.includes('HydrateFallback')) { + throw new Error( + `SPA Mode: Invalid \`HydrateFallback\` export found in ` + + `\`${relativePath}\`. ` + + `\`HydrateFallback\` is only permitted on the root route in SPA Mode. ` + + `See https://reactrouter.com/how-to/spa for more information.` + ); + } +}; + const transformRouteModule = async ( task: RouteModuleTransformTask ): Promise => { @@ -324,32 +382,11 @@ const transformRouteModule = async ( const ast = parse(code, { sourceType: 'module' }); if (task.environmentName === 'web' && !task.ssr && task.isSpaMode) { - const resolvedExportNames = collectProgramExportNames(getProgram(ast)); - const isRootRoute = task.resourcePath === task.rootRoutePath; - const relativePath = relative(process.cwd(), task.resourcePath); - - const invalidServerOnly = resolvedExportNames.filter(exp => { - if (isRootRoute && exp === 'loader') return false; - return SERVER_ONLY_ROUTE_EXPORTS_SET.has(exp); + validateSpaModeRouteExports({ + exportNames: collectProgramExportNames(getProgram(ast)), + resourcePath: task.resourcePath, + rootRoutePath: task.rootRoutePath, }); - - if (invalidServerOnly.length > 0) { - const list = invalidServerOnly.map(e => `\`${e}\``).join(', '); - throw new Error( - `SPA Mode: ${invalidServerOnly.length} invalid route export(s) in ` + - `\`${relativePath}\`: ${list}. ` + - `See https://reactrouter.com/how-to/spa for more information.` - ); - } - - if (!isRootRoute && resolvedExportNames.includes('HydrateFallback')) { - throw new Error( - `SPA Mode: Invalid \`HydrateFallback\` export found in ` + - `\`${relativePath}\`. ` + - `\`HydrateFallback\` is only permitted on the root route in SPA Mode. ` + - `See https://reactrouter.com/how-to/spa for more information.` - ); - } } const removedServerOnlyExports = diff --git a/src/route-watch.ts b/src/route-watch.ts index d98735a8..e100afaf 100644 --- a/src/route-watch.ts +++ b/src/route-watch.ts @@ -2,11 +2,12 @@ import { watch, type FSWatcher } from 'node:fs'; import { access, mkdir, readdir, writeFile } from 'node:fs/promises'; import type { RsbuildConfig } from '@rsbuild/core'; import * as Effect from 'effect/Effect'; +import type * as Scope from 'effect/Scope'; import { dirname, resolve } from 'pathe'; import { getCappedPluginConcurrency } from './concurrency.js'; import { createDelayedPluginTask, - runPluginEffect, + type PluginEffectRuntime, tryPluginPromise, } from './effect-runtime.js'; import type { Route } from './types.js'; @@ -47,6 +48,17 @@ type WatchDirectoryEntry = ( onError: (error: unknown) => void ) => DirectoryWatcher; +export type CreateRouteTopologyWatcherOptions = { + runtime: PluginEffectRuntime; + watchDirectory: string; + getRouteTopology: () => Promise>; + initialRouteTopology?: Set; + restartMarkerPath: string; + onError: (error: unknown) => void; + onRouteTopologyChange?: () => void | Promise; + watchDirectoryEntry?: WatchDirectoryEntry; +}; + const defaultWatchDirectoryEntry: WatchDirectoryEntry = ( directory, onChange, @@ -70,8 +82,8 @@ export const mergeWatchFiles = ( ] as WatchFilesConfig; }; -export const getRouteRestartMarkerPath = (outputClientPath: string): string => - resolve(outputClientPath, ROUTE_RESTART_MARKER_ASSET); +export const getRouteRestartMarkerPath = (appDirectory: string): string => + resolve(appDirectory, '..', ROUTE_RESTART_MARKER_ASSET); export const createRouteManifestSnapshot = ( routes: RouteManifestSnapshotEntries @@ -120,10 +132,6 @@ const areSetsEqual = (left: Set, right: Set): boolean => { return true; }; -const readRouteDirectories = (watchDirectory: string): Promise> => { - return runPluginEffect(readRouteDirectoriesEffect(watchDirectory)); -}; - const readRouteDirectoriesEffect = ( watchDirectory: string ): Effect.Effect, Error, never> => { @@ -148,7 +156,8 @@ const readRouteDirectoriesEffect = ( return walkDirectory(watchDirectory).pipe(Effect.as(directories)); }; -export const createRouteTopologyWatcher = async ({ +const createRouteTopologyWatcherEffect = ({ + runtime, watchDirectory, getRouteTopology, initialRouteTopology, @@ -156,202 +165,171 @@ export const createRouteTopologyWatcher = async ({ onError, onRouteTopologyChange, watchDirectoryEntry: watchDirectoryOverride = defaultWatchDirectoryEntry, -}: { - watchDirectory: string; - getRouteTopology: () => Promise>; - initialRouteTopology?: Set; - restartMarkerPath: string; - onError: (error: unknown) => void; - onRouteTopologyChange?: () => void | Promise; - watchDirectoryEntry?: WatchDirectoryEntry; -}): Promise<() => Promise> => { - const discoveredDirectories = await readRouteDirectories(watchDirectory); - let discoveredState: RouteDirectoryState; - try { - discoveredState = { +}: CreateRouteTopologyWatcherOptions) => + Effect.gen(function* () { + const discoveredDirectories = + yield* readRouteDirectoriesEffect(watchDirectory); + const routeTopology = yield* tryPluginPromise(getRouteTopology).pipe( + Effect.catchAll(error => { + if (!initialRouteTopology) return Effect.fail(error); + onError(error); + return Effect.succeed(initialRouteTopology); + }) + ); + const discoveredState: RouteDirectoryState = { directories: discoveredDirectories, - routeTopology: await getRouteTopology(), + routeTopology, }; - } catch (error) { - if (!initialRouteTopology) { - throw error; - } - onError(error); - discoveredState = { - directories: discoveredDirectories, - routeTopology: initialRouteTopology, + let state = { + ...discoveredState, + routeTopology: initialRouteTopology ?? discoveredState.routeTopology, }; - } - let state = { - ...discoveredState, - routeTopology: initialRouteTopology ?? discoveredState.routeTopology, - }; - let closed = false; - let rescanQueue = Promise.resolve(); - const directoryWatchers = new Map(); + let closed = false; + const directoryWatchers = new Map(); - const touchRestartMarkerEffect = (): Effect.Effect => - tryPluginPromise(() => - mkdir(dirname(restartMarkerPath), { recursive: true }) - ).pipe( - Effect.zipRight( - tryPluginPromise(() => writeFile(restartMarkerPath, String(Date.now()))) - ) - ); - - const closeRemovedDirectoryWatchers = ( - nextDirectories: Set - ): void => { - for (const [directory, watcher] of directoryWatchers) { - if (!nextDirectories.has(directory)) { - watcher.close(); - directoryWatchers.delete(directory); - } - } - }; + const touchRestartMarkerEffect = (): Effect.Effect => + tryPluginPromise(() => + mkdir(dirname(restartMarkerPath), { recursive: true }) + ).pipe( + Effect.zipRight( + tryPluginPromise(() => + writeFile(restartMarkerPath, String(Date.now())) + ) + ) + ); - const watchNewDirectories = (nextDirectories: Set): void => { - for (const directory of nextDirectories) { - if (directoryWatchers.has(directory)) { - continue; - } - try { - let watcher: DirectoryWatcher; - watcher = watchDirectoryOverride( - directory, - () => rescanTask.reschedule(), - error => { - if (directoryWatchers.get(directory) === watcher) { - watcher.close(); - directoryWatchers.delete(directory); - } - onError(error); - } - ); - directoryWatchers.set(directory, watcher); - } catch (error) { - onError(error); + const closeRemovedDirectoryWatchers = ( + nextDirectories: Set + ): void => { + for (const [directory, watcher] of directoryWatchers) { + if (!nextDirectories.has(directory)) { + watcher.close(); + directoryWatchers.delete(directory); + } } - } - }; - - const syncDirectoryWatchers = (nextDirectories: Set): void => { - closeRemovedDirectoryWatchers(nextDirectories); - watchNewDirectories(nextDirectories); - }; + }; - const applyNextStateEffect = ( - nextState: RouteDirectoryState - ): Effect.Effect => - Effect.suspend(() => { - if (closed) { - return Effect.void; - } - syncDirectoryWatchers(nextState.directories); - if (!areSetsEqual(state.routeTopology, nextState.routeTopology)) { - if (onRouteTopologyChange) { - // This is a notification boundary, not part of the rescan - // transaction. A custom-server callback may close this watcher while - // replacing its compiler, so awaiting it here would deadlock close(). - state = nextState; - return Effect.sync(() => { - try { - void Promise.resolve(onRouteTopologyChange()).catch(onError); - } catch (error) { + const watchNewDirectories = (nextDirectories: Set): void => { + for (const directory of nextDirectories) { + if (directoryWatchers.has(directory)) { + continue; + } + try { + let watcher: DirectoryWatcher; + watcher = watchDirectoryOverride( + directory, + () => { + if (!closed) rescanTask.reschedule(); + }, + error => { + if (closed) return; + if (directoryWatchers.get(directory) === watcher) { + watcher.close(); + directoryWatchers.delete(directory); + } onError(error); } - }); + ); + directoryWatchers.set(directory, watcher); + } catch (error) { + onError(error); } - return touchRestartMarkerEffect().pipe( - Effect.zipRight( - Effect.sync(() => { - if (!closed) { - state = nextState; - } - }) - ) - ); } - state = nextState; - return Effect.void; - }); + }; - const runRescanEffect = (): Effect.Effect => { - let nextDirectories: Set | undefined; - return Effect.gen(function* () { - if (closed) { - return; - } - nextDirectories = yield* readRouteDirectoriesEffect(watchDirectory); - if (closed) { - return; - } - const nextState = { - directories: nextDirectories, - routeTopology: yield* tryPluginPromise(getRouteTopology), - }; - if (closed) { - return; - } - yield* applyNextStateEffect(nextState); - }).pipe( - Effect.catchAll(error => - Effect.sync(() => { - if (nextDirectories && !closed) { - syncDirectoryWatchers(nextDirectories); + const syncDirectoryWatchers = (nextDirectories: Set): void => { + closeRemovedDirectoryWatchers(nextDirectories); + watchNewDirectories(nextDirectories); + }; + + const applyNextStateEffect = ( + nextState: RouteDirectoryState + ): Effect.Effect => + Effect.suspend(() => { + if (closed) return Effect.void; + syncDirectoryWatchers(nextState.directories); + if (!areSetsEqual(state.routeTopology, nextState.routeTopology)) { + if (onRouteTopologyChange) { + // This is a notification boundary, not part of the rescan + // transaction. A custom-server callback may close this watcher while + // replacing its compiler, so awaiting it here would deadlock close(). + state = nextState; + return Effect.sync(() => { + try { + void Promise.resolve(onRouteTopologyChange()).catch(onError); + } catch (error) { + onError(error); + } + }); } - onError(error); - }) - ) - ); - }; + return touchRestartMarkerEffect().pipe( + Effect.zipRight( + Effect.sync(() => { + if (!closed) { + state = nextState; + } + }) + ) + ); + } + state = nextState; + return Effect.void; + }); - const rescan = (): Promise => { - rescanQueue = rescanQueue.then( - () => runPluginEffect(runRescanEffect()), - () => runPluginEffect(runRescanEffect()) - ); - return rescanQueue; - }; + const runRescanEffect = (): Effect.Effect => { + let nextDirectories: Set | undefined; + return Effect.gen(function* () { + if (closed) return; + nextDirectories = yield* readRouteDirectoriesEffect(watchDirectory); + if (closed) return; + const nextState = { + directories: nextDirectories, + routeTopology: yield* tryPluginPromise(getRouteTopology), + }; + if (closed) return; + yield* applyNextStateEffect(nextState); + }).pipe( + Effect.catchAll(error => + Effect.sync(() => { + if (nextDirectories && !closed) + syncDirectoryWatchers(nextDirectories); + onError(error); + }) + ) + ); + }; - const rescanTask = createDelayedPluginTask({ - delayMs: ROUTE_TOPOLOGY_RESCAN_DEBOUNCE_MS, - run: () => - Effect.suspend(() => - closed ? Effect.void : tryPluginPromise(rescan).pipe(Effect.asVoid) - ), - onError, - }); + const rescanTask = createDelayedPluginTask({ + runtime, + delayMs: ROUTE_TOPOLOGY_RESCAN_DEBOUNCE_MS, + run: () => + Effect.suspend(() => (closed ? Effect.void : runRescanEffect())), + onError, + }); - const cancelScheduledRescan = (): Promise => - runPluginEffect(rescanTask.cancelEffect()); + const closeEffect = (): Effect.Effect => + tryPluginPromise(() => { + if (closed) return; + closed = true; + for (const watcher of directoryWatchers.values()) { + watcher.close(); + } + directoryWatchers.clear(); + }).pipe(Effect.ensuring(rescanTask.cancelEffect())); - const applyNextState = async (nextState: RouteDirectoryState) => { - await runPluginEffect(applyNextStateEffect(nextState)); - }; + yield* applyNextStateEffect(discoveredState).pipe( + Effect.catchAll(error => Effect.sync(() => onError(error))), + Effect.onInterrupt(() => Effect.ignore(closeEffect())) + ); - try { - await applyNextState(discoveredState); - } catch (error) { - onError(error); - } + return closeEffect; + }); - return async () => { - if (closed) { - await cancelScheduledRescan(); - await rescanQueue; - return; - } - closed = true; - await cancelScheduledRescan(); - for (const watcher of directoryWatchers.values()) { - watcher.close(); - } - directoryWatchers.clear(); - await rescanQueue; - for (const watcher of directoryWatchers.values()) { - watcher.close(); - } - directoryWatchers.clear(); - }; -}; +export const acquireRouteTopologyWatcher = ( + options: CreateRouteTopologyWatcherOptions +): Effect.Effect<() => Effect.Effect, Error, Scope.Scope> => + Effect.acquireRelease(createRouteTopologyWatcherEffect(options), close => + close().pipe( + Effect.catchAll(error => Effect.sync(() => options.onError(error))) + ) + ); diff --git a/src/rsc-dev-server.ts b/src/rsc-dev-server.ts new file mode 100644 index 00000000..dc932f2a --- /dev/null +++ b/src/rsc-dev-server.ts @@ -0,0 +1,112 @@ +import { createRequestListener } from '@remix-run/node-fetch-server'; +import type { RsbuildConfig } from '@rsbuild/core'; +import { installDevServerSourceMapSupport } from './dev-source-maps.js'; +import { escapeHtml } from './plugin-utils.js'; + +/** + * Turns an error thrown by the RSC server build into a source-mapped 500 HTML + * document. `@remix-run/node-fetch-server`'s request listener catches handler + * throws internally and, without an `onError`, emits a bare 500 with no body + * markup. React Router's classic dev path instead returns an ErrorBoundary + * document whose `
` carries the stack, and the integration test asserts + * on that `
`. Reading `error.stack` here triggers the installed + * `prepareStackTrace`, remapping generated `.js` frames back to the original + * `.tsx` source. + */ +const renderDevServerError = (error: unknown): Response => { + const stack = + error instanceof Error ? (error.stack ?? String(error)) : String(error); + return new Response( + `
${escapeHtml(
+      stack
+    )}
`, + { status: 500, headers: { 'content-type': 'text/html' } } + ); +}; + +type RscServerBuild = { + default?: { + fetch?: (request: Request) => Promise; + }; +}; + +type RscDevServerSetup = NonNullable< + NonNullable['setup'] +>; + +type RscDevServerSetupOptions = { + entryName: string; + pluginName: string; +}; + +/** + * Decides whether a dev-server request should skip the RSC request handler + * and fall through to the rest of the middleware chain. + * + * Only Rsbuild-internal endpoints (`/__rsbuild_*`, e.g. HMR web socket + * fallbacks) are bypassed. Everything else — any method, any pathname, + * with or without a file extension — is handled by the RSC server build, + * matching production where the RSC server handles every request. + * + * Static assets need no bypass here: the RSC middleware is registered via + * the callback returned from `server.setup`, which Rsbuild invokes only + * after its built-in middlewares (compiled assets, public dir) are + * registered. A request that reaches this middleware was already declined + * by static asset serving. + */ +export const shouldBypassRscDevRequest = (request: { + url?: string; +}): boolean => { + if (!request.url) { + return true; + } + const { pathname } = new URL(request.url, 'http://localhost'); + return pathname.startsWith('/__rsbuild_'); +}; + +export function createReactRouterRscDevServerSetup({ + entryName, + pluginName, +}: RscDevServerSetupOptions): RscDevServerSetup { + return context => { + if (context.action === 'preview') { + return; + } + const { server } = context; + // Remap generated `.js` stack frames back to original `.tsx` sources so a + // loader/render error surfaced below carries source-mapped locations, the + // same support the classic dev path installs. + installDevServerSourceMapSupport(); + const listener = createRequestListener( + async request => { + const build = + await server.environments.node.loadBundle(entryName); + const handler = build.default?.fetch; + if (typeof handler !== 'function') { + throw new Error( + `[${pluginName}] RSC server build must default-export an object with a fetch function.` + ); + } + return handler(request); + }, + { onError: renderDevServerError } + ); + + // Register the RSC middleware in the callback returned from + // `server.setup`. Rsbuild runs middlewares registered synchronously in + // `setup` BEFORE its built-ins, but runs returned callbacks AFTER + // built-in middlewares (compiled assets, public dir) are registered. + // Registering here makes the RSC handler the fallback for every request + // that static asset serving did not handle — the same routing semantics + // as production, where the RSC server handles everything. + return () => { + server.middlewares.use((req, res, next) => { + if (shouldBypassRscDevRequest(req)) { + next(); + return; + } + Promise.resolve(listener(req, res)).catch(next); + }); + }; + }; +} diff --git a/src/rsc-prerender.ts b/src/rsc-prerender.ts new file mode 100644 index 00000000..3d8c7b14 --- /dev/null +++ b/src/rsc-prerender.ts @@ -0,0 +1,362 @@ +import { existsSync } from 'node:fs'; +import { mkdir, writeFile } from 'node:fs/promises'; +import { pathToFileURL } from 'node:url'; +import type { RsbuildPluginAPI } from '@rsbuild/core'; +import { dirname, relative, resolve } from 'pathe'; +import * as Effect from 'effect/Effect'; +import { PLUGIN_NAME, SPA_FALLBACK_HTML_FILE } from './constants.js'; +import { + createBuildRequestEffect, + createBoundedPrerenderTasksEffect, +} from './prerender-build.js'; +import { normalizeAssetPrefix } from './plugin-utils.js'; +import { getPrerenderConcurrency } from './prerender.js'; +import type { Config } from './react-router-config.js'; +import { runPluginEffect } from './effect-runtime.js'; + +/** + * RSC-mode prerendering. + * + * Mirrors React Router's upstream RSC behavior, feeding the shared + * `prerender` plugin with one request per prerender path (plus + * `/__spa-fallback.html` when `ssr: false`) and post-processes each HTML + * response into two artifacts in the client build directory: + * + * - `/index.html` — the prerendered document + * - `.rsc` (`_.rsc` for `/`) — the RSC payload reassembled from the + * inline `__FLIGHT_DATA` scripts, served for client-side navigations + * + * Instead of an HTTP round-trip through a preview server, the RSC server + * bundle's default-exported `fetch` handler is invoked in-process, matching + * how classic mode prerenders through `createRequestHandler`. + */ + +export const SPA_FALLBACK_REQUEST_PATH: string = `/${SPA_FALLBACK_HTML_FILE}`; + +const redirectStatusCodes = new Set([301, 302, 303, 307, 308]); + +const FLIGHT_DATA_SCRIPT_REGEX = + /`; + +describe('normalizeRscPrerenderBasename', () => { + it('defaults to a root basename', () => { + expect(normalizeRscPrerenderBasename('')).toBe('/'); + expect(normalizeRscPrerenderBasename('/')).toBe('/'); + }); + + it('appends a trailing slash when missing', () => { + expect(normalizeRscPrerenderBasename('/base')).toBe('/base/'); + expect(normalizeRscPrerenderBasename('/base/')).toBe('/base/'); + }); +}); + +describe('getRscPrerenderRequestPaths', () => { + it('returns the prerender paths for ssr builds', () => { + expect( + getRscPrerenderRequests({ + prerenderPaths: ['/', '/about'], + ssr: true, + basename: '/', + }).map(r => r.requestPath) + ).toEqual(['/', '/about']); + }); + + it('adds the SPA fallback document when ssr is disabled', () => { + expect( + getRscPrerenderRequests({ + prerenderPaths: ['/'], + ssr: false, + basename: '/', + }).map(r => r.requestPath) + ).toEqual(['/', SPA_FALLBACK_REQUEST_PATH]); + }); + + it('prerenders only the SPA fallback for ssr:false without prerender paths', () => { + expect( + getRscPrerenderRequests({ + prerenderPaths: [], + ssr: false, + basename: '/', + }).map(r => r.requestPath) + ).toEqual([SPA_FALLBACK_REQUEST_PATH]); + }); + + it('joins paths with the basename', () => { + expect( + getRscPrerenderRequests({ + prerenderPaths: ['/', '/products/1'], + ssr: true, + basename: '/base', + }).map(r => r.requestPath) + ).toEqual(['/base/', '/base/products/1']); + }); + + it('keeps basename request paths separate from artifact paths', () => { + expect( + getRscPrerenderRequests({ + prerenderPaths: [], + ssr: false, + basename: '/base', + }) + ).toEqual([ + { + requestPath: '/base/__spa-fallback.html', + artifactPath: SPA_FALLBACK_REQUEST_PATH, + }, + ]); + }); + + it('deduplicates paths', () => { + expect( + getRscPrerenderRequests({ + prerenderPaths: ['/', '/'], + ssr: true, + basename: '/', + }).map(r => r.requestPath) + ).toEqual(['/']); + }); +}); + +describe('extractRscFlightData', () => { + it('returns null when the document has no flight data', () => { + expect(extractRscFlightData('hi')).toBeNull(); + }); + + it('extracts a single flight chunk', () => { + const html = `${flightScript('chunk-a')}`; + expect(extractRscFlightData(html)).toBe('chunk-a'); + }); + + it('concatenates multiple flight chunks in order', () => { + const html = [ + '', + flightScript('chunk-a'), + '
content
', + flightScript('chunk-b'), + '', + ].join(''); + expect(extractRscFlightData(html)).toBe('chunk-achunk-b'); + }); + + it('decodes JSON-escaped chunk contents', () => { + const chunk = '1:{"a":"line\nbreak \\"quoted\\""}\n'; + const html = flightScript(chunk); + expect(extractRscFlightData(html)).toBe(chunk); + }); +}); + +describe('getRscHtmlFilePath', () => { + it('maps the root path to index.html', () => { + expect(getRscHtmlFilePath('/')).toBe('/index.html'); + }); + + it('maps nested paths to a nested index.html', () => { + expect(getRscHtmlFilePath('/products/1')).toBe('/products/1/index.html'); + expect(getRscHtmlFilePath('/about/')).toBe('/about/index.html'); + }); + + it('maps the SPA fallback request to a top-level file', () => { + expect(getRscHtmlFilePath(SPA_FALLBACK_REQUEST_PATH)).toBe( + '__spa-fallback.html' + ); + }); +}); + +describe('getRscPayloadFilePath', () => { + it('maps the root path to _.rsc', () => { + expect(getRscPayloadFilePath('/')).toBe('_.rsc'); + }); + + it('maps nested paths to .rsc', () => { + expect(getRscPayloadFilePath('/products/1')).toBe('/products/1.rsc'); + }); + + it('maps the SPA fallback request to __spa-fallback.rsc', () => { + expect(getRscPayloadFilePath(SPA_FALLBACK_REQUEST_PATH)).toBe( + '__spa-fallback.rsc' + ); + }); +}); + +describe('runReactRouterRscPrerenderBuild', () => { + it('reports a failed RSC response without duplicating the path', async () => { + const buildDirectory = await mkdtemp( + resolve(tmpdir(), 'rsbuild-rsc-prerender-') + ); + + try { + const serverDirectory = resolve(buildDirectory, 'server'); + await mkdir(serverDirectory); + await writeFile( + resolve(serverDirectory, 'index.js'), + 'export default { fetch: async () => new Response(null, { status: 500 }) };' + ); + + await expect( + runReactRouterRscPrerenderBuild({ + api: { logger: createLogger({ level: 'silent' }) }, + hasWebEnvironment: true, + buildDirectory, + ssr: true, + prerenderConfig: true, + prerenderPaths: ['/about'], + basename: '/', + }) + ).rejects.toThrowError( + /^Prerender: Received a 500 status code from the RSC server while prerendering the `\/about` path\.$/ + ); + } finally { + await rm(buildDirectory, { recursive: true, force: true }); + } + }); +}); diff --git a/tests/rsc-route-config.test.ts b/tests/rsc-route-config.test.ts new file mode 100644 index 00000000..c1da949b --- /dev/null +++ b/tests/rsc-route-config.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, it } from '@rstest/core'; +import { createRscRouteConfig } from '../src/rsc-route-config'; + +describe('RSC route config', () => { + it('preserves declared sibling order', () => { + const code = createRscRouteConfig({ + appDirectory: '/app', + routes: { + root: { id: 'root', file: 'root.tsx' }, + 'routes/b': { + id: 'routes/b', + parentId: 'root', + path: 'b', + file: 'routes/b.tsx', + }, + 'routes/a': { + id: 'routes/a', + parentId: 'root', + path: 'a', + file: 'routes/a.tsx', + }, + }, + }); + + expect(code.indexOf('id: "routes/b"')).toBeLessThan( + code.indexOf('id: "routes/a"') + ); + }); +}); diff --git a/tests/rsc-route-exports.test.ts b/tests/rsc-route-exports.test.ts new file mode 100644 index 00000000..57c534fa --- /dev/null +++ b/tests/rsc-route-exports.test.ts @@ -0,0 +1,64 @@ +import { describe, expect, it } from '@rstest/core'; +import { + describeRscRouteExportConflict, + RSC_CLIENT_COMPONENT_EXPORTS, + RSC_MUTUALLY_EXCLUSIVE_ROUTE_EXPORTS, + RSC_ROUTE_COMPONENT_EXPORTS, + RSC_SERVER_COMPONENT_EXPORTS, +} from '../src/rsc-route-exports'; + +describe('RSC route export policy', () => { + it('keeps client and server component export pairs in one canonical order', () => { + expect(RSC_ROUTE_COMPONENT_EXPORTS).toEqual([ + { + routeProperty: 'Component', + clientExport: 'default', + serverExport: 'ServerComponent', + }, + { + routeProperty: 'Layout', + clientExport: 'Layout', + serverExport: 'ServerLayout', + }, + { + routeProperty: 'ErrorBoundary', + clientExport: 'ErrorBoundary', + serverExport: 'ServerErrorBoundary', + }, + { + routeProperty: 'HydrateFallback', + clientExport: 'HydrateFallback', + serverExport: 'ServerHydrateFallback', + }, + ]); + expect(RSC_CLIENT_COMPONENT_EXPORTS).toEqual([ + 'default', + 'Layout', + 'ErrorBoundary', + 'HydrateFallback', + ]); + expect(RSC_SERVER_COMPONENT_EXPORTS).toEqual([ + 'ServerComponent', + 'ServerLayout', + 'ServerErrorBoundary', + 'ServerHydrateFallback', + ]); + expect(RSC_MUTUALLY_EXCLUSIVE_ROUTE_EXPORTS).toEqual([ + ['default', 'ServerComponent'], + ['Layout', 'ServerLayout'], + ['ErrorBoundary', 'ServerErrorBoundary'], + ['HydrateFallback', 'ServerHydrateFallback'], + ]); + }); + + it('uses the same conflict wording for default and named exports', () => { + expect( + describeRscRouteExportConflict('default', 'ServerComponent') + ).toBe( + 'Module cannot have both a default export and a ServerComponent export' + ); + expect(describeRscRouteExportConflict('Layout', 'ServerLayout')).toBe( + 'Module cannot have both a Layout export and a ServerLayout export' + ); + }); +}); diff --git a/tests/rsc-route-transforms.test.ts b/tests/rsc-route-transforms.test.ts new file mode 100644 index 00000000..c5126006 --- /dev/null +++ b/tests/rsc-route-transforms.test.ts @@ -0,0 +1,614 @@ +import { describe, expect, it } from '@rstest/core'; +import { transformRscRouteModule } from '../src/rsc-route-transforms'; +import type { RouteChunkConfig } from '../src/route-chunks'; +import type { Route } from '../src/types'; + +const routeChunkConfig: RouteChunkConfig = { + splitRouteModules: false, + appDirectory: '/app', + rootRouteFile: 'root.tsx', +}; +const enforceRouteChunkConfig: RouteChunkConfig = { + ...routeChunkConfig, + splitRouteModules: 'enforce', +}; +const routeByFilePath = new Map([ + ['/app/routes/client.tsx', { id: 'routes/client', file: 'routes/client.tsx' }], + ['/app/routes/target.tsx', { id: 'routes/target', file: 'routes/target.tsx' }], +]); + +type TransformOverrides = Partial[0]> & + Pick[0], 'code' | 'resourcePath' | 'routeId'>; + +const transform = (overrides: TransformOverrides) => + transformRscRouteModule({ + isRootRoute: false, + routeChunkCache: new Map(), + routeChunkConfig, + isServerEnvironment: false, + isDev: false, + ...overrides, + }); + +describe('RSC route transforms', () => { + it('reexports the default root ErrorBoundary from root server route entries', async () => { + const result = await transform({ + code: ` + export default function Root() { + return null; + } + `, + resourcePath: '/app/root.tsx', + isRootRoute: true, + routeId: 'root', + isServerEnvironment: true, + }); + + expect(result.code).toContain( + 'export { ErrorBoundary } from "/app/root.tsx?client-route-module=shared";' + ); + expect(result.code).not.toContain('UNSAFE_RSCDefaultRootErrorBoundary'); + }); + + it('adds the default root ErrorBoundary to root shared client route chunks', async () => { + const result = await transform({ + code: ` + export default function Root() { + return null; + } + `, + resourcePath: '/app/root.tsx', + resourceQuery: '?client-route-module=shared', + isRootRoute: true, + routeId: 'root', + isServerEnvironment: true, + }); + + expect(result.code).toContain( + 'import { createElement as __rr_createElement } from "react";' + ); + expect(result.code).not.toContain('UNSAFE_RSCDefaultRootErrorBoundary'); + expect(result.code).toContain('export function ErrorBoundary()'); + expect(result.code).toContain('"Unexpected Server Error"'); + }); + + it('renders non-Error route rejections in the dev root ErrorBoundary', async () => { + // A react-router ErrorResponse (a plain object, no `.stack`) previously + // collapsed to "[object Object]" via String(error). The dev boundary must + // pretty-print such objects so the real payload is visible. + const result = await transform({ + code: ` + export default function Root() { + return null; + } + `, + resourcePath: '/app/root.tsx', + resourceQuery: '?client-route-module=shared', + isRootRoute: true, + routeId: 'root', + isServerEnvironment: true, + isDev: true, + }); + + expect(result.code).toContain('export function ErrorBoundary()'); + expect(result.code).toContain('function __rr_formatRouteError(error)'); + // Errors keep their (source-mapped) stack. + expect(result.code).toContain('if (error && error.stack) return String(error.stack)'); + // Non-Error objects fall back to a JSON dump instead of "[object Object]". + expect(result.code).toContain('JSON.stringify(error, null, 2)'); + + // Behavioral check: exercise the emitted formatter against an + // ErrorResponse-shaped object and a real Error. + const fnMatch = result.code.match( + /function __rr_formatRouteError[\s\S]*?\n\}\n/ + ); + expect(fnMatch).not.toBeNull(); + const format = new Function( + 'error', + `${fnMatch![0]}\nreturn __rr_formatRouteError(error);` + ) as (error: unknown) => string; + + const errorResponse = { + status: 404, + statusText: 'Not Found', + data: 'Error: No route matches URL', + internal: true, + }; + const formatted = format(errorResponse); + expect(formatted).not.toBe('[object Object]'); + expect(formatted).toContain('"status": 404'); + + const realError = new Error('boom'); + expect(format(realError)).toContain('Error: boom'); + }); + + it('targets client route chunks from the client route module query', async () => { + const result = await transform({ + code: ` + export async function loader() { + return null; + } + export async function clientLoader() { + return null; + } + export default function Route() { + return null; + } + `, + resourcePath: '/app/routes/client.tsx', + resourceQuery: '?client-route-module=clientLoader', + routeId: 'routes/client', + }); + + expect(result.code).toContain('export async function clientLoader()'); + expect(result.code).not.toContain('export async function loader()'); + expect(result.code).not.toContain('export default function Route()'); + }); + + it('targets RSC client route exports separately from shared exports', async () => { + const result = await transform({ + code: ` + export async function loader() { + return null; + } + export async function clientLoader() { + return null; + } + export const customExport = true; + export default function Route() { + return null; + } + `, + resourcePath: '/app/routes/client.tsx', + routeId: 'routes/client', + }); + + expect(result.code).toContain( + 'export const clientLoader = async (...args) => import("/app/routes/client.tsx?client-route-module=clientLoader")' + ); + expect(result.code).toContain( + 'export { customExport } from "/app/routes/client.tsx?client-route-module=shared";' + ); + expect(result.code).toContain( + 'export { default } from "/app/routes/client.tsx?client-route-module=route";' + ); + expect(result.code).not.toContain( + 'clientLoader")}).then(mod => mod.clientLoader' + ); + }); + + it('escapes route module ids embedded in generated dynamic imports', async () => { + const resourcePath = '/app/routes/quote"\\\\line\n);globalThis.pwned=true;//.tsx'; + const result = await transform({ + code: ` + export async function clientLoader() { + return null; + } + export function HydrateFallback() { + return null; + } + export default function Route() { + return null; + } + `, + resourcePath, + routeId: 'routes/adversarial', + }); + + for (const chunk of ['clientLoader', 'HydrateFallback']) { + const expectedTarget = `${resourcePath}?client-route-module=${chunk}`; + expect(result.code).toContain(`import(${JSON.stringify(expectedTarget)})`); + } + expect(result.code).not.toContain('import("/app/routes/quote"\\\\line'); + }); + + it('keeps unsplittable RSC client route exports in the shared client module', async () => { + const result = await transform({ + code: ` + const shared = true; + export async function clientLoader() { + return shared; + } + export default function Route() { + return shared ? null : null; + } + `, + resourcePath: '/app/routes/client.tsx', + routeId: 'routes/client', + }); + + expect(result.code).toContain( + 'export const clientLoader = async (...args) => import("/app/routes/client.tsx?client-route-module=route")' + ); + expect(result.code).toContain( + 'export { default } from "/app/routes/client.tsx?client-route-module=route";' + ); + }); + + it('rejects unsplittable clientMiddleware exports in enforce mode', async () => { + await expect( + transform({ + code: ` + const shared = true; + export const clientMiddleware = [ + async ({ request }, next) => { + return shared ? next() : undefined; + }, + ]; + export default function Route() { + return shared ? null : null; + } + `, + resourcePath: '/app/routes/client.tsx', + routeId: 'routes/client', + routeChunkConfig: enforceRouteChunkConfig, + }) + ).rejects.toThrowError( + /Error splitting route module: routes\/client[\s\S]*- clientMiddleware[\s\S]*This export[\s\S]*its own chunk[\s\S]*it shares/ + ); + }); + + it('refreshes default RSC client route modules when they update', async () => { + const result = await transform({ + code: 'export default function Route() { return null; }', + resourcePath: '/app/routes/client.tsx', + resourceQuery: '?client-route-module=route', + routeId: 'routes/client', + isDev: true, + }); + + expect(result.code).toContain('import.meta.webpackHot.accept(() =>'); + expect(result.code).toContain('const basename = router.basename || "/"'); + expect(result.code).toContain( + 'router.navigate(pathname + location.search + location.hash' + ); + // A trailing-slash basename (e.g. "/mybase/") consumes the leading slash + // when stripped, producing a relative `to` that react-router resolves + // against the current location and doubles into the URL (which route + // discovery then 404s). Re-assert absolute after stripping. + expect(result.code).toContain('if (pathname[0] !== "/") pathname = "/" + pathname'); + }); + + it('strips side-effect style imports from the data route chunk', async () => { + // The `?client-route-module=data` chunk carries only route data exports and + // must keep its client-manifest `cssFiles` empty so the native rspack + // `RscServerPlugin` never wraps these non-component exports in a + // CSS-injecting component wrapper. Assert the bare style side-effect import + // is dropped from the emitted data chunk. + const result = await transform({ + code: ` + import "./styles.css"; + export const meta = () => []; + export const links = () => []; + export default function Route() { return null; } + `, + resourcePath: '/app/routes/client.tsx', + resourceQuery: '?client-route-module=data', + routeId: 'routes/client', + }); + + expect(result.code).not.toContain('.css'); + expect(result.code).toContain('export const meta'); + expect(result.code).toContain('export const links'); + }); + + it('groups RSC client route exports in one route client module', async () => { + const result = await transform({ + code: ` + export async function loader() { + return null; + } + export async function clientLoader() { + return null; + } + export const customExport = true; + export default function Route() { + return null; + } + `, + resourcePath: '/app/routes/client.tsx', + resourceQuery: '?client-route-module=route', + routeId: 'routes/client', + }); + + expect(result.code).toContain('export async function clientLoader()'); + expect(result.code).toContain('export default function Route()'); + expect(result.code).not.toContain('export async function loader()'); + expect(result.code).not.toContain('export const customExport = true'); + }); + + it('keeps exported shared dependencies in unsplittable route client chunks', async () => { + const result = await transform({ + code: ` + export const shared = true; + export async function loader() { + return null; + } + export async function clientLoader() { + return shared; + } + export default function Route() { + return shared ? null : null; + } + `, + resourcePath: '/app/routes/client.tsx', + resourceQuery: '?client-route-module=route', + routeId: 'routes/client', + }); + + expect(result.code).toContain('const shared = true;'); + expect(result.code).toContain('export async function clientLoader()'); + expect(result.code).toContain('export default function Route()'); + expect(result.code).not.toContain('export async function loader()'); + }); + + it('keeps shared RSC route exports in shared client route chunks', async () => { + const result = await transform({ + code: ` + export async function loader() { + return null; + } + export async function clientLoader() { + return null; + } + export const customExport = true; + export default function Route() { + return null; + } + `, + resourcePath: '/app/routes/client.tsx', + resourceQuery: '?client-route-module=shared', + routeId: 'routes/client', + }); + + expect(result.code).toContain('export const customExport = true;'); + expect(result.code).not.toContain('export async function clientLoader()'); + expect(result.code).not.toContain('export default function Route()'); + }); + + it('re-adds a bare vanilla style import to the shared client chunk of a server-first route', async () => { + const result = await transform({ + code: ` + import * as localStyles from "./styles-vanilla-local.css"; + export function ServerComponent() { + return localStyles.index; + } + `, + resourcePath: '/app/routes/server.tsx', + resourceQuery: '?client-route-module=shared', + routeId: 'routes/server', + isServerEnvironment: true, + }); + + // The server component (and its value import) is stripped from the client + // module, so a bare side-effect import is re-added to make the client build + // extract the scoped stylesheet the server entry's `entryCssFiles` links. + expect(result.code).toContain('"use client";'); + expect(result.code).toContain('import "./styles-vanilla-local.css";'); + expect(result.code).not.toContain('export function ServerComponent()'); + }); + + it('does not re-add the vanilla style import to the data client chunk of a server-first route', async () => { + const result = await transform({ + code: ` + import * as localStyles from "./styles-vanilla-local.css"; + export function loader() { + return null; + } + export function ServerComponent() { + return localStyles.index; + } + `, + resourcePath: '/app/routes/server.tsx', + resourceQuery: '?client-route-module=data', + routeId: 'routes/server', + isServerEnvironment: true, + }); + + // The data chunk keeps `cssFiles` empty so RSC never wraps data exports in a + // CSS-injecting component; the vanilla import must not leak into it. + expect(result.code).not.toContain('import "./styles-vanilla-local.css";'); + }); + + it('does not re-add vanilla style imports to client (non-server-component) route chunks', async () => { + const result = await transform({ + code: ` + import * as localStyles from "./styles-vanilla-local.css"; + export default function Route() { + return localStyles.index; + } + `, + resourcePath: '/app/routes/client.tsx', + resourceQuery: '?client-route-module=route', + routeId: 'routes/client', + isServerEnvironment: true, + }); + + // A client route's component stays in the client graph, so its vanilla value + // import extracts normally — no synthetic bare import is added. + expect(result.code).not.toContain('import "./styles-vanilla-local.css";'); + expect(result.code).toContain('export default function Route()'); + }); + + it('rewrites RSC client route module imports to shared client modules', async () => { + const result = await transform({ + code: ` + import { customExport } from "./target"; + + export const handle = customExport; + export default function Route() { + return customExport ? null : null; + } + `, + resourcePath: '/app/routes/client.tsx', + resourceQuery: '?client-route-module=default', + routeId: 'routes/client', + routeByFilePath, + isServerEnvironment: true, + }); + + expect(result.code).toContain( + 'from \"/app/routes/target.tsx?client-route-module=shared\"' + ); + expect(result.code).not.toContain('from \"./target\"'); + }); + + it('targets server route modules from the server route module query', async () => { + const result = await transform({ + code: ` + export async function loader() { + return null; + } + export async function clientLoader() { + return null; + } + export default function Route() { + return null; + } + `, + resourcePath: '/app/routes/server.tsx', + resourceQuery: '?server-route-module=', + routeId: 'routes/server', + isServerEnvironment: true, + }); + + expect(result.code).toContain('export async function loader()'); + expect(result.code).not.toContain('export async function clientLoader()'); + expect(result.code).not.toContain('export default function Route()'); + }); + + it('marks server-first route modules as server entries so RSC collects their CSS', async () => { + const result = await transform({ + code: ` + import "./styles.css"; + export function ServerComponent() { + return null; + } + `, + resourcePath: '/app/routes/server.tsx', + resourceQuery: '?server-route-module=', + routeId: 'routes/server', + isServerEnvironment: true, + }); + + expect(result.code).toContain("'use server-entry';"); + expect(result.code).toContain('import "./styles.css"'); + expect(result.code).toContain('export function ServerComponent()'); + }); + + it('re-adds a bare side-effect import for value-imported vanilla styles in server-first route modules', async () => { + const result = await transform({ + code: ` + import * as localStyles from "./styles-vanilla-local.css"; + export function ServerComponent() { + return localStyles.index; + } + `, + resourcePath: '/app/routes/server.tsx', + resourceQuery: '?server-route-module=', + routeId: 'routes/server', + isServerEnvironment: true, + }); + + // The value import (used for the scoped class name) survives, and a bare + // side-effect import of the same file is added so the vanilla CSS side + // effect is retained in the server graph and recorded in `entryCssFiles`. + expect(result.code).toContain("'use server-entry';"); + expect(result.code).toContain( + 'import * as localStyles from "./styles-vanilla-local.css"' + ); + expect(result.code).toContain('import "./styles-vanilla-local.css";'); + }); + + it('does not re-add bare imports for CSS Modules or `?url` value imports in server-first route modules', async () => { + const result = await transform({ + code: ` + import moduleStyles from "./styles.module.css"; + import cssUrl from "./styles.css?url"; + export function ServerComponent() { + return moduleStyles.index + cssUrl; + } + `, + resourcePath: '/app/routes/server.tsx', + resourceQuery: '?server-route-module=', + routeId: 'routes/server', + isServerEnvironment: true, + }); + + // CSS Modules are handled by the native CSS pipeline and `?url` imports ride + // the asset-relocation path; neither should gain a synthetic bare import. + expect(result.code).not.toContain('import "./styles.module.css";'); + expect(result.code).not.toContain('import "./styles.css?url";'); + expect(result.code).not.toContain('import "./styles.css";'); + }); + + it('does not mark data-only server route modules as server entries', async () => { + const result = await transform({ + code: ` + export async function loader() { + return null; + } + `, + resourcePath: '/app/routes/server.tsx', + resourceQuery: '?server-route-module=', + routeId: 'routes/server', + isServerEnvironment: true, + }); + + expect(result.code).not.toContain('use server-entry'); + expect(result.code).toContain('export async function loader()'); + }); + + it('streams server-entry CSS links from server-first route entries', async () => { + const result = await transform({ + code: ` + export function ServerComponent() { + return null; + } + `, + resourcePath: '/app/routes/server.tsx', + routeId: 'routes/server', + isServerEnvironment: true, + }); + + expect(result.code).toContain( + 'import { ServerComponent as ServerComponentWithoutClientChunk } from "/app/routes/server.tsx?server-route-module="' + ); + expect(result.code).toContain( + '...(ServerComponentWithoutClientChunk.entryCssFiles ?? []).map(href =>' + ); + expect(result.code).toContain( + 'React.createElement("link", { key: href, rel: "stylesheet", href: href, precedence: "default" })' + ); + expect(result.code).toContain( + 'React.createElement(ServerComponentWithoutClientChunk, props)' + ); + }); + + it('preserves client component initializer side effects in server route modules', async () => { + const result = await transform({ + code: ` + export async function loader() { + return globalThis.componentCount; + } + export const clientLoader = (() => { + globalThis.clientLoaderCount = true; + return async () => null; + })(); + const Route = (() => { + globalThis.componentCount = true; + return () => null; + })(); + export default Route; + `, + resourcePath: '/app/routes/server.tsx', + resourceQuery: '?server-route-module=', + routeId: 'routes/server', + isServerEnvironment: true, + }); + + expect(result.code).toContain('globalThis.componentCount = true'); + expect(result.code).not.toContain('globalThis.clientLoaderCount = true'); + expect(result.code).not.toContain('export default'); + }); +}); diff --git a/tests/rsc-support.test.ts b/tests/rsc-support.test.ts new file mode 100644 index 00000000..daa8dbda --- /dev/null +++ b/tests/rsc-support.test.ts @@ -0,0 +1,188 @@ +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { describe, expect, it } from '@rstest/core'; +import { + assertReactRouterRscConfigSupport, + assertReactRouterRscSupport, + createReactRouterRscResolveAliases, + createReactRouterRscVirtualModules, + setupReactRouterRscPlugin, +} from '../src/rsc-support'; + +describe('RSC support helpers', () => { + it('creates aliases for upstream colon virtual IDs and internal client modules', () => { + const aliases = createReactRouterRscResolveAliases('/repo'); + + expect(aliases).toMatchObject({ + 'virtual:react-router/unstable_rsc/routes': expect.stringContaining( + 'virtual/react-router/unstable_rsc/routes.js' + ), + 'virtual/react-router/unstable_rsc/routes': expect.stringContaining( + 'virtual/react-router/unstable_rsc/routes.js' + ), + 'react-router/internal/react-server-client': expect.stringContaining( + 'virtual/react-router/rsc-internal-client.js' + ), + }); + }); + + it('creates only RSC virtual modules with normalized bootstrap scripts', () => { + const modules = createReactRouterRscVirtualModules({ + allowedActionOrigins: ['https://app.example.com'], + appDirectory: '/repo/app', + basename: '/', + buildDirectory: '/repo/build', + isBuild: false, + jsDistPath: 'custom/js', + outputClientPath: '/repo/build/client', + publicPath: '/assets', + routeDiscovery: { mode: 'initial' }, + routes: { + root: { + id: 'root', + file: 'root.tsx', + path: '', + }, + }, + ssr: true, + }); + + expect(modules['virtual/react-router/server-build']).toBeUndefined(); + expect( + modules['virtual/react-router/unstable_rsc/bootstrap-scripts'] + ).toContain('/assets/custom/js/index.js'); + // The RSC HMR runtime only self-accepts; the single `rsc:update` navigate + // handler now lives in the RSC client entry, not this virtual module. + expect( + modules['virtual/react-router/unstable_rsc/inject-hmr-runtime'] + ).toContain('import.meta.webpackHot.accept()'); + expect( + modules['virtual/react-router/unstable_rsc/allowed-action-origins'] + ).toBe('export default ["https://app.example.com"];'); + expect( + modules['virtual/react-router/unstable_rsc/server-manifest'] + ).toContain('__webpack_require__.rscM?.serverManifest'); + }); + + it('defaults RSC route discovery for SSR and SPA mode', () => { + const createModules = ( + ssr: boolean, + routeDiscovery?: Parameters< + typeof createReactRouterRscVirtualModules + >[0]['routeDiscovery'] + ) => + createReactRouterRscVirtualModules({ + allowedActionOrigins: undefined, + appDirectory: '/repo/app', + basename: '/', + buildDirectory: '/repo/build', + isBuild: true, + jsDistPath: 'static/js', + outputClientPath: '/repo/build/client', + publicPath: '/', + routeDiscovery, + routes: {}, + ssr, + }); + + expect( + createModules(true)[ + 'virtual/react-router/unstable_rsc/route-discovery' + ] + ).toBe('export default {"mode":"lazy"};'); + expect( + createModules(false, { mode: 'lazy' })[ + 'virtual/react-router/unstable_rsc/route-discovery' + ] + ).toBe('export default {"mode":"initial"};'); + }); + + it('rejects config options RSC framework mode does not support', () => { + expect(() => + assertReactRouterRscConfigSupport({ + pluginName: 'test-plugin', + userConfig: { + buildEnd: async () => {}, + serverBundles: () => 'bundle', + subResourceIntegrity: true, + }, + }) + ).toThrow( + /does not currently support[\s\S]*- buildEnd[\s\S]*- serverBundles[\s\S]*- subResourceIntegrity/ + ); + + expect(() => + assertReactRouterRscConfigSupport({ + pluginName: 'test-plugin', + userConfig: { ssr: true, basename: '/app' }, + }) + ).not.toThrow(); + }); + + it('rejects the legacy SRI future alias after config normalization', () => { + expect(() => + assertReactRouterRscConfigSupport({ + pluginName: 'test-plugin', + userConfig: { + future: { unstable_subResourceIntegrity: true }, + subResourceIntegrity: true, + }, + }) + ).toThrow(/subResourceIntegrity/); + }); + + it('rejects separately registered rsbuild-plugin-rsc instances', async () => { + await expect( + setupReactRouterRscPlugin({ + api: { + isPluginExists: () => true, + } as any, + entryRscPath: '/repo/app/entry.rsc.tsx', + entrySsrPath: '/repo/app/entry.rsc.ssr.tsx', + pluginName: 'test-plugin', + rsc: {}, + }) + ).rejects.toThrow(/already registered/); + }); + + it('rejects React Router versions before the RSC export surface', () => { + const tempDir = mkdtempSync(join(tmpdir(), 'rr-rsc-support-')); + const packageJsonPath = join(tempDir, 'react-router.json'); + writeFileSync(packageJsonPath, JSON.stringify({ version: '7.17.0' })); + + try { + expect(() => + assertReactRouterRscSupport({ + pluginName: 'test-plugin', + resolvePackagePath: specifier => + specifier === 'react-router/package.json' + ? packageJsonPath + : '/repo/node_modules/fake.js', + }) + ).toThrow('requires react-router >=7.18.0 or >=8.0.0'); + } finally { + rmSync(tempDir, { force: true, recursive: true }); + } + }); + + it('rejects missing RSC runtime dependencies', () => { + const tempDir = mkdtempSync(join(tmpdir(), 'rr-rsc-support-')); + const packageJsonPath = join(tempDir, 'react-router.json'); + writeFileSync(packageJsonPath, JSON.stringify({ version: '8.0.1' })); + + try { + expect(() => + assertReactRouterRscSupport({ + pluginName: 'test-plugin', + resolvePackagePath: specifier => + specifier === 'react-router/package.json' + ? packageJsonPath + : undefined, + }) + ).toThrow('requires `react-server-dom-rspack/client.browser`'); + } finally { + rmSync(tempDir, { force: true, recursive: true }); + } + }); +}); diff --git a/tests/server-utils.test.ts b/tests/server-utils.test.ts index 8f01667b..f2a64d91 100644 --- a/tests/server-utils.test.ts +++ b/tests/server-utils.test.ts @@ -104,17 +104,6 @@ describe('resolveReactRouterServerBuild', () => { ).resolves.toMatchObject({ assets: { version: 'async' } }); }); - it('resolves server builds through the Effect path', async () => { - const build = createBuild('effect'); - - await expect( - resolveReactRouterServerBuild({ - ...build, - assets: async () => build.assets, - }) - ).resolves.toMatchObject({ assets: { version: 'effect' } }); - }); - it('rejects modules without a React Router server build', async () => { await expect( resolveReactRouterServerBuild({ default: { routes: {} } }) diff --git a/tests/setup.ts b/tests/setup.ts index 4b5f77df..cb449e62 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -149,7 +149,6 @@ rstest.mock('@scripts/test-helper', () => ({ const mergeRsbuildConfig = (a: any, b: any) => deepMerge(a, b); const pending: Promise[] = []; - const stub: any = { addPlugins: rstest.fn(), unwrapConfig: rstest.fn(), @@ -158,13 +157,17 @@ rstest.mock('@scripts/test-helper', () => ({ onAfterStartDevServer: rstest.fn(), onCloseDevServer: rstest.fn(), onCloseBuild: rstest.fn(), + onExit: rstest.fn(), onBeforeBuild: rstest.fn(), onAfterBuild: rstest.fn(), onBeforeDevCompile: rstest.fn(), onAfterDevCompile: rstest.fn(), + onBeforeCreateCompiler: rstest.fn(), onAfterCreateCompiler: rstest.fn(), getNormalizedConfig: rstest.fn().mockImplementation(() => mergedConfig), + isPluginExists: rstest.fn().mockReturnValue(false), modifyRsbuildConfig: rstest.fn(), + modifyBundlerChain: rstest.fn(), onAfterEnvironmentCompile: rstest.fn(), // Keep as a spy-only hook; tests in this repo assert against the merged // Rsbuild config (modifyRsbuildConfig), not post-normalization environment @@ -207,8 +210,11 @@ rstest.mock('@scripts/test-helper', () => ({ }); // In Rsbuild, `addPlugins()` triggers plugin setup before config is read. + const flattenPlugins = (plugins: any[]): any[] => + plugins.flatMap(plugin => (Array.isArray(plugin) ? flattenPlugins(plugin) : plugin)); + stub.addPlugins.mockImplementation((next: any[]) => { - for (const plugin of next) { + for (const plugin of flattenPlugins(next)) { if (typeof plugin?.setup === 'function') { // Tests do not await `addPlugins`, so ensure `unwrapConfig` waits for setup. pending.push(Promise.resolve(plugin.setup(stub))); diff --git a/tests/spa-mode.test.ts b/tests/spa-mode.test.ts index 0fdc20ef..97225bae 100644 --- a/tests/spa-mode.test.ts +++ b/tests/spa-mode.test.ts @@ -21,6 +21,7 @@ describe('SPA Mode (ssr: false)', () => { basename: '/', appDirectory: 'app', ssr: false, + isSpaMode: true, routeDiscovery: { mode: 'initial' }, }); @@ -28,6 +29,52 @@ describe('SPA Mode (ssr: false)', () => { expect(result).toContain('export const ssr = false'); }); + it('should only import the root route when ssr is false', () => { + const result = generateServerBuild( + { + rootKey: mockRoutes.root, + homeKey: mockRoutes['routes/home'], + }, + { + entryServerPath: './app/entry.server.tsx', + assetsBuildDirectory: 'build/client', + basename: '/', + appDirectory: 'app', + ssr: false, + isSpaMode: true, + routeDiscovery: { mode: 'initial' }, + } + ); + + expect(result).toMatch(/import \* as route0 from ".+\/app\/root\.tsx";/); + expect(result).not.toContain('routes/home.tsx'); + expect(result).toContain('const route1 = { default: () => null };'); + }); + + it('should import non-root routes for ssr false prerender paths', () => { + const result = generateServerBuild( + { + rootKey: mockRoutes.root, + homeKey: mockRoutes['routes/home'], + }, + { + entryServerPath: './app/entry.server.tsx', + assetsBuildDirectory: 'build/client', + basename: '/', + appDirectory: 'app', + ssr: false, + isSpaMode: false, + prerender: ['/'], + routeDiscovery: { mode: 'initial' }, + } + ); + + expect(result).toMatch(/import \* as route0 from ".+\/app\/root\.tsx";/); + expect(result).toMatch( + /import \* as route1 from ".+\/app\/routes\/home\.tsx";/ + ); + }); + it('should set isSpaMode to false when ssr is true', () => { const result = generateServerBuild(mockRoutes, { entryServerPath: './app/entry.server.tsx', @@ -35,6 +82,7 @@ describe('SPA Mode (ssr: false)', () => { basename: '/', appDirectory: 'app', ssr: true, + isSpaMode: false, routeDiscovery: { mode: 'lazy', manifestPath: '/__manifest' }, }); @@ -55,6 +103,7 @@ describe('SPA Mode (ssr: false)', () => { basename: '/', appDirectory: 'app', ssr: false, + isSpaMode: true, routeDiscovery: { mode: 'initial' }, }); @@ -68,6 +117,7 @@ describe('SPA Mode (ssr: false)', () => { basename: '/', appDirectory: 'app', ssr: true, + isSpaMode: false, routeDiscovery: { mode: 'lazy', manifestPath: '/__manifest' }, }); @@ -88,6 +138,7 @@ describe('SPA Mode (ssr: false)', () => { basename: '/', appDirectory: 'app', ssr: true, + isSpaMode: false, federation: true, routeDiscovery: { mode: 'lazy', manifestPath: '/__manifest' }, }); @@ -104,6 +155,7 @@ describe('SPA Mode (ssr: false)', () => { basename: '/', appDirectory: 'app', ssr: true, + isSpaMode: false, federation: false, routeDiscovery: { mode: 'lazy', manifestPath: '/__manifest' }, }); @@ -126,6 +178,7 @@ describe('SPA Mode (ssr: false)', () => { basename: '/my-app', appDirectory: 'app', ssr: true, + isSpaMode: false, routeDiscovery: { mode: 'lazy', manifestPath: '/__manifest' }, }); @@ -139,6 +192,7 @@ describe('SPA Mode (ssr: false)', () => { basename: '/', appDirectory: 'app', ssr: true, + isSpaMode: false, routeDiscovery: { mode: 'lazy', manifestPath: '/__manifest' }, }); @@ -158,6 +212,7 @@ describe('SPA Mode (ssr: false)', () => { basename: '/', appDirectory: 'app', ssr: true, + isSpaMode: false, future: { v3_fetcherPersist: true }, routeDiscovery: { mode: 'lazy', manifestPath: '/__manifest' }, }); @@ -172,6 +227,7 @@ describe('SPA Mode (ssr: false)', () => { basename: '/', appDirectory: 'app', ssr: true, + isSpaMode: false, routeDiscovery: { mode: 'lazy', manifestPath: '/__manifest' }, }); @@ -191,6 +247,7 @@ describe('SPA Mode (ssr: false)', () => { basename: '/', appDirectory: 'app', ssr: true, + isSpaMode: false, allowedActionOrigins: ['https://example.com'], routeDiscovery: { mode: 'lazy', manifestPath: '/__manifest' }, }); diff --git a/tests/ssr-asset-relocation.test.ts b/tests/ssr-asset-relocation.test.ts new file mode 100644 index 00000000..97e8b1d8 --- /dev/null +++ b/tests/ssr-asset-relocation.test.ts @@ -0,0 +1,238 @@ +import { describe, expect, it } from '@rstest/core'; +import { join } from 'pathe'; +import { + collectRelocatableServerAssets, + isRelocatableServerAsset, + relocateServerAssetsToClient, + type RelocatableAssetCompilation, + type RelocatableAssetInfo, +} from '../src/ssr-asset-relocation'; + +type FakeAsset = { + info: RelocatableAssetInfo; + buffer: Buffer; +}; + +const createCompilation = ( + assets: Record +): RelocatableAssetCompilation & { deleted: string[] } => { + const store: Record = { ...assets }; + const deleted: string[] = []; + return { + deleted, + assets: store, + getAsset(name: string) { + const asset = store[name]; + if (!asset) { + return undefined; + } + return { + name, + source: { buffer: () => asset.buffer }, + info: asset.info, + }; + }, + deleteAsset(name: string) { + deleted.push(name); + delete store[name]; + }, + }; +}; + +describe('isRelocatableServerAsset', () => { + it('treats asset-module outputs (with sourceFilename) as relocatable', () => { + expect( + isRelocatableServerAsset({ sourceFilename: 'app/assets/test.txt?url' }) + ).toBe(true); + expect( + isRelocatableServerAsset({ sourceFilename: 'app/assets/test.css' }) + ).toBe(true); + }); + + it('excludes JavaScript chunks even when they carry a sourceFilename', () => { + expect( + isRelocatableServerAsset({ + sourceFilename: 'app/entry.server.tsx', + javascriptModule: true, + }) + ).toBe(false); + }); + + it('excludes generated files with no sourceFilename (e.g. package.json)', () => { + expect(isRelocatableServerAsset({})).toBe(false); + expect(isRelocatableServerAsset(undefined)).toBe(false); + expect(isRelocatableServerAsset({ javascriptModule: true })).toBe(false); + }); +}); + +describe('collectRelocatableServerAssets', () => { + it('returns only static assets, sorted by public path', () => { + const compilation = createCompilation({ + 'static/js/app.js': { + info: { javascriptModule: true, sourceFilename: 'app/entry.tsx' }, + buffer: Buffer.from('js'), + }, + 'package.json': { info: {}, buffer: Buffer.from('{}') }, + 'static/assets/test.4fdcca.txt': { + info: { sourceFilename: 'app/assets/test.txt?url' }, + buffer: Buffer.from('test'), + }, + 'static/assets/test.454e65.css': { + info: { sourceFilename: 'app/assets/test.css?url' }, + buffer: Buffer.from('.test{color:red}'), + }, + 'static/js/async/780.js': { + info: { javascriptModule: true, sourceFilename: 'app/lib.ts' }, + buffer: Buffer.from('chunk'), + }, + }); + + const collected = collectRelocatableServerAssets(compilation); + + expect(collected.map(asset => asset.name)).toEqual([ + 'static/assets/test.454e65.css', + 'static/assets/test.4fdcca.txt', + ]); + }); +}); + +describe('relocateServerAssetsToClient', () => { + it('writes missing assets into the client output and strips them from the server build', async () => { + const outputClientPath = '/build/client'; + const compilation = createCompilation({ + 'static/assets/test.4fdcca.txt': { + info: { sourceFilename: 'app/assets/test.txt?url' }, + buffer: Buffer.from('test'), + }, + 'static/assets/test.454e65.css': { + info: { sourceFilename: 'app/assets/test.css?url' }, + buffer: Buffer.from('.test{color:red}'), + }, + 'static/js/app.js': { + info: { javascriptModule: true, sourceFilename: 'app/entry.tsx' }, + buffer: Buffer.from('js'), + }, + 'static/js/async/780.js': { + info: { javascriptModule: true, sourceFilename: 'app/lib.ts' }, + buffer: Buffer.from('chunk'), + }, + }); + + const mkdirCalls: string[] = []; + const writes: Array<{ path: string; data: string }> = []; + const renames: Array<{ from: string; to: string }> = []; + + const result = await relocateServerAssetsToClient({ + compilation, + outputClientPath, + existsSyncFn: () => false, + mkdirFn: async dir => { + mkdirCalls.push(dir); + }, + writeFileFn: async (path, data) => { + writes.push({ path, data: data.toString('utf8') }); + }, + renameFn: async (from, to) => { + renames.push({ from, to }); + }, + }); + + expect(result.written).toEqual([ + 'static/assets/test.454e65.css', + 'static/assets/test.4fdcca.txt', + ]); + expect(result.skipped).toEqual([]); + + expect(writes.map(write => write.data)).toEqual([ + '.test{color:red}', + 'test', + ]); + expect(renames).toEqual([ + { + from: expect.stringContaining( + join(outputClientPath, 'static/assets/test.454e65.css.tmp-') + ), + to: join(outputClientPath, 'static/assets/test.454e65.css'), + }, + { + from: expect.stringContaining( + join(outputClientPath, 'static/assets/test.4fdcca.txt.tmp-') + ), + to: join(outputClientPath, 'static/assets/test.4fdcca.txt'), + }, + ]); + + // Static assets removed from the server build; JS chunks untouched. + expect(compilation.deleted.sort()).toEqual([ + 'static/assets/test.454e65.css', + 'static/assets/test.4fdcca.txt', + ]); + expect(compilation.assets['static/js/app.js']).toBeDefined(); + expect(compilation.assets['static/js/async/780.js']).toBeDefined(); + }); + + it('does not overwrite an asset already present in the client build but still strips it from the server', async () => { + const compilation = createCompilation({ + 'static/assets/shared.abc.txt': { + info: { sourceFilename: 'app/assets/shared.txt?url' }, + buffer: Buffer.from('shared'), + }, + }); + + const writes: string[] = []; + + const result = await relocateServerAssetsToClient({ + compilation, + outputClientPath: '/build/client', + existsSyncFn: () => true, + mkdirFn: async () => undefined, + readFileFn: async () => Buffer.from('shared'), + writeFileFn: async path => { + writes.push(path); + }, + renameFn: async () => undefined, + }); + + expect(result.written).toEqual([]); + expect(result.skipped).toEqual(['static/assets/shared.abc.txt']); + expect(writes).toEqual([]); + expect(compilation.deleted).toEqual(['static/assets/shared.abc.txt']); + }); + + it('overwrites an existing stale asset atomically', async () => { + const compilation = createCompilation({ + 'static/assets/shared.css': { + info: { sourceFilename: 'app/assets/shared.css' }, + buffer: Buffer.from('.fresh{color:green}'), + }, + }); + const writes: Array<{ path: string; data: string }> = []; + const renames: Array<{ from: string; to: string }> = []; + + const result = await relocateServerAssetsToClient({ + compilation, + outputClientPath: '/build/client', + existsSyncFn: () => true, + mkdirFn: async () => undefined, + readFileFn: async () => Buffer.from('.stale{color:red}'), + writeFileFn: async (path, data) => { + writes.push({ path, data: data.toString('utf8') }); + }, + renameFn: async (from, to) => { + renames.push({ from, to }); + }, + }); + + expect(result.written).toEqual(['static/assets/shared.css']); + expect(result.skipped).toEqual([]); + expect(writes).toHaveLength(1); + expect(writes[0].data).toBe('.fresh{color:green}'); + expect(renames).toEqual([ + { + from: writes[0].path, + to: '/build/client/static/assets/shared.css', + }, + ]); + expect(compilation.deleted).toEqual(['static/assets/shared.css']); + }); +}); diff --git a/tests/typegen.test.ts b/tests/typegen.test.ts index a67c89bb..4afa934c 100644 --- a/tests/typegen.test.ts +++ b/tests/typegen.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it, rstest } from '@rstest/core'; import type { ResultPromise } from 'execa'; +import { createPluginEffectRuntime } from '../src/effect-runtime'; import { createReactRouterTypegenRunner, registerReactRouterTypegen, @@ -18,6 +19,52 @@ const createProcess = () => { return { process, rejectProcess }; }; +const createDefaultTypegenRunner = () => ({ + startWatch: rstest.fn().mockResolvedValue(undefined), + closeWatch: rstest.fn().mockResolvedValue(undefined), + runBuild: rstest.fn().mockResolvedValue(undefined), +}); + +type TypegenRunnerSpies = ReturnType; + +const createTypegenRegistrationHarness = ( + { + action = 'dev', + runner: runnerOverrides, + }: { + action?: 'dev' | 'build'; + runner?: Partial; + } = {} +) => { + let afterDevCompile: (() => void) | undefined; + const runnerSpies = { ...createDefaultTypegenRunner(), ...runnerOverrides }; + const runner: ReactRouterTypegenRunner = runnerSpies; + const runtime = createPluginEffectRuntime(); + const api = { + context: { action }, + logger: { warn: rstest.fn() }, + onAfterDevCompile: rstest.fn((callback: () => void) => { + afterDevCompile = callback; + }), + onBeforeStartDevServer: rstest.fn(), + onCloseDevServer: rstest.fn(), + onBeforeBuild: rstest.fn(), + }; + + return { + runtime, + api, + runner, + ...runnerSpies, + get afterDevCompile(): () => void { + if (!afterDevCompile) { + throw new Error('expected onAfterDevCompile to be registered'); + } + return afterDevCompile; + }, + }; +}; + describe('React Router typegen runner', () => { it('starts one watch process and kills it on close', async () => { const first = createProcess(); @@ -56,6 +103,23 @@ describe('React Router typegen runner', () => { expect(execa).toHaveBeenCalledTimes(2); }); + it('does not launch a watch after close finishes during execa loading', async () => { + let resolveExeca!: (execa: ReturnType) => void; + const execaLoaded = new Promise>(resolve => { + resolveExeca = resolve; + }); + const process = createProcess(); + const execa = rstest.fn().mockReturnValue(process.process); + const runner = createReactRouterTypegenRunner(async () => execaLoaded); + + const startWatch = runner.startWatch(); + await runner.closeWatch(); + resolveExeca(execa); + await startWatch; + + expect(execa).not.toHaveBeenCalled(); + }); + it('runs one-shot build typegen through npx when no app directory is given', async () => { const execa = rstest.fn().mockResolvedValue(undefined); const runner = createReactRouterTypegenRunner(async () => execa); @@ -104,126 +168,82 @@ describe('React Router typegen runner', () => { }); it('starts dev watch after the first dev compile without blocking startup', async () => { - let afterDevCompile!: () => void; - const startWatch = rstest.fn().mockResolvedValue(undefined); - const runner: ReactRouterTypegenRunner = { - startWatch, - closeWatch: rstest.fn().mockResolvedValue(undefined), - runBuild: rstest.fn().mockResolvedValue(undefined), - }; - const api = { - context: { action: 'dev' }, - logger: { warn: rstest.fn() }, - onAfterDevCompile: rstest.fn(callback => { - afterDevCompile = callback; - }), - onBeforeStartDevServer: rstest.fn(), - onCloseDevServer: rstest.fn(), - onBeforeBuild: rstest.fn(), - }; - - registerReactRouterTypegen(api as never, { runner, devWatchDelayMs: 0 }); - - expect(api.onBeforeStartDevServer).not.toHaveBeenCalled(); - const result = afterDevCompile(); + const harness = createTypegenRegistrationHarness(); + + await registerReactRouterTypegen(harness.api as never, { + runtime: harness.runtime, + runner: harness.runner, + devWatchDelayMs: 0, + }); + + expect(harness.api.onBeforeStartDevServer).not.toHaveBeenCalled(); + const result = harness.afterDevCompile(); expect(result).toBeUndefined(); - afterDevCompile(); - expect(startWatch).not.toHaveBeenCalled(); - await expect.poll(() => startWatch.mock.calls.length).toBe(1); + harness.afterDevCompile(); + expect(harness.startWatch).not.toHaveBeenCalled(); + await expect.poll(() => harness.startWatch.mock.calls.length).toBe(1); + await harness.runtime.dispose(); }); it('defers the dev watch while compiles keep happening', async () => { - let afterDevCompile!: () => void; - const startWatch = rstest.fn().mockResolvedValue(undefined); - const runner: ReactRouterTypegenRunner = { - startWatch, - closeWatch: rstest.fn().mockResolvedValue(undefined), - runBuild: rstest.fn().mockResolvedValue(undefined), - }; - const api = { - context: { action: 'dev' }, - logger: { warn: rstest.fn() }, - onAfterDevCompile: rstest.fn(callback => { - afterDevCompile = callback; - }), - onBeforeStartDevServer: rstest.fn(), - onCloseDevServer: rstest.fn(), - onBeforeBuild: rstest.fn(), - }; - - registerReactRouterTypegen(api as never, { runner, devWatchDelayMs: 200 }); + const harness = createTypegenRegistrationHarness(); + + await registerReactRouterTypegen(harness.api as never, { + runtime: harness.runtime, + runner: harness.runner, + devWatchDelayMs: 200, + }); // Compiles arriving faster than the idle delay keep pushing the start out. for (let i = 0; i < 4; i += 1) { - afterDevCompile(); + harness.afterDevCompile(); await new Promise(resolve => setTimeout(resolve, 50)); - expect(startWatch).not.toHaveBeenCalled(); + expect(harness.startWatch).not.toHaveBeenCalled(); } await expect - .poll(() => startWatch.mock.calls.length, { timeout: 1000 }) + .poll(() => harness.startWatch.mock.calls.length, { timeout: 1000 }) .toBe(1); // Once started, later compiles do not restart or duplicate the watch. - afterDevCompile(); + harness.afterDevCompile(); await new Promise(resolve => setTimeout(resolve, 100)); - expect(startWatch).toHaveBeenCalledTimes(1); + expect(harness.startWatch).toHaveBeenCalledTimes(1); + await harness.runtime.dispose(); }); - it('cancels delayed dev watch startup on close', async () => { - let afterDevCompile!: () => void; - let closeDevServer!: () => Promise; - const startWatch = rstest.fn().mockResolvedValue(undefined); - const closeWatch = rstest.fn().mockResolvedValue(undefined); - const runner: ReactRouterTypegenRunner = { - startWatch, - closeWatch, - runBuild: rstest.fn().mockResolvedValue(undefined), - }; - const api = { - context: { action: 'dev' }, - logger: { warn: rstest.fn() }, - onAfterDevCompile: rstest.fn(callback => { - afterDevCompile = callback; - }), - onBeforeStartDevServer: rstest.fn(), - onCloseDevServer: rstest.fn(callback => { - closeDevServer = callback; - }), - onBeforeBuild: rstest.fn(), - }; - - registerReactRouterTypegen(api as never, { - runner, - devWatchDelayMs: 1000, + it('cancels delayed startup and surfaces close failures during disposal', async () => { + const closeWatch = rstest + .fn() + .mockRejectedValue(new Error('typegen close failed')); + const harness = createTypegenRegistrationHarness({ + runner: { closeWatch }, + }); + + await registerReactRouterTypegen(harness.api as never, { + runtime: harness.runtime, + runner: harness.runner, + devWatchDelayMs: 25, }); - afterDevCompile(); - await closeDevServer(); - await new Promise(resolve => setTimeout(resolve, 20)); + harness.afterDevCompile(); + await expect(harness.runtime.dispose()).rejects.toThrow('typegen close failed'); + await new Promise(resolve => setTimeout(resolve, 50)); - expect(startWatch).not.toHaveBeenCalled(); + expect(harness.startWatch).not.toHaveBeenCalled(); expect(closeWatch).toHaveBeenCalledTimes(1); }); - it('does not register the dev watch hook during production builds', () => { - const runner: ReactRouterTypegenRunner = { - startWatch: rstest.fn().mockResolvedValue(undefined), - closeWatch: rstest.fn().mockResolvedValue(undefined), - runBuild: rstest.fn().mockResolvedValue(undefined), - }; - const api = { - context: { action: 'build' }, - logger: { warn: rstest.fn() }, - onAfterDevCompile: rstest.fn(), - onBeforeStartDevServer: rstest.fn(), - onCloseDevServer: rstest.fn(), - onBeforeBuild: rstest.fn(), - }; - - registerReactRouterTypegen(api as never, { runner }); - - expect(api.onAfterDevCompile).not.toHaveBeenCalled(); - expect(api.onBeforeBuild).toHaveBeenCalled(); + it('does not register the dev watch hook during production builds', async () => { + const harness = createTypegenRegistrationHarness({ action: 'build' }); + + await registerReactRouterTypegen(harness.api as never, { + runtime: harness.runtime, + runner: harness.runner, + }); + + expect(harness.api.onAfterDevCompile).not.toHaveBeenCalled(); + expect(harness.api.onBeforeBuild).toHaveBeenCalled(); + await harness.runtime.dispose(); }); }); diff --git a/tsconfig.tests.json b/tsconfig.tests.json new file mode 100644 index 00000000..e33e8959 --- /dev/null +++ b/tsconfig.tests.json @@ -0,0 +1,9 @@ +{ + "extends": "./config/tsconfig-node16.json", + "compilerOptions": { + "composite": false, + "noEmit": true, + "rootDir": "." + }, + "include": ["tests/react-router-framework-*.test.ts"] +}