diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..dbb2ecb --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [["@module-federation/tanstack"]], + "linked": [], + "access": "public", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3a5cd73 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,38 @@ +name: CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + verify: + name: Verify (Node ${{ matrix.node }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node: ["22.18.0", "24", "26"] + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + - uses: pnpm/action-setup@b0f76dfb45f55f8421693e4803ac7bb65143bd34 # v6 + with: + version: 10.15.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + with: + node-version: ${{ matrix.node }} + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm format:check + - run: pnpm typecheck + - run: pnpm build + - run: pnpm test + - run: pnpm pack:tanstack diff --git a/.github/workflows/pkg-pr-new.yml b/.github/workflows/pkg-pr-new.yml new file mode 100644 index 0000000..5fad24d --- /dev/null +++ b/.github/workflows/pkg-pr-new.yml @@ -0,0 +1,24 @@ +name: pkg-pr-new + +on: + push: + branches: [main] + pull_request: + +permissions: {} + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + - uses: pnpm/action-setup@b0f76dfb45f55f8421693e4803ac7bb65143bd34 # v6 + with: + version: 10.15.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm build:package + - run: pnpm dlx pkg-pr-new@0.0.54 publish ./packages/tanstack diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..c0e9964 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,21 @@ +name: Validate PR title + +on: + pull_request: + branches: [main] + +permissions: + pull-requests: read + +jobs: + semantic-pr: + runs-on: ubuntu-latest + steps: + - name: Accept generated release title + if: ${{ startsWith(github.event.pull_request.title, 'Release ') }} + run: echo "Generated release PR title accepted." + + - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 + if: ${{ !startsWith(github.event.pull_request.title, 'Release ') }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-pull-request.yml b/.github/workflows/release-pull-request.yml new file mode 100644 index 0000000..996f614 --- /dev/null +++ b/.github/workflows/release-pull-request.yml @@ -0,0 +1,46 @@ +name: Release Pull Request + +on: + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +concurrency: + group: release-pull-request + cancel-in-progress: false + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Validate release request + env: + REPO_SCOPED_TOKEN: ${{ secrets.REPO_SCOPED_TOKEN }} + run: | + test "$GITHUB_REF" = "refs/heads/main" || { echo "Release PRs are main-only."; exit 1; } + test -n "$REPO_SCOPED_TOKEN" || { echo "REPO_SCOPED_TOKEN is required."; exit 1; } + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + fetch-depth: 10 + token: ${{ secrets.REPO_SCOPED_TOKEN }} + - uses: pnpm/action-setup@b0f76dfb45f55f8421693e4803ac7bb65143bd34 # v6 + with: + version: 10.15.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile --ignore-scripts + - uses: module-federation/actions@842df3a7740093a5ff44de2b9a890106046b8e32 # v2 + with: + version: latest + versionNumber: auto + type: pull request + tools: changeset + coreNpmName: "@module-federation/tanstack" + env: + GITHUB_TOKEN: ${{ secrets.REPO_SCOPED_TOKEN }} + REPOSITORY: ${{ github.repository }} + REF: ${{ github.ref }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..2043550 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,100 @@ +name: Release + +on: + release: + types: [published] + workflow_dispatch: + inputs: + version: + type: choice + required: true + default: next + options: [latest, next] + branch: + required: true + default: main + +permissions: + contents: read + id-token: write + +concurrency: + group: publish-module-federation-tanstack + cancel-in-progress: false + +jobs: + publish: + runs-on: ubuntu-latest + environment: Publish + steps: + - name: Validate manual target + if: github.event_name == 'workflow_dispatch' + env: + RELEASE_BRANCH: ${{ inputs.branch }} + run: test "$RELEASE_BRANCH" = "main" || { echo "Releases are main-only."; exit 1; } + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + fetch-depth: 0 + ref: ${{ github.event.release.tag_name || inputs.branch }} + - name: Verify source branch + run: | + git fetch --no-tags origin main:refs/remotes/origin/main + git merge-base --is-ancestor HEAD origin/main || { echo "Source is not on main."; exit 1; } + - uses: pnpm/action-setup@b0f76dfb45f55f8421693e4803ac7bb65143bd34 # v6 + with: + version: 10.15.0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 + with: + node-version: 24 + cache: pnpm + registry-url: https://registry.npmjs.org/ + - run: pnpm install --frozen-lockfile + - name: Verify release tag + if: github.event_name == 'release' + env: + RELEASE_TAG: ${{ github.event.release.tag_name }} + run: | + test "${RELEASE_TAG#v}" = "$RELEASE_TAG" || { echo "Tags must not start with v."; exit 1; } + test "$(node -p "require('./packages/tanstack/package.json').version")" = "$RELEASE_TAG" || { echo "Tag/version mismatch."; exit 1; } + - name: Generate preview version + if: github.event_name == 'workflow_dispatch' && inputs.version == 'next' + run: | + pnpm changeset version --snapshot next + SNAPSHOT_VERSION="$(node -p "require('./packages/tanstack/package.json').version")" + BASE_VERSION="${SNAPSHOT_VERSION%%-*}" + npm pkg set version="${BASE_VERSION}-next.${GITHUB_RUN_ID}" --prefix packages/tanstack + - run: pnpm format:check + - run: pnpm typecheck + - run: pnpm build + - run: pnpm test + - run: pnpm pack:tanstack + - name: Compute dist-tag + id: dist + env: + INPUT_VERSION: ${{ inputs.version }} + PRERELEASE: ${{ github.event.release.prerelease || false }} + run: | + TAG=latest + if [ "$INPUT_VERSION" = "next" ] || [ "$PRERELEASE" = "true" ]; then TAG=next; fi + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + - name: Refuse conflicting republish + id: publish-check + env: + PACKAGE: "@module-federation/tanstack" + DIST_TAG: ${{ steps.dist.outputs.tag }} + run: | + VERSION="$(node -p "require('./packages/tanstack/package.json').version")" + EXISTING="$(npm view "$PACKAGE@$VERSION" version --json 2>/dev/null || true)" + EXISTING="${EXISTING#\"}"; EXISTING="${EXISTING%\"}" + if [ "$EXISTING" = "$VERSION" ]; then + CURRENT="$(npm view "$PACKAGE" "dist-tags.$DIST_TAG" --json 2>/dev/null || true)" + CURRENT="${CURRENT#\"}"; CURRENT="${CURRENT%\"}" + test "$CURRENT" = "$VERSION" || { echo "Existing version has conflicting dist-tag."; exit 1; } + echo "publish=false" >> "$GITHUB_OUTPUT" + else + echo "publish=true" >> "$GITHUB_OUTPUT" + fi + - name: Publish to npm + if: steps.publish-check.outputs.publish == 'true' + working-directory: packages/tanstack + run: npm publish --provenance --tag "${{ steps.dist.outputs.tag }}" --access public diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cc0b184 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +node_modules/ +dist/ +.turbo/ +.mf/ +@mf-types/ +*.log +pnpm-debug.log* +coverage/ +*.tgz +*.tsbuildinfo +.DS_Store diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..90edf5a --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +auto-install-peers=false +prefer-frozen-lockfile=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..77c3854 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +dist +node_modules +pnpm-lock.yaml +.turbo +**/routeTree.gen.ts diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..bccee91 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "printWidth": 100 +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b0cebd8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Module Federation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 5ad6652..a49299a 100644 --- a/README.md +++ b/README.md @@ -1 +1,109 @@ -# tanstack \ No newline at end of file +# @module-federation/tanstack + +Module Federation support for TanStack Start on Vite and Rsbuild. Rsbuild uses +Rspack under the hood. + +## Install + +```bash +pnpm add @module-federation/tanstack @tanstack/react-start +``` + +Install the adapter for the bundler you use. They are optional peers so an +Rsbuild-only application does not install or resolve the Vite adapter, and a +Vite-only application does not install or resolve the Rsbuild adapter. + +```bash +pnpm add @module-federation/vite vite # Vite +pnpm add @module-federation/rsbuild-plugin @rsbuild/core # Rsbuild +``` + +## Configure + +The federation wrapper must come before `tanstackStart()` so Module Federation +can normalize TanStack's client and SSR entrypoints: + +```ts +import { tanstackStartModuleFederation } from "@module-federation/tanstack/vite"; +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [ + tanstackStartModuleFederation({ + name: "host", + remotes: { + remote: { + type: "module", + name: "remote", + entry: "https://example.test/remoteEntry.js", + }, + }, + }), + tanstackStart(), + react(), + ], +}); +``` + +Defaults are `remoteEntry.js`, `manifest: true`, and +`hostInitInjectLocation: "entry"`. React and `react-dom` are shared singleton +defaults; any explicit entries in `shared` take precedence. + +Do not set a global Module Federation `target`. TanStack Start builds client +and SSR environments from one Vite configuration, so the wrapper leaves target +selection to `@module-federation/vite` for each environment. + +The package supports Node 22.18+, 24.11+, and 26+, plus Vite 7/8 and TanStack +Start 1.x. Use Vite 8 when developing SSR hosts that load remotes; Vite 7 +remains supported for production builds. + +The package root remains a Vite compatibility export. New Vite projects can +import `/vite` explicitly. Rsbuild projects must import `/rsbuild`: + +```ts +import { defineConfig } from "@rsbuild/core"; +import { pluginReact } from "@rsbuild/plugin-react"; +import { tanstackStart } from "@tanstack/react-start/plugin/rsbuild"; +import { tanstackStartModuleFederation } from "@module-federation/tanstack/rsbuild"; + +export default defineConfig({ + plugins: [ + pluginReact(), + tanstackStart(), + ...tanstackStartModuleFederation({ + federation: { + name: "host", + remotes: { remote: "remote@https://example.test/mf-manifest.json" }, + }, + }), + ], +}); +``` + +The Rsbuild adapter registers browser and SSR federation plugins by default. It +emits script-compatible browser output and async-node CommonJS SSR output so +its manifest can be consumed by Vite and Rsbuild hosts. The wrapper adds eager, +singleton `react` and `react-dom` entries without changing the caller's config +object. Set `server: false` for browser-only remotes. + +Server federation mode emits `dist/server/index.cjs`; browser-only mode keeps +TanStack Start's standard `dist/server/index.js` entry. + +## Example + +The workspace includes Vite and Rsbuild pairs. `apps/vite-remote` exposes a +stateful React card to `apps/vite-host`. `apps/rsbuild-remote` exposes a second card +to the Vite host, while `apps/rsbuild-host` consumes the Vite remote. Both +cross-bundler links use manifest URLs. They render after hydration, so this is +browser interop coverage, not an unverified SSR interoperability claim. + +```bash +pnpm install +pnpm start +``` + +This starts all four applications so both same-bundler and cross-bundler paths +are available. Open the Vite host at http://localhost:3000 or the Rsbuild host +at http://localhost:3003. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..47d2940 --- /dev/null +++ b/TODO.md @@ -0,0 +1,285 @@ +# SSR completion TODO + +This file tracks the work required before `@module-federation/tanstack` can +claim SSR support across both Vite and Rsbuild/Rspack. + +Assessment date: 2026-09-18. + +## Current support boundary + +| Host | Remote | Federated SSR status | Current proof | +| ------- | ------- | -------------------- | ------------------------------------------------------------------------------- | +| Vite | Vite | Supported | The Vite host response contains remote markup before hydration. | +| Vite | Rsbuild | Not supported | The Rsbuild remote loads in the browser after hydration. | +| Rsbuild | Vite | Not supported | The Vite remote loads through the browser runtime after hydration. | +| Rsbuild | Rsbuild | Not yet supported | The adapter can build a server container, but no end-to-end SSR fixture exists. | + +The Rsbuild examples are TanStack Start SSR applications, so their local route +shells render on the server. Their Module Federation configuration passes +`server: false`; federated components are client-only. Do not describe that as +federated SSR. + +The supported claim today is: + +> Vite-to-Vite federated SSR, plus Vite/Rsbuild browser interoperability. + +## Evidence behind this boundary + +### Vite-to-Vite SSR works + +- [`packages/tanstack/src/vite.ts`](packages/tanstack/src/vite.ts) registers one + Vite federation plugin for TanStack Start's client and server environments. +- The wrapper leaves `target` unset so `@module-federation/vite` selects `web` + for the client and `node` for SSR. +- `hostInitInjectLocation: "entry"` initializes federation before TanStack + hydrates an application without an `index.html` entry. +- `module-federation/vite` enables its SSR entry loader for every production + build and for Vite 8 development. Vite 7 development does not have the + ModuleRunner support required by this path. +- Vite remotes emit a separate ESM `remoteEntry.ssr.js` and include it in + `mf-manifest.json` as `metaData.ssrRemoteEntry`. +- The Vite SSR loader resolves the manifest entry, links shared packages to the + host, and evaluates the remote through Vite ModuleRunner, `vm.SourceTextModule`, + or a temporary ESM graph. +- [`test/example-runtime.test.mjs`](test/example-runtime.test.mjs) verifies that + the Vite host's initial HTML contains `Owned by the remote app` and + `Rendered on the server`. + +Reference implementation paths: + +- `module-federation/vite/src/utils/ssrCapabilities.ts` +- `module-federation/vite/src/plugins/pluginSSRRemoteEntry.ts` +- `module-federation/vite/src/utils/ssrEntryLoader.ts` +- `module-federation/vite/src/plugins/pluginAddEntry.ts` +- `module-federation/vite/src/virtualModules/virtualRuntimeInitStatus.ts` + +### Rsbuild has the required pieces, but not the proof + +- [`packages/tanstack/src/rsbuild.ts`](packages/tanstack/src/rsbuild.ts) installs + separate `web` and `node` federation plugins for TanStack Start's `client` + and `ssr` environments. +- The browser compiler emits script/JSONP-compatible output. +- The server compiler can emit async-node CommonJS output with + `serverRemoteEntry.cjs` and `dist/server/index.cjs`. +- React and React DOM default to eager singletons for Rsbuild. TanStack's server + bundle imports React synchronously, and Core reports `RUNTIME-006` when those + shares are lazy. +- Core's `target: "node"` path changes the compiler target to `async-node`, + creates a CommonJS server container, and injects + `@module-federation/node/runtimePlugin`. +- Core only installs its startup dependency handling when + `experiments.asyncStartup` is enabled. The TanStack adapter does not default + this option today. +- Both committed Rsbuild examples pass `server: false`, so the server adapter is + covered by compiler-contract tests, not by a running host/remote pair. + +Reference implementation paths: + +- `module-federation/core/packages/rsbuild-plugin/src/cli/index.ts` +- `module-federation/core/packages/rsbuild-plugin/src/utils/ssr.ts` +- `module-federation/core/packages/node/src/runtimePlugin.ts` +- `module-federation/core/packages/enhanced/src/lib/container/ModuleFederationPlugin.ts` +- `module-federation/core/packages/webpack-bundler-runtime/src/installInitialConsumes.ts` + +### Cross-bundler SSR needs an explicit transport contract + +The two server entry formats differ: + +- Vite emits an ESM SSR entry and an ESM dependency graph. +- The current Rsbuild adapter emits a CommonJS container and async-node chunks. + +The Vite loader can `require()` a local CommonJS entry. For an HTTP CommonJS +entry, `createRequire()` cannot load the URL, and the loader falls through to +paths designed around ESM source. That is not a reliable Rsbuild remote loader. + +In the other direction, Core's Node runtime has ESM loading support, but this +repository does not prove that an Rsbuild host can load Vite's manifest-declared +ESM SSR entry, preserve the host React instance, and hydrate the result. + +## Phase 1: finish Rsbuild-to-Rsbuild SSR + +### Adapter defaults + +- [ ] Merge `experiments.asyncStartup: true` into the Rsbuild browser and server + federation options. +- [ ] Preserve an explicit `experiments.asyncStartup: false` override. +- [ ] Keep `react` and `react-dom` eager singletons by default. +- [ ] Cover React subpaths used by the compiled application, including + `react/jsx-runtime` and `react-dom/client`, without creating a second React + instance. +- [ ] Assert that Core's node target injects the Node runtime plugin exactly + once. +- [ ] Confirm that two environment-scoped Rsbuild plugins continue to install + one browser container and one server container. + +### Server-enabled fixtures + +- [ ] Add a dedicated Rsbuild remote with the server adapter enabled. +- [ ] Expose a stateful component from both `remoteEntry.js` and + `serverRemoteEntry.cjs`. +- [ ] Add a dedicated Rsbuild host with the server adapter enabled. +- [ ] Import the remote through a route loader or another real async boundary. +- [ ] Start the production remote from `dist/server/index.cjs`. +- [ ] Serve the client manifest, server entry, server chunks, JavaScript, CSS, + and federated type archive from stable URLs. +- [ ] Start the production host from `dist/server/index.cjs`. +- [ ] Ensure development and production use the same remote naming and share + scope. + +### Rsbuild SSR acceptance criteria + +- [ ] The host's initial HTML contains remote component markup. +- [ ] The initial HTML does not contain only a loading placeholder. +- [ ] Hydration completes without warnings or subtree replacement. +- [ ] A remote button remains interactive after hydration. +- [ ] Host context and hooks work inside the remote component. +- [ ] React and React DOM resolve to one physical host-owned instance. +- [ ] No `RUNTIME-006`, `RUNTIME-001`, or missing chunk error appears. +- [ ] The remote manifest declares a `commonjs-module` server entry. +- [ ] Every server entry import and async chunk is reachable in production. +- [ ] The built server output contains no build-machine absolute paths. + +## Phase 2: support a Vite host with an Rsbuild SSR remote + +Choose and document one server-entry strategy before implementing this path. + +### Option A: emit an ESM SSR entry from Rsbuild + +This aligns the remote with the existing Vite SSR loader. + +- [ ] Determine whether the Rsbuild server compiler can emit a second ESM + container without changing the CommonJS entry used by Rsbuild hosts. +- [ ] Decide how the manifest identifies both server formats. The current + manifest has one `ssrRemoteEntry` slot. +- [ ] Keep server chunks portable and reachable over HTTP. +- [ ] Verify that Vite's temporary-file and VM strategies can link the emitted + graph. + +### Option B: teach the Vite loader to evaluate HTTP CommonJS containers + +This aligns the host with the existing Rsbuild server output. + +- [ ] Add a bounded HTTP CommonJS loader rather than passing an HTTP URL to + `createRequire()`. +- [ ] Reuse Core's Node chunk-loading behavior where possible. +- [ ] Resolve relative async chunks from the remote public path. +- [ ] Preserve Node module-cache identity for React and other singletons. +- [ ] Apply the same fetch timeout and maximum-body limits used by the ESM + loader. +- [ ] Avoid unrestricted `eval` or unbounded code fetching. + +### Vite-host acceptance criteria + +- [ ] Vite 8 development renders the Rsbuild remote into initial HTML. +- [ ] Vite 8 production renders and hydrates the same remote. +- [ ] Vite 7 production works, or the package documents a narrower requirement. +- [ ] Remote CSS appears before or during hydration without a layout flash. +- [ ] Manifest revalidation replaces a changed remote without restarting the + host process. + +## Phase 3: support an Rsbuild host with a Vite SSR remote + +- [ ] Confirm that Core's Node runtime selects `metaData.ssrRemoteEntry` instead + of the browser entry in a Vite manifest. +- [ ] Confirm support for a manifest entry whose type is `module`. +- [ ] Load Vite's transitive ESM chunks without Node network-import flags. +- [ ] Map shared package imports to the Rsbuild host share scope. +- [ ] Remove any dependency on `--experimental-vm-modules` from the supported + default path. +- [ ] Verify development against Vite's `/__mf_ssr__/` and ModuleRunner + endpoints. +- [ ] Verify production against the built `remoteEntry.ssr.js` graph. + +### Rsbuild-host acceptance criteria + +- [ ] The Rsbuild host's initial HTML contains Vite remote markup. +- [ ] Hydration preserves that markup and remote state. +- [ ] The Node runtime reports no missing module, VM import, or chunk execution + error. +- [ ] A remote outage returns a controlled fallback instead of a 500 response. + +## Phase 4: shared identity and recovery hardening + +### Shared dependencies + +- [ ] Test React context created by the host and consumed by every remote. +- [ ] Test `useState`, `useEffect`, Suspense, and error boundaries across the + federation boundary. +- [ ] Test React 18 and React 19 if both remain in the support range. +- [ ] Validate version mismatch warnings for incompatible React majors. +- [ ] Ensure `import: false` fails at startup when the host cannot provide the + required package. + +### Remote lifecycle + +- [ ] Start the host while the remote is unavailable. +- [ ] Bring the remote online and prove that a later request recovers. +- [ ] Restart the remote with a new manifest and server entry. +- [ ] Revalidate without serving a stale container or stale shared module. +- [ ] Test concurrent first requests while the remote entry is loading. +- [ ] Test cyclic imports in the server remote graph. + +### Error reporting + +- [ ] Preserve the original network or execution error. +- [ ] Distinguish manifest failure, entry failure, chunk failure, and shared + dependency failure. +- [ ] Add optional observability reports for raw runtime codes that do not + contain enough context. + +## Required test matrix + +| Host | Remote | Development | Production | Initial remote HTML | Hydration and interaction | +| --------- | --------- | ------------ | ---------- | ------------------- | ------------------------- | +| Vite 8 | Vite 8 | Existing | Existing | Existing | Add browser assertion | +| Vite 7 | Vite 7 | Not required | Add | Add | Add | +| Rsbuild 2 | Rsbuild 2 | Add | Add | Add | Add | +| Vite 8 | Rsbuild 2 | Add | Add | Add | Add | +| Rsbuild 2 | Vite 8 | Add | Add | Add | Add | + +Run the production matrix on every supported Node line: + +- [ ] Node 22.18 +- [ ] Node 24 +- [ ] Node 26 + +Each matrix row must verify: + +- [ ] manifest and remote entry return 200; +- [ ] every manifest-referenced asset returns 200; +- [ ] initial HTML contains the expected remote marker; +- [ ] hydration produces no console errors or mismatch warnings; +- [ ] remote interaction updates state; +- [ ] host and remote use one React dispatcher; +- [ ] shutting down the remote produces the documented fallback; +- [ ] restarting the remote allows recovery. + +## Documentation and release work + +- [ ] Update the root README only after a matrix row satisfies every acceptance + criterion. +- [ ] Document `server: false` as browser-only federation, not SSR federation. +- [ ] Document `dist/server/index.cjs` when the CommonJS Rsbuild server adapter + is enabled. +- [ ] Document the chosen cross-bundler server-entry format. +- [ ] Keep Vite 8 as the development SSR requirement unless Vite 7 gains an + equivalent ModuleRunner path. +- [ ] Add a Changeset for any new supported SSR combination. +- [ ] Include the tested bundler and Node version matrix in the release notes. + +## Definition of done + +SSR support for a host/remote combination is complete only when: + +- [ ] the remote is rendered into the host's initial HTML; +- [ ] the same markup hydrates without replacement; +- [ ] the remote remains interactive; +- [ ] shared React identity is proven; +- [ ] development and production both pass where claimed; +- [ ] remote outage and recovery behavior are tested; +- [ ] server entries and all chunks are deployable without local paths; +- [ ] the supported versions are documented; +- [ ] CI runs the proof on every supported Node line. + +Until then, keep the package description precise: Vite-to-Vite SSR is supported; +Rsbuild and cross-bundler SSR are in progress. diff --git a/apps/README.md b/apps/README.md new file mode 100644 index 0000000..dbce32d --- /dev/null +++ b/apps/README.md @@ -0,0 +1,28 @@ +# TanStack Start examples + +Both sides of this example are TanStack Start applications. + +- `vite-remote` exposes `./StatusCard` from its Module Federation container and also + renders the component on its own `/` route. +- `vite-host` loads `tanstack_vite_remote/StatusCard` from the remote running on + port 3001. Its own application runs on port 3000. + +From the repository root: + +```bash +pnpm start +``` + +The federation wrapper must precede `tanstackStart()` in both Vite configs. +React and React DOM use the wrapper's singleton defaults. + +Each package's `start` script runs Vite's development server. In Vite's CLI, +the bare `vite` command means “start”; the literal `vite start` command would +treat `start` as a project directory. + +`rsbuild-remote` exposes the same kind of card through Rspack with a browser +manifest. Both hosts consume both remotes by manifest URL after hydration. +These examples exercise same-bundler and cross-bundler client interoperability; +they do not claim that cross-bundler SSR remote rendering is supported. + +`pnpm start` runs all four applications on ports 3000 through 3003. diff --git a/apps/rsbuild-host/package.json b/apps/rsbuild-host/package.json new file mode 100644 index 0000000..7491972 --- /dev/null +++ b/apps/rsbuild-host/package.json @@ -0,0 +1,29 @@ +{ + "name": "tanstack-start-rsbuild-host", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "build": "rsbuild build", + "dev": "rsbuild dev --port 3003 --strict-port", + "start": "rsbuild dev --host 127.0.0.1 --port 3003 --strict-port", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@module-federation/runtime": "2.9.0", + "@tanstack/react-router": "1.170.38", + "@tanstack/react-start": "1.168.56", + "react": "19.3.0", + "react-dom": "19.3.0" + }, + "devDependencies": { + "@module-federation/rsbuild-plugin": "2.9.0", + "@module-federation/tanstack": "workspace:*", + "@rsbuild/core": "2.2.8", + "@rsbuild/plugin-react": "2.1.0", + "@types/node": "24.10.1", + "@types/react": "19.3.0", + "@types/react-dom": "19.3.0", + "typescript": "6.0.3" + } +} diff --git a/apps/rsbuild-host/rsbuild.config.ts b/apps/rsbuild-host/rsbuild.config.ts new file mode 100644 index 0000000..73ad455 --- /dev/null +++ b/apps/rsbuild-host/rsbuild.config.ts @@ -0,0 +1,24 @@ +import { defineConfig } from "@rsbuild/core"; +import { pluginReact } from "@rsbuild/plugin-react"; +import { tanstackStart } from "@tanstack/react-start/plugin/rsbuild"; +import { tanstackStartModuleFederation } from "@module-federation/tanstack/rsbuild"; + +export default defineConfig({ + dev: { lazyCompilation: false }, + server: { host: "127.0.0.1", port: 3003 }, + plugins: [ + pluginReact(), + tanstackStart(), + ...tanstackStartModuleFederation({ + federation: { + dts: false, + name: "tanstack_rsbuild_host", + remotes: { + tanstack_vite_remote: "tanstack_vite_remote@http://127.0.0.1:3001/mf-manifest.json", + tanstack_rsbuild_remote: "tanstack_rsbuild_remote@http://127.0.0.1:3002/mf-manifest.json", + }, + }, + server: false, + }), + ], +}); diff --git a/apps/rsbuild-host/src/assets.d.ts b/apps/rsbuild-host/src/assets.d.ts new file mode 100644 index 0000000..596a189 --- /dev/null +++ b/apps/rsbuild-host/src/assets.d.ts @@ -0,0 +1,4 @@ +declare module "*.css?url" { + const href: string; + export default href; +} diff --git a/apps/rsbuild-host/src/components/RemoteCardSlot.tsx b/apps/rsbuild-host/src/components/RemoteCardSlot.tsx new file mode 100644 index 0000000..c99449f --- /dev/null +++ b/apps/rsbuild-host/src/components/RemoteCardSlot.tsx @@ -0,0 +1,38 @@ +import { loadRemote } from "@module-federation/runtime"; +import { useEffect, useState, type ComponentType } from "react"; + +type RemoteModule = { + default: ComponentType; +}; + +export function RemoteCardSlot({ fallback, remote }: { fallback: string; remote: string }) { + const [RemoteCard, setRemoteCard] = useState(null); + const [failed, setFailed] = useState(false); + + useEffect(() => { + let active = true; + setRemoteCard(null); + setFailed(false); + + loadRemote(remote) + .then((remoteModule) => { + if (!active) return; + if (remoteModule?.default) { + setRemoteCard(() => remoteModule.default); + } else { + setFailed(true); + } + }) + .catch(() => { + if (active) setFailed(true); + }); + + return () => { + active = false; + }; + }, [remote]); + + if (failed) return
Remote unavailable
; + if (!RemoteCard) return
{fallback}
; + return ; +} diff --git a/apps/rsbuild-host/src/routeTree.gen.ts b/apps/rsbuild-host/src/routeTree.gen.ts new file mode 100644 index 0000000..dceedff --- /dev/null +++ b/apps/rsbuild-host/src/routeTree.gen.ts @@ -0,0 +1,68 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as IndexRouteImport } from './routes/index' + +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' + fileRoutesByTo: FileRoutesByTo + to: '/' + id: '__root__' | '/' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/apps/rsbuild-host/src/router.tsx b/apps/rsbuild-host/src/router.tsx new file mode 100644 index 0000000..6982ebb --- /dev/null +++ b/apps/rsbuild-host/src/router.tsx @@ -0,0 +1,16 @@ +import { createRouter } from "@tanstack/react-router"; +import { routeTree } from "./routeTree.gen"; + +export function getRouter() { + return createRouter({ + defaultPreload: "intent", + routeTree, + scrollRestoration: true, + }); +} + +declare module "@tanstack/react-router" { + interface Register { + router: ReturnType; + } +} diff --git a/apps/rsbuild-host/src/routes/__root.tsx b/apps/rsbuild-host/src/routes/__root.tsx new file mode 100644 index 0000000..2eb5fe8 --- /dev/null +++ b/apps/rsbuild-host/src/routes/__root.tsx @@ -0,0 +1,36 @@ +import { createRootRoute, HeadContent, Outlet, Scripts } from "@tanstack/react-router"; +import type { ReactNode } from "react"; +import stylesHref from "../styles.css?url"; + +export const Route = createRootRoute({ + head: () => ({ + links: [{ href: stylesHref, rel: "stylesheet" }], + meta: [ + { charSet: "utf-8" }, + { name: "viewport", content: "width=device-width, initial-scale=1" }, + ], + }), + component: Root, +}); + +function Root() { + return ( + + + + ); +} + +function Document({ children }: { children: ReactNode }) { + return ( + + + + + + {children} + + + + ); +} diff --git a/apps/rsbuild-host/src/routes/index.tsx b/apps/rsbuild-host/src/routes/index.tsx new file mode 100644 index 0000000..41b643c --- /dev/null +++ b/apps/rsbuild-host/src/routes/index.tsx @@ -0,0 +1,24 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { RemoteCardSlot } from "../components/RemoteCardSlot"; + +export const Route = createFileRoute("/")({ component: Home }); + +function Home() { + return ( +
+

Rsbuild host

+

Rspack host, two remote formats.

+

This TanStack Start app loads both remotes through the browser runtime.

+
+ + +
+
+ ); +} diff --git a/apps/rsbuild-host/src/styles.css b/apps/rsbuild-host/src/styles.css new file mode 100644 index 0000000..e379e6a --- /dev/null +++ b/apps/rsbuild-host/src/styles.css @@ -0,0 +1,32 @@ +:root { + background: #111; + color: #edf6ef; + font-family: system-ui, sans-serif; +} +body { + margin: 0; +} +.page-shell { + width: min(760px, calc(100% - 40px)); + margin: 0 auto; + padding: 80px 0; +} +.eyebrow { + color: #91f1b0; + font-family: monospace; + text-transform: uppercase; +} +h1 { + font-size: clamp(3rem, 10vw, 6rem); + margin: 12px 0; +} +.remote-loading { + border: 1px solid #4b6653; + margin-top: 36px; + padding: 30px; +} +.remote-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 20px; +} diff --git a/apps/rsbuild-host/tsconfig.json b/apps/rsbuild-host/tsconfig.json new file mode 100644 index 0000000..0416574 --- /dev/null +++ b/apps/rsbuild-host/tsconfig.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "include": ["src/**/*.ts", "src/**/*.tsx", "rsbuild.config.ts"], + "compilerOptions": { + "allowImportingTsExtensions": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "jsx": "react-jsx", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "noUncheckedIndexedAccess": true, + "skipLibCheck": true, + "strict": true, + "target": "ES2022", + "types": ["node"], + "verbatimModuleSyntax": true + } +} diff --git a/apps/rsbuild-remote/package.json b/apps/rsbuild-remote/package.json new file mode 100644 index 0000000..82947ab --- /dev/null +++ b/apps/rsbuild-remote/package.json @@ -0,0 +1,29 @@ +{ + "name": "tanstack-start-rsbuild-remote", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "build": "rsbuild build", + "dev": "rsbuild dev --port 3002 --strict-port", + "start": "rsbuild dev --host 127.0.0.1 --port 3002 --strict-port", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@module-federation/runtime": "2.9.0", + "@tanstack/react-router": "1.170.38", + "@tanstack/react-start": "1.168.56", + "react": "19.3.0", + "react-dom": "19.3.0" + }, + "devDependencies": { + "@module-federation/rsbuild-plugin": "2.9.0", + "@module-federation/tanstack": "workspace:*", + "@rsbuild/core": "2.2.8", + "@rsbuild/plugin-react": "2.1.0", + "@types/node": "24.10.1", + "@types/react": "19.3.0", + "@types/react-dom": "19.3.0", + "typescript": "6.0.3" + } +} diff --git a/apps/rsbuild-remote/rsbuild.config.ts b/apps/rsbuild-remote/rsbuild.config.ts new file mode 100644 index 0000000..73bbc22 --- /dev/null +++ b/apps/rsbuild-remote/rsbuild.config.ts @@ -0,0 +1,24 @@ +import { defineConfig } from "@rsbuild/core"; +import { pluginReact } from "@rsbuild/plugin-react"; +import { tanstackStart } from "@tanstack/react-start/plugin/rsbuild"; +import { tanstackStartModuleFederation } from "@module-federation/tanstack/rsbuild"; + +export default defineConfig({ + dev: { lazyCompilation: false }, + server: { + cors: { origin: ["http://127.0.0.1:3000", "http://127.0.0.1:3003"] }, + host: "127.0.0.1", + port: 3002, + }, + plugins: [ + pluginReact(), + tanstackStart(), + ...tanstackStartModuleFederation({ + federation: { + exposes: { "./StatusCard": "./src/components/StatusCard.tsx" }, + name: "tanstack_rsbuild_remote", + }, + server: false, + }), + ], +}); diff --git a/apps/rsbuild-remote/src/assets.d.ts b/apps/rsbuild-remote/src/assets.d.ts new file mode 100644 index 0000000..18e9eca --- /dev/null +++ b/apps/rsbuild-remote/src/assets.d.ts @@ -0,0 +1,6 @@ +declare module "*.css"; + +declare module "*.css?url" { + const href: string; + export default href; +} diff --git a/apps/rsbuild-remote/src/components/StatusCard.css b/apps/rsbuild-remote/src/components/StatusCard.css new file mode 100644 index 0000000..f055223 --- /dev/null +++ b/apps/rsbuild-remote/src/components/StatusCard.css @@ -0,0 +1,23 @@ +.remote-card { + border: 1px solid #83502a; + background: #291c13; + color: #fff3e8; + padding: 30px; +} + +.remote-card .eyebrow { + color: #ffbc7c; + font-family: monospace; + text-transform: uppercase; +} + +.remote-card h2 { + font-size: 2rem; +} + +.remote-card button { + background: #ffbc7c; + border: 0; + cursor: pointer; + padding: 12px; +} diff --git a/apps/rsbuild-remote/src/components/StatusCard.tsx b/apps/rsbuild-remote/src/components/StatusCard.tsx new file mode 100644 index 0000000..20fedfb --- /dev/null +++ b/apps/rsbuild-remote/src/components/StatusCard.tsx @@ -0,0 +1,16 @@ +import { useState } from "react"; +import "./StatusCard.css"; + +export default function StatusCard() { + const [count, setCount] = useState(0); + return ( +
+

Federated from Rsbuild

+

One component, either host.

+

This card is built by Rspack and shared with the Vite host.

+ +
+ ); +} diff --git a/apps/rsbuild-remote/src/routeTree.gen.ts b/apps/rsbuild-remote/src/routeTree.gen.ts new file mode 100644 index 0000000..dceedff --- /dev/null +++ b/apps/rsbuild-remote/src/routeTree.gen.ts @@ -0,0 +1,68 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as IndexRouteImport } from './routes/index' + +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' + fileRoutesByTo: FileRoutesByTo + to: '/' + id: '__root__' | '/' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/apps/rsbuild-remote/src/router.tsx b/apps/rsbuild-remote/src/router.tsx new file mode 100644 index 0000000..673839e --- /dev/null +++ b/apps/rsbuild-remote/src/router.tsx @@ -0,0 +1,12 @@ +import { createRouter } from "@tanstack/react-router"; +import { routeTree } from "./routeTree.gen"; + +export function getRouter() { + return createRouter({ defaultPreload: "intent", routeTree, scrollRestoration: true }); +} + +declare module "@tanstack/react-router" { + interface Register { + router: ReturnType; + } +} diff --git a/apps/rsbuild-remote/src/routes/__root.tsx b/apps/rsbuild-remote/src/routes/__root.tsx new file mode 100644 index 0000000..2eb5fe8 --- /dev/null +++ b/apps/rsbuild-remote/src/routes/__root.tsx @@ -0,0 +1,36 @@ +import { createRootRoute, HeadContent, Outlet, Scripts } from "@tanstack/react-router"; +import type { ReactNode } from "react"; +import stylesHref from "../styles.css?url"; + +export const Route = createRootRoute({ + head: () => ({ + links: [{ href: stylesHref, rel: "stylesheet" }], + meta: [ + { charSet: "utf-8" }, + { name: "viewport", content: "width=device-width, initial-scale=1" }, + ], + }), + component: Root, +}); + +function Root() { + return ( + + + + ); +} + +function Document({ children }: { children: ReactNode }) { + return ( + + + + + + {children} + + + + ); +} diff --git a/apps/rsbuild-remote/src/routes/index.tsx b/apps/rsbuild-remote/src/routes/index.tsx new file mode 100644 index 0000000..9cfe99e --- /dev/null +++ b/apps/rsbuild-remote/src/routes/index.tsx @@ -0,0 +1,14 @@ +import { createFileRoute } from "@tanstack/react-router"; +import StatusCard from "../components/StatusCard"; + +export const Route = createFileRoute("/")({ component: Home }); + +function Home() { + return ( +
+

Rsbuild remote

+

Rspack-built remote.

+ +
+ ); +} diff --git a/apps/rsbuild-remote/src/styles.css b/apps/rsbuild-remote/src/styles.css new file mode 100644 index 0000000..fbab7d3 --- /dev/null +++ b/apps/rsbuild-remote/src/styles.css @@ -0,0 +1,22 @@ +:root { + background: #16110d; + color: #fff3e8; + font-family: system-ui, sans-serif; +} +body { + margin: 0; +} +.page-shell { + width: min(760px, calc(100% - 40px)); + margin: 0 auto; + padding: 80px 0; +} +.eyebrow { + color: #ffbc7c; + font-family: monospace; + text-transform: uppercase; +} +h1 { + font-size: clamp(3rem, 10vw, 6rem); + margin: 12px 0 48px; +} diff --git a/apps/rsbuild-remote/tsconfig.json b/apps/rsbuild-remote/tsconfig.json new file mode 100644 index 0000000..0416574 --- /dev/null +++ b/apps/rsbuild-remote/tsconfig.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "include": ["src/**/*.ts", "src/**/*.tsx", "rsbuild.config.ts"], + "compilerOptions": { + "allowImportingTsExtensions": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "jsx": "react-jsx", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "noUncheckedIndexedAccess": true, + "skipLibCheck": true, + "strict": true, + "target": "ES2022", + "types": ["node"], + "verbatimModuleSyntax": true + } +} diff --git a/apps/vite-host/package.json b/apps/vite-host/package.json new file mode 100644 index 0000000..65015fb --- /dev/null +++ b/apps/vite-host/package.json @@ -0,0 +1,28 @@ +{ + "name": "tanstack-start-vite-host", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite dev --port 3000 --strictPort", + "start": "vite --host 127.0.0.1 --port 3000 --strictPort", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@module-federation/runtime": "2.9.0", + "@tanstack/react-router": "1.170.38", + "@tanstack/react-start": "1.168.56", + "react": "19.3.0", + "react-dom": "19.3.0" + }, + "devDependencies": { + "@module-federation/tanstack": "workspace:*", + "@types/node": "24.10.1", + "@types/react": "19.3.0", + "@types/react-dom": "19.3.0", + "@vitejs/plugin-react": "6.1.1", + "typescript": "6.0.3", + "vite": "8.3.0" + } +} diff --git a/apps/vite-host/src/remote.d.ts b/apps/vite-host/src/remote.d.ts new file mode 100644 index 0000000..a2c6621 --- /dev/null +++ b/apps/vite-host/src/remote.d.ts @@ -0,0 +1,13 @@ +declare module "tanstack_vite_remote/StatusCard" { + import type { ComponentType } from "react"; + + const StatusCard: ComponentType; + export default StatusCard; +} + +declare module "tanstack_rsbuild_remote/StatusCard" { + import type { ComponentType } from "react"; + + const StatusCard: ComponentType; + export default StatusCard; +} diff --git a/apps/vite-host/src/routeTree.gen.ts b/apps/vite-host/src/routeTree.gen.ts new file mode 100644 index 0000000..dceedff --- /dev/null +++ b/apps/vite-host/src/routeTree.gen.ts @@ -0,0 +1,68 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as IndexRouteImport } from './routes/index' + +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' + fileRoutesByTo: FileRoutesByTo + to: '/' + id: '__root__' | '/' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/apps/vite-host/src/router.tsx b/apps/vite-host/src/router.tsx new file mode 100644 index 0000000..f8bf919 --- /dev/null +++ b/apps/vite-host/src/router.tsx @@ -0,0 +1,16 @@ +import { createRouter } from "@tanstack/react-router"; +import { routeTree } from "./routeTree.gen"; + +export function getRouter() { + return createRouter({ + routeTree, + defaultPreload: "intent", + scrollRestoration: true, + }); +} + +declare module "@tanstack/react-router" { + interface Register { + router: ReturnType; + } +} diff --git a/apps/vite-host/src/routes/__root.tsx b/apps/vite-host/src/routes/__root.tsx new file mode 100644 index 0000000..0571218 --- /dev/null +++ b/apps/vite-host/src/routes/__root.tsx @@ -0,0 +1,37 @@ +import { createRootRoute, HeadContent, Outlet, Scripts } from "@tanstack/react-router"; +import type { ReactNode } from "react"; +import stylesHref from "../styles.css?url"; + +export const Route = createRootRoute({ + head: () => ({ + links: [{ rel: "stylesheet", href: stylesHref }], + meta: [ + { charSet: "utf-8" }, + { name: "viewport", content: "width=device-width, initial-scale=1" }, + { title: "TanStack Start federation Vite host" }, + ], + }), + component: RootComponent, +}); + +function RootComponent() { + return ( + + + + ); +} + +function RootDocument({ children }: { children: ReactNode }) { + return ( + + + + + + {children} + + + + ); +} diff --git a/apps/vite-host/src/routes/index.tsx b/apps/vite-host/src/routes/index.tsx new file mode 100644 index 0000000..f124c7c --- /dev/null +++ b/apps/vite-host/src/routes/index.tsx @@ -0,0 +1,56 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { lazy, Suspense, useEffect, useState } from "react"; + +const RemoteStatusCard = lazy(() => import("tanstack_vite_remote/StatusCard")); +const RsbuildStatusCard = lazy(() => import("tanstack_rsbuild_remote/StatusCard")); + +export const Route = createFileRoute("/")({ + loader: () => import("tanstack_vite_remote/StatusCard").then(() => null), + component: Home, +}); + +function Home() { + return ( +
+
+

TanStack Start × Module Federation

+

Two full-stack apps. One React tree.

+

+ This page belongs to the Vite host on port 3000. The interactive card below is owned and + built by a second TanStack Start application on port 3001. +

+
+ +
+
+ 01 +

Host route

+

Rendered by TanStack Start

+

The route, document shell, and server response come from the host app.

+
+ + Loading the remote app…}> + + + + +
+
+ ); +} + +function RsbuildRemoteSlot() { + const [mounted, setMounted] = useState(false); + + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) return
Rsbuild remote loads after hydration
; + + return ( + Loading the Rsbuild remote…}> + + + ); +} diff --git a/apps/vite-host/src/styles.css b/apps/vite-host/src/styles.css new file mode 100644 index 0000000..47b0470 --- /dev/null +++ b/apps/vite-host/src/styles.css @@ -0,0 +1,139 @@ +:root { + color: #e9efe9; + background: #101411; + font-family: "Avenir Next", "Trebuchet MS", sans-serif; + font-synthesis: none; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; + background: + linear-gradient(rgba(143, 255, 184, 0.045) 1px, transparent 1px), + linear-gradient(90deg, rgba(143, 255, 184, 0.045) 1px, transparent 1px), + radial-gradient(circle at 82% 8%, rgba(62, 133, 91, 0.24), transparent 35%), #101411; + background-size: + 40px 40px, + 40px 40px, + auto; +} + +button { + font: inherit; +} + +.page-shell { + width: min(1120px, calc(100% - 40px)); + margin: 0 auto; + padding: 88px 0 72px; +} + +.hero { + max-width: 820px; + animation: rise 520ms ease-out both; +} + +.eyebrow, +.card-label, +.card-index { + font-family: "SFMono-Regular", Consolas, monospace; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.eyebrow { + color: #8fffb8; + font-size: 0.78rem; +} + +h1 { + margin: 18px 0 20px; + max-width: 760px; + font-size: clamp(3rem, 8vw, 6.8rem); + line-height: 0.94; + letter-spacing: -0.065em; +} + +.lede { + max-width: 650px; + color: #aeb9b1; + font-size: 1.08rem; + line-height: 1.7; +} + +.proof-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 18px; + margin-top: 64px; +} + +.host-card, +.remote-loading { + min-height: 300px; + border: 1px solid #344239; + padding: 30px; +} + +.host-card { + position: relative; + background: rgba(20, 28, 23, 0.82); +} + +.card-index { + position: absolute; + top: 24px; + right: 26px; + color: #617066; + font-size: 0.72rem; +} + +.card-label { + margin: 0 0 70px; + color: #8fffb8; + font-size: 0.72rem; +} + +.host-card h2 { + margin: 0 0 14px; + font-size: 1.8rem; +} + +.host-card p:last-child { + max-width: 390px; + color: #96a39a; + line-height: 1.65; +} + +.remote-loading { + display: grid; + place-items: center; + color: #8fffb8; + font-family: "SFMono-Regular", Consolas, monospace; +} + +@keyframes rise { + from { + opacity: 0; + transform: translateY(16px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@media (max-width: 760px) { + .page-shell { + padding-top: 56px; + } + + .proof-grid { + grid-template-columns: 1fr; + } +} diff --git a/apps/vite-host/tsconfig.json b/apps/vite-host/tsconfig.json new file mode 100644 index 0000000..189cae2 --- /dev/null +++ b/apps/vite-host/tsconfig.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "include": ["src/**/*.ts", "src/**/*.tsx", "vite.config.ts"], + "compilerOptions": { + "allowImportingTsExtensions": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "jsx": "react-jsx", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "noUncheckedIndexedAccess": true, + "skipLibCheck": true, + "strict": true, + "target": "ES2022", + "types": ["node", "vite/client"], + "verbatimModuleSyntax": true + } +} diff --git a/apps/vite-host/vite.config.ts b/apps/vite-host/vite.config.ts new file mode 100644 index 0000000..20fbedc --- /dev/null +++ b/apps/vite-host/vite.config.ts @@ -0,0 +1,35 @@ +import { tanstackStartModuleFederation } from "@module-federation/tanstack"; +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [ + tanstackStartModuleFederation({ + name: "tanstack_vite_host", + dts: false, + remotes: { + tanstack_vite_remote: { + type: "module", + name: "tanstack_vite_remote", + entry: "http://127.0.0.1:3001/remoteEntry.js", + }, + tanstack_rsbuild_remote: { + type: "global", + name: "tanstack_rsbuild_remote", + entry: "http://127.0.0.1:3002/mf-manifest.json", + }, + }, + }), + tanstackStart(), + react(), + ], + server: { + port: 3000, + }, + ssr: { + optimizeDeps: { + include: ["react", "react-dom"], + }, + }, +}); diff --git a/apps/vite-remote/package.json b/apps/vite-remote/package.json new file mode 100644 index 0000000..74045fe --- /dev/null +++ b/apps/vite-remote/package.json @@ -0,0 +1,28 @@ +{ + "name": "tanstack-start-vite-remote", + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite dev --port 3001 --strictPort", + "start": "vite --host 127.0.0.1 --port 3001 --strictPort", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@module-federation/runtime": "2.9.0", + "@tanstack/react-router": "1.170.38", + "@tanstack/react-start": "1.168.56", + "react": "19.3.0", + "react-dom": "19.3.0" + }, + "devDependencies": { + "@module-federation/tanstack": "workspace:*", + "@types/node": "24.10.1", + "@types/react": "19.3.0", + "@types/react-dom": "19.3.0", + "@vitejs/plugin-react": "6.1.1", + "typescript": "6.0.3", + "vite": "8.3.0" + } +} diff --git a/apps/vite-remote/src/components/StatusCard.css b/apps/vite-remote/src/components/StatusCard.css new file mode 100644 index 0000000..c8ab085 --- /dev/null +++ b/apps/vite-remote/src/components/StatusCard.css @@ -0,0 +1,77 @@ +.remote-card { + position: relative; + min-height: 300px; + border: 1px solid #7b4d26; + background: #241b14; + color: #e9efe9; + padding: 30px; + box-shadow: 14px 14px 0 #0a0d0b; + font-family: "Avenir Next", "Trebuchet MS", sans-serif; +} + +.remote-card .card-index, +.remote-card .card-label, +.remote-card .remote-status, +.remote-card button { + font-family: "SFMono-Regular", Consolas, monospace; +} + +.remote-card .card-index, +.remote-card .card-label, +.remote-card .remote-status { + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.remote-card .card-index { + position: absolute; + top: 24px; + right: 26px; + color: #8f6d4e; + font-size: 0.72rem; +} + +.remote-card .card-label { + margin: 0 0 58px; + color: #f4a95f; + font-size: 0.75rem; +} + +.remote-card .remote-status { + display: flex; + align-items: center; + gap: 8px; + color: #c4cfca; + font-size: 0.66rem; +} + +.remote-card .status-dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: #8fffb8; + box-shadow: 0 0 10px rgba(143, 255, 184, 0.72); +} + +.remote-card h2 { + margin: 22px 0 12px; + font-size: 1.8rem; +} + +.remote-card > p:not(.card-label) { + color: #aeb9b1; + line-height: 1.65; +} + +.remote-card button { + margin-top: 24px; + border: 0; + background: #f4a95f; + color: #17120e; + cursor: pointer; + padding: 12px 16px; +} + +.remote-card button:hover { + background: #ffc27f; +} diff --git a/apps/vite-remote/src/components/StatusCard.tsx b/apps/vite-remote/src/components/StatusCard.tsx new file mode 100644 index 0000000..bb5efaf --- /dev/null +++ b/apps/vite-remote/src/components/StatusCard.tsx @@ -0,0 +1,27 @@ +import { useEffect, useState } from "react"; +import "./StatusCard.css"; + +export default function StatusCard() { + const [count, setCount] = useState(0); + const [hydrated, setHydrated] = useState(false); + + useEffect(() => { + setHydrated(true); + }, []); + + return ( +
+ 02 +

Federated route module

+
+ + {hydrated ? "Hydrated on the host" : "Rendered on the server"} +
+

Owned by the remote app

+

This component crossed an application boundary while React stayed a shared singleton.

+ +
+ ); +} diff --git a/apps/vite-remote/src/routeTree.gen.ts b/apps/vite-remote/src/routeTree.gen.ts new file mode 100644 index 0000000..dceedff --- /dev/null +++ b/apps/vite-remote/src/routeTree.gen.ts @@ -0,0 +1,68 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as IndexRouteImport } from './routes/index' + +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' + fileRoutesByTo: FileRoutesByTo + to: '/' + id: '__root__' | '/' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/apps/vite-remote/src/router.tsx b/apps/vite-remote/src/router.tsx new file mode 100644 index 0000000..f8bf919 --- /dev/null +++ b/apps/vite-remote/src/router.tsx @@ -0,0 +1,16 @@ +import { createRouter } from "@tanstack/react-router"; +import { routeTree } from "./routeTree.gen"; + +export function getRouter() { + return createRouter({ + routeTree, + defaultPreload: "intent", + scrollRestoration: true, + }); +} + +declare module "@tanstack/react-router" { + interface Register { + router: ReturnType; + } +} diff --git a/apps/vite-remote/src/routes/__root.tsx b/apps/vite-remote/src/routes/__root.tsx new file mode 100644 index 0000000..987b3fc --- /dev/null +++ b/apps/vite-remote/src/routes/__root.tsx @@ -0,0 +1,37 @@ +import { createRootRoute, HeadContent, Outlet, Scripts } from "@tanstack/react-router"; +import type { ReactNode } from "react"; +import stylesHref from "../styles.css?url"; + +export const Route = createRootRoute({ + head: () => ({ + links: [{ rel: "stylesheet", href: stylesHref }], + meta: [ + { charSet: "utf-8" }, + { name: "viewport", content: "width=device-width, initial-scale=1" }, + { title: "TanStack Start federation Vite remote" }, + ], + }), + component: RootComponent, +}); + +function RootComponent() { + return ( + + + + ); +} + +function RootDocument({ children }: { children: ReactNode }) { + return ( + + + + + + {children} + + + + ); +} diff --git a/apps/vite-remote/src/routes/index.tsx b/apps/vite-remote/src/routes/index.tsx new file mode 100644 index 0000000..b1a59cd --- /dev/null +++ b/apps/vite-remote/src/routes/index.tsx @@ -0,0 +1,22 @@ +import { createFileRoute } from "@tanstack/react-router"; +import StatusCard from "../components/StatusCard"; + +export const Route = createFileRoute("/")({ + component: Home, +}); + +function Home() { + return ( +
+
+

Standalone remote

+

This is a complete TanStack Start app.

+

+ It owns a route and a server build, then exposes the same component to the host through + Module Federation. +

+
+ +
+ ); +} diff --git a/apps/vite-remote/src/styles.css b/apps/vite-remote/src/styles.css new file mode 100644 index 0000000..2db3a3d --- /dev/null +++ b/apps/vite-remote/src/styles.css @@ -0,0 +1,59 @@ +:root { + color: #e9efe9; + background: #101411; + font-family: "Avenir Next", "Trebuchet MS", sans-serif; + font-synthesis: none; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; + background: radial-gradient(circle at 75% 12%, rgba(231, 155, 76, 0.2), transparent 32%), #101411; +} + +button { + font: inherit; +} + +.remote-page { + display: grid; + grid-template-columns: minmax(0, 1.2fr) minmax(320px, 0.8fr); + align-items: center; + gap: 70px; + width: min(1050px, calc(100% - 40px)); + min-height: 100vh; + margin: 0 auto; +} + +.eyebrow { + font-family: "SFMono-Regular", Consolas, monospace; + color: #f4a95f; + font-size: 0.75rem; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +h1 { + margin: 18px 0 20px; + font-size: clamp(3rem, 6vw, 5.8rem); + line-height: 0.96; + letter-spacing: -0.06em; +} + +.lede { + color: #aeb9b1; + line-height: 1.65; +} + +@media (max-width: 780px) { + .remote-page { + grid-template-columns: 1fr; + gap: 40px; + padding: 56px 0; + } +} diff --git a/apps/vite-remote/tsconfig.json b/apps/vite-remote/tsconfig.json new file mode 100644 index 0000000..189cae2 --- /dev/null +++ b/apps/vite-remote/tsconfig.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "include": ["src/**/*.ts", "src/**/*.tsx", "vite.config.ts"], + "compilerOptions": { + "allowImportingTsExtensions": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "jsx": "react-jsx", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": true, + "noUncheckedIndexedAccess": true, + "skipLibCheck": true, + "strict": true, + "target": "ES2022", + "types": ["node", "vite/client"], + "verbatimModuleSyntax": true + } +} diff --git a/apps/vite-remote/vite.config.ts b/apps/vite-remote/vite.config.ts new file mode 100644 index 0000000..9fd7628 --- /dev/null +++ b/apps/vite-remote/vite.config.ts @@ -0,0 +1,32 @@ +import { tanstackStartModuleFederation } from "@module-federation/tanstack"; +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [ + tanstackStartModuleFederation({ + name: "tanstack_vite_remote", + dts: false, + exposes: { + "./StatusCard": "./src/components/StatusCard.tsx", + }, + }), + tanstackStart(), + react(), + ], + server: { + port: 3001, + cors: true, + origin: "http://127.0.0.1:3001", + }, + preview: { + port: 3001, + cors: true, + }, + ssr: { + optimizeDeps: { + include: ["react", "react-dom"], + }, + }, +}); diff --git a/docs/RELEASING.md b/docs/RELEASING.md new file mode 100644 index 0000000..7f536c6 --- /dev/null +++ b/docs/RELEASING.md @@ -0,0 +1,48 @@ +--- +title: Releasing +summary: Release and trusted npm publishing for @module-federation/tanstack. +read_when: + - Preparing a release + - Updating release automation +updated_at: 2026-09-16 +--- + +# Releasing + +Add a Changeset to every user-visible package change: + +```bash +pnpm changeset +``` + +Run the complete local gate before merging: + +```bash +pnpm install --frozen-lockfile +pnpm format:check +pnpm typecheck +pnpm build +pnpm test +pnpm pack:tanstack +``` + +The `Release Pull Request` workflow versions the package and updates its +changelog. After merging that PR, publish from `main` with a GitHub Release +whose tag exactly matches `packages/tanstack/package.json` (without a `v`), or +dispatch `Release` with `version=latest`. Preview dispatches use `version=next` +and publish to npm's `next` dist-tag. + +GitHub prerelease tags must match the prerelease version already committed in +`packages/tanstack/package.json`. The workflow publishes that exact version to +the `next` dist-tag. + +The publish workflow reruns the gate, checks that the source is reachable from +`main`, uses npm provenance, and refuses to republish an existing version under +a different dist-tag. Configure npm trusted publishing for repository +`module-federation/tanstack`, workflow `release.yml`, environment `Publish`. + +The release gate builds the Vite and Rsbuild example pairs. It checks the Vite +Vite host server bundle and both directions of client manifest interoperability. The +Rsbuild example remote is browser-only; package tests cover the adapter's +async-node CommonJS SSR configuration. Browser-level coverage for hydrated +cross-bundler rendering remains follow-up work. diff --git a/package.json b/package.json new file mode 100644 index 0000000..f423342 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "mf-tanstack", + "private": true, + "packageManager": "pnpm@10.15.0", + "engines": { + "node": "^22.18.0 || ^24.11.0 || >=26.0.0" + }, + "scripts": { + "build": "turbo run build", + "build:package": "pnpm --filter @module-federation/tanstack build", + "dev": "pnpm build:package && turbo run dev", + "test": "pnpm build && node --test --test-concurrency=1 test/*.test.mjs", + "typecheck": "turbo run typecheck", + "format": "prettier --write .", + "format:check": "prettier --check .", + "pack:tanstack": "pnpm --filter @module-federation/tanstack pack:dry", + "start": "pnpm build:package && turbo run start", + "changeset": "changeset", + "changeset:status": "changeset status" + }, + "devDependencies": { + "@changesets/cli": "3.0.3", + "prettier": "3.9.7", + "turbo": "2.10.13", + "typescript": "6.0.3" + } +} diff --git a/packages/tanstack/CHANGELOG.md b/packages/tanstack/CHANGELOG.md new file mode 100644 index 0000000..ff942c3 --- /dev/null +++ b/packages/tanstack/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 0.1.0 + +- Initial TanStack Start integration for Module Federation. diff --git a/packages/tanstack/LICENSE b/packages/tanstack/LICENSE new file mode 100644 index 0000000..b0cebd8 --- /dev/null +++ b/packages/tanstack/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Module Federation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/tanstack/README.md b/packages/tanstack/README.md new file mode 100644 index 0000000..fee8af5 --- /dev/null +++ b/packages/tanstack/README.md @@ -0,0 +1,79 @@ +# @module-federation/tanstack + +TanStack Start integration for Module Federation on Vite and Rsbuild. The +adapter packages are optional peers, so importing one adapter does not resolve +the other. + +The root import remains compatible with existing Vite configurations. New Vite +projects can use `/vite` explicitly: + +```ts +import { tanstackStartModuleFederation } from "@module-federation/tanstack/vite"; +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [ + tanstackStartModuleFederation({ + name: "host", + remotes: { + remote: { + type: "module", + name: "remote", + entry: "https://example.test/remoteEntry.js", + }, + }, + }), + tanstackStart(), + ], +}); +``` + +Place the federation wrapper before `tanstackStart()`, followed by the React +plugin. Defaults: `filename: "remoteEntry.js"`, `manifest: true`, +`hostInitInjectLocation: "entry"`, and singleton `react`/`react-dom` shared +entries. User-provided `shared` entries override those defaults. + +The wrapper intentionally omits the global Module Federation `target` option. +TanStack Start's client and SSR environments need `@module-federation/vite` to +select `web` and `node` independently. + +Production builds support Vite 7 and 8. Vite 8 is required for SSR remote +loading during development. + +For TanStack Start's Rsbuild integration, use the dedicated adapter. It adds +separate browser and SSR Module Federation plugins so Rspack emits a browser +script remote and async-node CommonJS SSR remote. For a browser-only remote, +pass `server: false`: + +```ts +import { defineConfig } from "@rsbuild/core"; +import { pluginReact } from "@rsbuild/plugin-react"; +import { tanstackStart } from "@tanstack/react-start/plugin/rsbuild"; +import { tanstackStartModuleFederation } from "@module-federation/tanstack/rsbuild"; + +export default defineConfig({ + plugins: [ + pluginReact(), + tanstackStart(), + ...tanstackStartModuleFederation({ + federation: { + name: "remote", + exposes: { "./StatusCard": "./src/StatusCard.tsx" }, + }, + }), + ], +}); +``` + +Install `@module-federation/vite` with Vite projects, or +`@module-federation/rsbuild-plugin` and `@rsbuild/core` with Rsbuild projects. +Those packages, plus `@tanstack/react-start`, are optional peers because each +adapter is loaded only by its matching subpath. + +The default Rsbuild server adapter changes TanStack Start's server output to +async-node CommonJS. Start that build with `node dist/server/index.cjs`. Passing +`server: false` keeps TanStack Start's standard `dist/server/index.js` output +for browser-only federation. + +Supported Node releases are 22.18+, 24.11+, and 26+. diff --git a/packages/tanstack/package.json b/packages/tanstack/package.json new file mode 100644 index 0000000..660843a --- /dev/null +++ b/packages/tanstack/package.json @@ -0,0 +1,116 @@ +{ + "name": "@module-federation/tanstack", + "version": "0.1.0", + "type": "module", + "description": "TanStack Start integration for Module Federation", + "license": "MIT", + "sideEffects": false, + "main": "./dist/index.cjs", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } + }, + "./vite": { + "import": { + "types": "./dist/vite.d.ts", + "default": "./dist/vite.js" + }, + "require": { + "types": "./dist/vite.d.cts", + "default": "./dist/vite.cjs" + } + }, + "./rsbuild": { + "import": { + "types": "./dist/rsbuild.d.ts", + "default": "./dist/rsbuild.js" + }, + "require": { + "types": "./dist/rsbuild.d.cts", + "default": "./dist/rsbuild.cjs" + } + }, + "./package.json": "./package.json" + }, + "files": [ + "dist/**/*", + "README.md", + "CHANGELOG.md", + "LICENSE" + ], + "publishConfig": { + "access": "public", + "provenance": true + }, + "repository": { + "type": "git", + "url": "git+https://github.com/module-federation/tanstack.git", + "directory": "packages/tanstack" + }, + "bugs": { + "url": "https://github.com/module-federation/tanstack/issues" + }, + "homepage": "https://github.com/module-federation/tanstack#readme", + "keywords": [ + "tanstack", + "tanstack-start", + "module-federation", + "microfrontend" + ], + "engines": { + "node": "^22.18.0 || ^24.11.0 || >=26.0.0" + }, + "scripts": { + "build": "rslib build --lib esm && rslib build --lib cjs --no-clean", + "dev": "rslib build --watch", + "pack:dry": "npm pack --dry-run", + "prepack": "pnpm run build", + "prepublishOnly": "pnpm run typecheck && pnpm run build", + "typecheck": "tsc -p tsconfig.json --noEmit" + }, + "peerDependencies": { + "@tanstack/react-start": "^1.0.0", + "@module-federation/rsbuild-plugin": "^2.9.0", + "@module-federation/vite": "^1.22.0", + "@rsbuild/core": "^2.0.0", + "vite": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@tanstack/react-start": { + "optional": true + }, + "@module-federation/rsbuild-plugin": { + "optional": true + }, + "@module-federation/vite": { + "optional": true + }, + "@rsbuild/core": { + "optional": true + }, + "vite": { + "optional": true + } + }, + "devDependencies": { + "@module-federation/rsbuild-plugin": "2.9.0", + "@module-federation/vite": "1.22.0", + "@rsbuild/core": "2.2.8", + "@rslib/core": "1.0.1", + "@tanstack/react-start": "1.168.56", + "@types/node": "24.10.1", + "react": "19.3.0", + "react-dom": "19.3.0", + "typescript": "6.0.3", + "vite": "8.3.0" + } +} diff --git a/packages/tanstack/rslib.config.ts b/packages/tanstack/rslib.config.ts new file mode 100644 index 0000000..a0cfbe7 --- /dev/null +++ b/packages/tanstack/rslib.config.ts @@ -0,0 +1,36 @@ +import { defineConfig } from "@rslib/core"; + +const entry = { + index: "./src/index.ts", + rsbuild: "./src/rsbuild.ts", + shared: "./src/shared.ts", + vite: "./src/vite.ts", +}; + +const output = { + distPath: { root: "./dist" }, + sourceMap: { js: "source-map" }, + target: "node", +} as const; + +export default defineConfig({ + lib: [ + { + bundle: false, + dts: { autoExtension: true }, + format: "esm", + output, + redirect: { dts: { extension: true } }, + source: { entry }, + syntax: "es2022", + }, + { + bundle: false, + dts: { autoExtension: true }, + format: "cjs", + output, + source: { entry }, + syntax: "es2022", + }, + ], +}); diff --git a/packages/tanstack/src/index.ts b/packages/tanstack/src/index.ts new file mode 100644 index 0000000..24f205b --- /dev/null +++ b/packages/tanstack/src/index.ts @@ -0,0 +1,4 @@ +// The root entrypoint remains Vite-compatible. Rsbuild users should import +// `@module-federation/tanstack/rsbuild` so their installation never resolves +// the Vite adapter. +export { tanstackStartModuleFederation, type TanStackStartModuleFederationOptions } from "./vite"; diff --git a/packages/tanstack/src/rsbuild.ts b/packages/tanstack/src/rsbuild.ts new file mode 100644 index 0000000..96d78f3 --- /dev/null +++ b/packages/tanstack/src/rsbuild.ts @@ -0,0 +1,177 @@ +import { + pluginModuleFederation, + type ModuleFederationOptions, +} from "@module-federation/rsbuild-plugin"; +import type { RsbuildPlugin } from "@rsbuild/core"; +import { eagerShared, resolveShared } from "./shared"; + +type ModuleFederationOverride = Partial; + +type ClientFederationOptions = ModuleFederationOverride & { + chunkLoadingGlobal?: string; + environment?: string; + forceScriptOutput?: boolean; +}; + +type ServerFederationOptions = + | false + | (ModuleFederationOverride & { + chunkFilename?: string; + entryFilename?: string; + environment?: string; + forceCommonJsOutput?: boolean; + }); + +export type TanStackStartRsbuildModuleFederationOptions = { + client?: ClientFederationOptions; + federation: ModuleFederationOptions; + server?: ServerFederationOptions; +}; + +/** Creates browser and SSR federation adapters for TanStack Start's Rsbuild integration. */ +export function tanstackStartModuleFederation({ + client = {}, + federation, + server = {}, +}: TanStackStartRsbuildModuleFederationOptions): RsbuildPlugin[] { + const { + chunkLoadingGlobal, + environment: clientEnvironment = "client", + forceScriptOutput = true, + ...clientOverrides + } = client; + const clientFederationOptions = withDefaults({ ...federation, ...clientOverrides }); + const federationName = clientFederationOptions.name; + + if (!federationName) throw new Error("federation.name is required"); + + const plugins: RsbuildPlugin[] = [ + pluginModuleFederation(clientFederationOptions, { + environment: clientEnvironment, + target: "web", + }), + clientCompatibilityPlugin({ + chunkLoadingGlobal: chunkLoadingGlobal ?? `chunk_${federationName}`, + environment: clientEnvironment, + forceScriptOutput, + name: federationName, + }), + ]; + + if (server === false) return plugins; + + const { + chunkFilename, + entryFilename, + environment: serverEnvironment = "ssr", + forceCommonJsOutput = true, + ...serverOverrides + } = server; + const serverFederationOptions = withDefaults( + { ...federation, ...serverOverrides }, + forceCommonJsOutput ? "serverRemoteEntry.cjs" : "serverRemoteEntry.js", + ); + + return [ + ...plugins, + pluginModuleFederation(serverFederationOptions, { + environment: serverEnvironment, + target: "node", + }), + serverCompatibilityPlugin({ + chunkFilename: + chunkFilename ?? (forceCommonJsOutput ? `[id].${federationName}.cjs` : undefined), + entryFilename: entryFilename ?? (forceCommonJsOutput ? "[name].cjs" : undefined), + environment: serverEnvironment, + forceCommonJsOutput, + }), + ]; +} + +export const pluginTanStackStartModuleFederation = tanstackStartModuleFederation; + +function withDefaults( + options: ModuleFederationOptions, + filename = "remoteEntry.js", +): ModuleFederationOptions { + return { + ...options, + filename: options.filename ?? filename, + manifest: options.manifest ?? true, + shared: resolveShared(options.shared, eagerShared) as ModuleFederationOptions["shared"], + }; +} + +function clientCompatibilityPlugin({ + chunkLoadingGlobal, + environment, + forceScriptOutput, + name, +}: { + chunkLoadingGlobal: string; + environment: string; + forceScriptOutput: boolean; + name: string; +}): RsbuildPlugin { + return { + name: "tanstack-start-federation-client-compat", + setup(api) { + api.onBeforeCreateCompiler(({ bundlerConfigs }) => { + for (const config of bundlerConfigs ?? []) { + if (config.name !== environment) continue; + + config.output ||= {}; + config.output.chunkFormat = "array-push"; + config.output.chunkLoading = "jsonp"; + config.output.chunkLoadingGlobal = chunkLoadingGlobal; + config.output.uniqueName ??= name; + + if (forceScriptOutput) { + config.output.module = false; + const experiments = (config.experiments ??= {}) as { outputModule?: boolean }; + experiments.outputModule = false; + } + } + }); + }, + }; +} + +function serverCompatibilityPlugin({ + chunkFilename, + entryFilename, + environment, + forceCommonJsOutput, +}: { + chunkFilename?: string; + entryFilename?: string; + environment: string; + forceCommonJsOutput: boolean; +}): RsbuildPlugin { + return { + name: "tanstack-start-federation-ssr-compat", + setup(api) { + api.onBeforeCreateCompiler(({ bundlerConfigs }) => { + for (const config of bundlerConfigs ?? []) { + if (config.name !== environment) continue; + + config.output ||= {}; + if (forceCommonJsOutput) { + config.output.filename = entryFilename ?? "[name].cjs"; + config.output.chunkFilename = + chunkFilename ?? `[id].${config.output.uniqueName ?? "server"}.cjs`; + config.target = "async-node"; + config.output.module = false; + config.output.chunkFormat = "commonjs"; + config.output.chunkLoading = "async-node"; + config.output.library = { type: "commonjs2" }; + delete config.output.chunkLoadingGlobal; + } else { + if (entryFilename) config.output.filename = entryFilename; + if (chunkFilename) config.output.chunkFilename = chunkFilename; + } + } + }); + }, + }; +} diff --git a/packages/tanstack/src/shared.ts b/packages/tanstack/src/shared.ts new file mode 100644 index 0000000..20c04ec --- /dev/null +++ b/packages/tanstack/src/shared.ts @@ -0,0 +1,31 @@ +export const defaultShared = { + react: { singleton: true }, + "react-dom": { singleton: true }, +} as const; + +export const eagerShared = { + react: { eager: true, singleton: true }, + "react-dom": { eager: true, singleton: true }, +} as const; + +export function resolveShared( + shared: unknown, + defaults: Record = defaultShared, +): Record { + if (Array.isArray(shared)) { + return { + ...defaults, + ...Object.fromEntries( + shared + .filter((packageName): packageName is string => typeof packageName === "string") + .filter((packageName) => !Object.hasOwn(defaults, packageName)) + .map((packageName) => [packageName, {}]), + ), + }; + } + + return { + ...defaults, + ...(shared && typeof shared === "object" ? shared : {}), + }; +} diff --git a/packages/tanstack/src/vite.ts b/packages/tanstack/src/vite.ts new file mode 100644 index 0000000..ca1b162 --- /dev/null +++ b/packages/tanstack/src/vite.ts @@ -0,0 +1,21 @@ +import { federation, type ModuleFederationOptions } from "@module-federation/vite"; +import { resolveShared } from "./shared"; + +export type TanStackStartModuleFederationOptions = Omit; + +/** Creates one Vite federation plugin set for TanStack Start's client and SSR environments. */ +export function tanstackStartModuleFederation(options: TanStackStartModuleFederationOptions) { + const { + shared, + target: _environmentSpecificTarget, + ...configured + } = options as ModuleFederationOptions; + + return federation({ + ...configured, + filename: configured.filename ?? "remoteEntry.js", + manifest: configured.manifest ?? true, + hostInitInjectLocation: configured.hostInitInjectLocation ?? "entry", + shared: resolveShared(shared) as ModuleFederationOptions["shared"], + }); +} diff --git a/packages/tanstack/tsconfig.json b/packages/tanstack/tsconfig.json new file mode 100644 index 0000000..058e070 --- /dev/null +++ b/packages/tanstack/tsconfig.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "allowImportingTsExtensions": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "noUncheckedIndexedAccess": true, + "rootDir": "./src", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "esModuleInterop": true, + "skipLibCheck": true, + "noEmit": true, + "verbatimModuleSyntax": true + }, + "include": ["src/**/*.ts"], + "exclude": ["dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..0262cbd --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2875 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@changesets/cli': + specifier: 3.0.3 + version: 3.0.3 + prettier: + specifier: 3.9.7 + version: 3.9.7 + turbo: + specifier: 2.10.13 + version: 2.10.13 + typescript: + specifier: 6.0.3 + version: 6.0.3 + + apps/rsbuild-host: + dependencies: + '@module-federation/runtime': + specifier: 2.9.0 + version: 2.9.0 + '@tanstack/react-router': + specifier: 1.170.38 + version: 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/react-start': + specifier: 1.168.56 + version: 1.168.56(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + react: + specifier: 19.3.0 + version: 19.3.0 + react-dom: + specifier: 19.3.0 + version: 19.3.0(react@19.3.0) + devDependencies: + '@module-federation/rsbuild-plugin': + specifier: 2.9.0 + version: 2.9.0(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(typescript@6.0.3) + '@module-federation/tanstack': + specifier: workspace:* + version: link:../../packages/tanstack + '@rsbuild/core': + specifier: 2.2.8 + version: 2.2.8(@module-federation/runtime-tools@2.9.0) + '@rsbuild/plugin-react': + specifier: 2.1.0 + version: 2.1.0(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23)) + '@types/node': + specifier: 24.10.1 + version: 24.10.1 + '@types/react': + specifier: 19.3.0 + version: 19.3.0 + '@types/react-dom': + specifier: 19.3.0 + version: 19.3.0(@types/react@19.3.0) + typescript: + specifier: 6.0.3 + version: 6.0.3 + + apps/rsbuild-remote: + dependencies: + '@module-federation/runtime': + specifier: 2.9.0 + version: 2.9.0 + '@tanstack/react-router': + specifier: 1.170.38 + version: 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/react-start': + specifier: 1.168.56 + version: 1.168.56(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + react: + specifier: 19.3.0 + version: 19.3.0 + react-dom: + specifier: 19.3.0 + version: 19.3.0(react@19.3.0) + devDependencies: + '@module-federation/rsbuild-plugin': + specifier: 2.9.0 + version: 2.9.0(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(typescript@6.0.3) + '@module-federation/tanstack': + specifier: workspace:* + version: link:../../packages/tanstack + '@rsbuild/core': + specifier: 2.2.8 + version: 2.2.8(@module-federation/runtime-tools@2.9.0) + '@rsbuild/plugin-react': + specifier: 2.1.0 + version: 2.1.0(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23)) + '@types/node': + specifier: 24.10.1 + version: 24.10.1 + '@types/react': + specifier: 19.3.0 + version: 19.3.0 + '@types/react-dom': + specifier: 19.3.0 + version: 19.3.0(@types/react@19.3.0) + typescript: + specifier: 6.0.3 + version: 6.0.3 + + apps/vite-host: + dependencies: + '@module-federation/runtime': + specifier: 2.9.0 + version: 2.9.0 + '@tanstack/react-router': + specifier: 1.170.38 + version: 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/react-start': + specifier: 1.168.56 + version: 1.168.56(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + react: + specifier: 19.3.0 + version: 19.3.0 + react-dom: + specifier: 19.3.0 + version: 19.3.0(react@19.3.0) + devDependencies: + '@module-federation/tanstack': + specifier: workspace:* + version: link:../../packages/tanstack + '@types/node': + specifier: 24.10.1 + version: 24.10.1 + '@types/react': + specifier: 19.3.0 + version: 19.3.0 + '@types/react-dom': + specifier: 19.3.0 + version: 19.3.0(@types/react@19.3.0) + '@vitejs/plugin-react': + specifier: 6.1.1 + version: 6.1.1(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + typescript: + specifier: 6.0.3 + version: 6.0.3 + vite: + specifier: 8.3.0 + version: 8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1) + + apps/vite-remote: + dependencies: + '@module-federation/runtime': + specifier: 2.9.0 + version: 2.9.0 + '@tanstack/react-router': + specifier: 1.170.38 + version: 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/react-start': + specifier: 1.168.56 + version: 1.168.56(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + react: + specifier: 19.3.0 + version: 19.3.0 + react-dom: + specifier: 19.3.0 + version: 19.3.0(react@19.3.0) + devDependencies: + '@module-federation/tanstack': + specifier: workspace:* + version: link:../../packages/tanstack + '@types/node': + specifier: 24.10.1 + version: 24.10.1 + '@types/react': + specifier: 19.3.0 + version: 19.3.0 + '@types/react-dom': + specifier: 19.3.0 + version: 19.3.0(@types/react@19.3.0) + '@vitejs/plugin-react': + specifier: 6.1.1 + version: 6.1.1(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + typescript: + specifier: 6.0.3 + version: 6.0.3 + vite: + specifier: 8.3.0 + version: 8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1) + + packages/tanstack: + devDependencies: + '@module-federation/rsbuild-plugin': + specifier: 2.9.0 + version: 2.9.0(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(typescript@6.0.3) + '@module-federation/vite': + specifier: 1.22.0 + version: 1.22.0(typescript@6.0.3)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + '@rsbuild/core': + specifier: 2.2.8 + version: 2.2.8(@module-federation/runtime-tools@2.9.0) + '@rslib/core': + specifier: 1.0.1 + version: 1.0.1(@module-federation/runtime-tools@2.9.0)(typescript@6.0.3) + '@tanstack/react-start': + specifier: 1.168.56 + version: 1.168.56(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + '@types/node': + specifier: 24.10.1 + version: 24.10.1 + react: + specifier: 19.3.0 + version: 19.3.0 + react-dom: + specifier: 19.3.0 + version: 19.3.0(react@19.3.0) + typescript: + specifier: 6.0.3 + version: 6.0.3 + vite: + specifier: 8.3.0 + version: 8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1) + +packages: + + '@ast-grep/napi-darwin-arm64@0.45.3': + resolution: {integrity: sha512-rMZJ6frSpR/xSbeV4hiA9WR5hHqk3rVYX8lOqEoUCaUnjEuFXvHdGN4WawJ8pVDq/IqM1UnTtsDSYgUdVmO5DA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@ast-grep/napi-darwin-x64@0.45.3': + resolution: {integrity: sha512-tIclZ5pM+YSgk8NNTBoTkaclbS39cU5lbVfSrFWX4LUnxXfUPG+F+ppxyFjKwJTBELsGHEO4LdX3/Lc+FDwPNA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@ast-grep/napi-linux-arm64-gnu@0.45.3': + resolution: {integrity: sha512-Bm8F7/oRHQkDXbjH+gBi9oB5huJ0q12r/AIOMIypf5PWlAVvh206Tsw468phw4Oh6kd4Ad1Z+gflNW1MutgK2g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@ast-grep/napi-linux-arm64-musl@0.45.3': + resolution: {integrity: sha512-yr5xcRerV41GeRxmZ8SKbfGeyZRhQePoNNCgTAb4OrWMelWwbMI80aY7/IlF2bo1sN40BSW4hZdGIO/akTd1RQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@ast-grep/napi-linux-x64-gnu@0.45.3': + resolution: {integrity: sha512-lluRK3hxfHMQdFuVID6seAKVx4FH0kEwF6rCh5/I40Sf3EtNR2UCBXAqd6RBA6hqlSckktvBxCfw+h4aescDmg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@ast-grep/napi-linux-x64-musl@0.45.3': + resolution: {integrity: sha512-09wJQKq86LqILqQtw0+X09SzhcPwUnMVFKyia7S3XLbLqSeQpAMF55Y+zll53JRQ1dCzFEZJyvTT/7j9vwfE3w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@ast-grep/napi-win32-arm64-msvc@0.45.3': + resolution: {integrity: sha512-/HiVgw2cdtzXLDLoG4eBBcauekNXewfagy7qiVbjkVu/KcCH8dJ9lpj8DzeWwuBNtZxuD8jpiwP0M1pz3Bc3uw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@ast-grep/napi-win32-ia32-msvc@0.45.3': + resolution: {integrity: sha512-/gwJrJQUw65uNtAFcG2rtSTr117Gqqy3bfjaljCPKsa65dTeRcMMnjKLlZ6V9/g8z+4Xf78kdhp5ECbir4yfSw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@ast-grep/napi-win32-x64-msvc@0.45.3': + resolution: {integrity: sha512-CEZAMAsaDoQoVE3MT2c/ry3/ydfyaqlTjxv8DK5PG355SrdAYPgevGC2Me6tz4dhphJin89Y20VsnrS5UrYV5Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@ast-grep/napi@0.45.3': + resolution: {integrity: sha512-bjJsUt0g3UCwbn9bjv7MnoR15rszSm79yPCXn4DWWK0NuYUUqBU/srtNMR4WnpfR7TnfBRdYFoqSNx0pGCNWJg==} + engines: {node: '>= 10'} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@changesets/apply-release-plan@8.1.1': + resolution: {integrity: sha512-nk2Hbq3M9NeHMdaIRBDvawKrQJzXB9sGyBUE5yWnIKtvdcNCPj75zBekJYr0gTT2ecJH3eFeTrc8eisPImcv0Q==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/assemble-release-plan@7.0.0': + resolution: {integrity: sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/changelog-git@1.0.0': + resolution: {integrity: sha512-3Dst2Ime2Op5nd4XmWJLPIgp11ZFqJqSkVug9izK6TDcIV4YlhPS4ECbEVR+eGI0bk0r1ItogD4j2Oli87bJrA==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/cli@3.0.3': + resolution: {integrity: sha512-L1i67bMfkK6iC/kDejuNO4pFgTRcWIObsD6Qof2woP1I4M8tj17DGlcpRaTzTv1DYSP5EMwWdEtR8X0Pj5Jg3Q==} + engines: {node: ^22.11 || ^24 || >=26, npm: '>=10.9.0', pnpm: '>=10.0.0', yarn: '>=4.5.2'} + hasBin: true + + '@changesets/config@4.0.1': + resolution: {integrity: sha512-PxpxSQLQSLDjceAgRq+4FWlU+HflDIw03mmPVipxoQx9xrXFUjmqTy3SE+n/2t+DqPdpPiLWVr5NRiLZvjyLwg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/errors@1.0.0': + resolution: {integrity: sha512-ElN/mEzn6zmETgjwf5MclCMa9ef59sAR0lfO8VSYIsiRvbC2FbLB/92EoYw10Sl0kGixxHFiJZUSv7dA+YpR8g==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/format@0.1.2': + resolution: {integrity: sha512-Caez5XtNXCFS/G5bwyav3wuXL0tMxVd2ZGbaumWbzN08tyzO21asCw7JZhNtVsAZDCvDRUzZN+Iit9SyRITSYA==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/get-dependents-graph@3.0.0': + resolution: {integrity: sha512-ji/t5wFA1zREKXRUePE6Qi+Qu2UgxCeSSGQrphezwvQZrp49B7sJ+8+wvM0tA7zPeSxYKCojDy3WWgrl+s+awg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/git@4.0.1': + resolution: {integrity: sha512-6vWpIAC4LkpmlqaIu37ViT5enyd9+uBwAiGqCGhASvkSHBgx4PrtTGXWTMFYpDmZbjgyonyVgMciPrW4MqtJsA==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/parse@1.0.0': + resolution: {integrity: sha512-P0iaMb9p9CRYZiTgAllEIF9AUMQHIy1G72tKlcIqJp61icZSsKQNiOPdxAMZG8m/DvZwt/oz5xEbTpike//dWg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/pre@3.0.0': + resolution: {integrity: sha512-Zm/6YliV/a2oeWTqHJf6KxLrQwgcK1i/BRDl2m0EKZvbnxV5fG9QRhwJJGshjsZTUTS6dkfURQ2K6aAvgNw/3Q==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/read@1.0.1': + resolution: {integrity: sha512-nzvSC6RcTiWf/dsuwZFXpLzGGsRHYCL68U3uHV7srsulU93aVWY7u2GEJiDZ+4GIIElfaW5EqtKC0UHroY+LTQ==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/should-skip-package@1.0.0': + resolution: {integrity: sha512-pwqoJmbONn1XgXmZXPEExgAaT+HdZLjALFTDgIm+PnS5KeO2nLtzA2/Q+4aMFY14kFMuXKG30MObhvDzWgzDgg==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/types@7.0.0': + resolution: {integrity: sha512-c5GoiQyt3pxiXjrWSNoP8/GRf4kG+VnKzovx1OQM8dYYALlSwgedmkPmJ+ZqGxqwg9D3Bkj85Uo4KLd5BN3A0w==} + engines: {node: ^22.11 || ^24 || >=26} + + '@changesets/write@1.0.1': + resolution: {integrity: sha512-q/ThtP9gcnEP6xlv7LrY26C2mHqdGBti/MmOVngt/M/oIGYkssmQGxPK9WzBNt2juVcH/vml2WQ+ra8LXYOTaA==} + engines: {node: ^22.11 || ^24 || >=26} + + '@clack/core@1.5.1': + resolution: {integrity: sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==} + engines: {node: '>= 20.12.0'} + + '@clack/prompts@1.8.1': + resolution: {integrity: sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==} + engines: {node: '>= 20.12.0'} + + '@emnapi/core@1.11.3': + resolution: {integrity: sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==} + + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + + '@emnapi/wasi-threads@1.2.3': + resolution: {integrity: sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@manypkg/find-root@3.1.0': + resolution: {integrity: sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==} + engines: {node: '>=20.0.0'} + + '@manypkg/get-packages@3.1.0': + resolution: {integrity: sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==} + engines: {node: '>=20.0.0'} + + '@manypkg/tools@2.1.2': + resolution: {integrity: sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==} + engines: {node: '>=20.0.0'} + + '@module-federation/bridge-react-webpack-plugin@2.9.0': + resolution: {integrity: sha512-hv3fuQkGERQ/COBKTVbFV1GWovReSg/bzJzDuxWR1F84h6NYtbYMWQqX8Egvb7HKMtn9Rflzw0GeaLr8F08vDg==} + + '@module-federation/cli@2.9.0': + resolution: {integrity: sha512-r9RdlLRy3zWxuWQRonif48xdDu1reBGx18QpkZwLQ4JfSrOARjycNIGY2Y7QaUw7dedzxyee4FtKFeX/kOVLYQ==} + engines: {node: '>=16.0.0'} + hasBin: true + + '@module-federation/dts-plugin@2.9.0': + resolution: {integrity: sha512-uMGqEG/p9odG2BVr7WRbBe2OgrvzBd3LPzcp5WP+bm3djBdUJ4PZ3ozqKp6qpy+NFnlh6vnM815Xn6AXkKy1Vw==} + peerDependencies: + typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + vue-tsc: '>=1.0.24' + peerDependenciesMeta: + vue-tsc: + optional: true + + '@module-federation/enhanced@2.9.0': + resolution: {integrity: sha512-Jd/JHoFL9fNKL4Nnzo/9hf6FD/oQo3gKpE8xt6EGuhxmrvg6wM0radPtqgHiSSWLMW7CiXr2agNjKvquKd83rw==} + hasBin: true + peerDependencies: + typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + vue-tsc: '>=1.0.24' + webpack: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + vue-tsc: + optional: true + webpack: + optional: true + + '@module-federation/error-codes@2.9.0': + resolution: {integrity: sha512-IGpd+VRlji3NyGOGGTsk7YEmPRKcDoBK4YHqIuP+OQwyb2YhHCUHO2vW9RXeQxCuKwi+c6xkTweRYG+Umy+0Zw==} + + '@module-federation/inject-external-runtime-core-plugin@2.9.0': + resolution: {integrity: sha512-k3wCSZsY21HjYQ61wmIJUQleOgcHgqx/sX4qXYw0XnJnzHuFo64rzMvgr+TuBIgZ1peAQtfILLlTnWyTRjtZHw==} + peerDependencies: + '@module-federation/runtime-tools': 2.9.0 + + '@module-federation/managers@2.9.0': + resolution: {integrity: sha512-8KhB2PF4g+M0hGaethU1H4GVWabLn5sF5TvnM6VxgAcDL4TfLY8aC/YobAXHG/SV0jpU5lsGe6s6xy6ccV0MQw==} + + '@module-federation/manifest@2.9.0': + resolution: {integrity: sha512-Zhm9luVOw9XPou50eTwsvdgr208CszoQ6cGORQDCPAMhjpGb5oYFtilJ+LKW7hQ1LktD15bEZmGhC1anvgXEiQ==} + + '@module-federation/node@2.7.50': + resolution: {integrity: sha512-mbpQRdafyeWgsmYoJfdhOQf76zS6onOGpC2X1ELpWXB1Y4BcZGloL0CLjNMNon9m3ucfpc99tOGAQqFzQVkSBQ==} + peerDependencies: + webpack: ^5.40.0 + peerDependenciesMeta: + webpack: + optional: true + + '@module-federation/rsbuild-plugin@2.9.0': + resolution: {integrity: sha512-CaWAxZg+zOMw/BfgRXQdNBQZ/e5U7DBZvE13LzG13GEclt7yxaMQgKH0si3uOsq9f/X2NC8UnuhNHLIkloqlqA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@rsbuild/core': ^1.3.21 || ^2.0.0-0 + peerDependenciesMeta: + '@rsbuild/core': + optional: true + + '@module-federation/rspack@2.9.0': + resolution: {integrity: sha512-9zSlmQYKRHKVWqhlZSMyjstp2A3VVrQV2Of8mrF5NXhngFemNx7Hw35+MTo+gsRfF5glpN8bAzLyIokRUo1Cog==} + peerDependencies: + '@rspack/core': ^0.7.0 || ^1.0.0 || ^2.0.0-0 + typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + vue-tsc: '>=1.0.24' + peerDependenciesMeta: + typescript: + optional: true + vue-tsc: + optional: true + + '@module-federation/runtime-core@2.9.0': + resolution: {integrity: sha512-dLykRYfpbEJBTdk2NlbNoVVTB196O3qujawTBguLABJkPCWETJNk60NR1yZO34eI+DTH+eGwHU3m7FddAWnbnw==} + + '@module-federation/runtime-tools@2.9.0': + resolution: {integrity: sha512-u2puqsaHiw1bVvLNk1uh98iPo6UzaBuci/aTvOFwKvbT/RF18C3vGGnm+ShHPKQEnoN6ctPtnygyOZDPR+8cgg==} + + '@module-federation/runtime@2.9.0': + resolution: {integrity: sha512-3cyAav0hWP+dNvB7qrcK9CKxQn+JvkbRvLtZz3zS2g0EyxtDFDjgbHY0Afcf7oHf8IHhKeEdzb/3Kjr1Pjmr7A==} + + '@module-federation/sdk@2.9.0': + resolution: {integrity: sha512-IMjObgBGQTXd33jTTCYcxvz8iOQGLsZ2QIPOj90rDxuBtJsN4WJwYyXqligrhY/e5p/BipUPdbIgKmWL7zFhTw==} + + '@module-federation/third-party-dts-extractor@2.9.0': + resolution: {integrity: sha512-R1Xuqnqzw6wQxWV3yU9fX1FydXAeZF2TNVFAVo1N1oLBA2j5y7aUO/Fc3VSNgd9/gxMzJgTVBdA+nMiD2SFCgg==} + + '@module-federation/vite@1.22.0': + resolution: {integrity: sha512-113KQtubRIg2ezPs8R2+q+sTKY/8IEePqaGsNAdeSHvQvtP8TUW0YvBvpNFgiPz/7VG0FhI7XYGraBekBZUzMA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@module-federation/webpack-bundler-runtime@2.9.0': + resolution: {integrity: sha512-MdU6NQibT57MaJG3KPBjC30IVBrz5eU7IcjHoVDYnMh7OmASw3stagBu7hYjdMTP31luNdtzUn85s9axvdwTlw==} + + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@oozcitak/dom@2.0.2': + resolution: {integrity: sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==} + engines: {node: '>=20.0'} + + '@oozcitak/infra@2.0.2': + resolution: {integrity: sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==} + engines: {node: '>=20.0'} + + '@oozcitak/url@3.0.0': + resolution: {integrity: sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==} + engines: {node: '>=20.0'} + + '@oozcitak/util@10.0.0': + resolution: {integrity: sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==} + engines: {node: '>=20.0'} + + '@oxc-project/types@0.150.0': + resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==} + + '@pnpm/deps.graph-sequencer@1100.0.1': + resolution: {integrity: sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==} + engines: {node: '>=22.13'} + + '@rolldown/binding-android-arm-eabi@1.2.9': + resolution: {integrity: sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.9': + resolution: {integrity: sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.9': + resolution: {integrity: sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.9': + resolution: {integrity: sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.9': + resolution: {integrity: sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.9': + resolution: {integrity: sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.9': + resolution: {integrity: sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.2.9': + resolution: {integrity: sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-ppc64-gnu@1.2.9': + resolution: {integrity: sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@rolldown/binding-linux-s390x-gnu@1.2.9': + resolution: {integrity: sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.2.9': + resolution: {integrity: sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.2.9': + resolution: {integrity: sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.2.9': + resolution: {integrity: sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.9': + resolution: {integrity: sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.9': + resolution: {integrity: sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@rsbuild/core@2.2.8': + resolution: {integrity: sha512-/htjYpZiqZFB/fELguE8nkqKDT/DqHXeXUgdf+X4UlP5BJc1EeolZ9ksDpCw9WNTlcn3zsPOj7UZ0dUUB4FAFA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + core-js: '>= 3.0.0' + peerDependenciesMeta: + core-js: + optional: true + + '@rsbuild/plugin-react@2.1.0': + resolution: {integrity: sha512-RQTIAWB/CwPjoWt9iAl+8HixeQVgZ7kEIBrWPCixfITyHdiD84h0YpUTpEUuz6kGHw1KXT9mHZ3Rwy6WG7aRDA==} + peerDependencies: + '@rsbuild/core': ^2.0.0 + peerDependenciesMeta: + '@rsbuild/core': + optional: true + + '@rslib/core@1.0.1': + resolution: {integrity: sha512-FsUgiFALdgPQDM41OEYmt18J4061Vcm/fW2RXkZcwlO56PNaAEu2vtk+i8iH0lPEpCK79OaJPYjUzygOj4pPpA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7 + typescript: ^5 || ^6 || ^7 + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + typescript: + optional: true + + '@rspack/binding-darwin-arm64@2.2.6': + resolution: {integrity: sha512-y8I8MAeKAOYuLqEKYQSmNFcW4dI1u5O+xYQcjT1L3Gby02KEt0YxlmSPvzElSQJUjsJ8PkKqU3U1suDjA6xvkg==} + cpu: [arm64] + os: [darwin] + + '@rspack/binding-darwin-x64@2.2.6': + resolution: {integrity: sha512-+zhtUAP+nZON1lSlqOFJWW2VpIXRZ4/NQskYe0xohELI4Fr6xun/Oh8dIMt+Nviamvo7lbO9XURrFio1+Fziog==} + cpu: [x64] + os: [darwin] + + '@rspack/binding-linux-arm64-gnu@2.2.6': + resolution: {integrity: sha512-7GI9xlkzVMAp9dbNvynvAreMjQQNw01b+khYYfmoQOgWXTUwrtzR7/JhJ3tijVQTXkyiYAKJ0RPvz57yD+p1vQ==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-arm64-musl@2.2.6': + resolution: {integrity: sha512-klkjpGdeROzRiGSk2amfeZBAh0vWu0GjX5xKKQfRR4IqKQxURGCQafbvl3VX5XrHEUnKFzag89Lyv+ZVx5eTCw==} + cpu: [arm64] + os: [linux] + + '@rspack/binding-linux-ppc64-gnu@2.2.6': + resolution: {integrity: sha512-TpLZr49oU31EuEqVDczpbfLZ6c976zu8HspBuUWYj1Ra4SNeOHhINKE1i42OVw2DCEMpsz8Tv3tYyLVACGC8JA==} + cpu: [ppc64] + os: [linux] + + '@rspack/binding-linux-riscv64-gnu@2.2.6': + resolution: {integrity: sha512-gU3Ki8Lt6cE4qRM62959/28GG3XQUZsnvstgDULJpkHR5QFGYXUhOoPe2fpyWYrLw6+t9lWXBuI/gUez1zc8Sg==} + cpu: [riscv64] + os: [linux] + + '@rspack/binding-linux-riscv64-musl@2.2.6': + resolution: {integrity: sha512-OQRvU8HT9rGmtgdpieO722IrGViDlR0D6yjqbt7S51b+saBPwJ8Nf3fwMtpZnBo1djV2BGXK00L34jYypeChrw==} + cpu: [riscv64] + os: [linux] + + '@rspack/binding-linux-s390x-gnu@2.2.6': + resolution: {integrity: sha512-pus4Z76+J1iRO5Gutc6pq1LkyVhXqi0Sw6o5Dxwtl3sSbVy+F5PJkxtAT+DEg07pQy5m3dCymz2CIpiFlwYLTQ==} + cpu: [s390x] + os: [linux] + + '@rspack/binding-linux-x64-gnu@2.2.6': + resolution: {integrity: sha512-M0Efi9ny2cJ6VdhHxsZbcwdRCQ0HqBAsnPxPB8Ulknj+VtRlWnTlX/DlqMaHFuCb0MOWS6DXGGEAZ8veEil0BA==} + cpu: [x64] + os: [linux] + + '@rspack/binding-linux-x64-musl@2.2.6': + resolution: {integrity: sha512-614vRMr6VAqUXQm9TY7FCamcB08XJOv51WAwIlfEpoKTYxzNiQvrTYBYPgStUH20ywLwVoN6EJ++WCmHaebGWw==} + cpu: [x64] + os: [linux] + + '@rspack/binding-wasm32-wasi@2.2.6': + resolution: {integrity: sha512-p995VmOEcHWPKw/Eek0CYUjmXWhfyWOPoKtE51Dy0/jqHcs7uRy53igDd277yHTuKCHibmwU+cjSKUe6b/aBZg==} + cpu: [wasm32] + + '@rspack/binding-win32-arm64-msvc@2.2.6': + resolution: {integrity: sha512-xC8nHMqVzQWqVW95HOcf9qTsM445ui7QNfUTDui6oTiHcBtsyfK1CEdXsc50bdi/FrJ2ueOps3M5gxbM8Ttl/A==} + cpu: [arm64] + os: [win32] + + '@rspack/binding-win32-ia32-msvc@2.2.6': + resolution: {integrity: sha512-sfqP/LSnMwAHpTA+Cfbkhvx/IvUr23Z7S+miOkaMT/j2XJYCBrPZK0ZYm8+LUNDQ2ij8/XCJT4pVpQbtc/SMPw==} + cpu: [ia32] + os: [win32] + + '@rspack/binding-win32-x64-msvc@2.2.6': + resolution: {integrity: sha512-b1ERY5QydZWJKA13AYsoMPZycG5AGoUc3sOMqLszO0gs84aMnLbK7lBiyeZCvEcHEa7gSFQlLQr9p+Xk7HPO4Q==} + cpu: [x64] + os: [win32] + + '@rspack/binding@2.2.6': + resolution: {integrity: sha512-bCB4a7KKaQpPNlkExf27AmLJabeGjpjXlhnCdvmSIHHlT3YfpYz2CSKOLLLI/BcBez8zUBNDVmvswm3ggCCRqA==} + + '@rspack/core@2.2.6': + resolution: {integrity: sha512-sqN75Fgf6v3tCmt8GAG/rdcYHOUDv/YZbQf24YCGDoMLmdNlXCosCHkBYWI8qdLs9IqSnGt7XgbcA39Zilg6UQ==} + 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/plugin-react-refresh@2.0.2': + resolution: {integrity: sha512-dGNZiCxQxgAUI9sah7gd8u+O7OJZRCmqtEJNDOd8xW5RqcieC86F7p5qcShyw6onH5pKf57evpr2VjGbaFGkZg==} + peerDependencies: + '@rspack/core': ^2.0.0 + react-refresh: '>=0.10.0 <1.0.0' + peerDependenciesMeta: + '@rspack/core': + optional: true + + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + + '@tanstack/history@1.162.4': + resolution: {integrity: sha512-utTS5L2OkeYUzXGohL1Z8sefu1GLNOJcxe8Hd6iIdc/Xo1K1nDB2JEp4iSFhvYh33xKC9V91TxrS8qfrpoKobQ==} + engines: {node: '>=20.19'} + + '@tanstack/react-router@1.170.38': + resolution: {integrity: sha512-iHM9b0aDJbuvftmkiQXawzoqsdV68p2Re2safbVLCnxafe+iLKV++2i3hI/BMAP6uR92/Mjw94JKJJfD7pbwHQ==} + engines: {node: '>=20.19'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-client@1.168.36': + resolution: {integrity: sha512-TRXZKag/Ng0TqVfcN4cbEDVJhEZYosHZ9/VAEhK3PK/YqznytK+bcoonE4jP5NCXMdH2YRTMsrWKrApEuWz0Cg==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-rsc@0.1.55': + resolution: {integrity: sha512-qfW89LMrewtMLHMCumMP3+3mf2gPezFE7FZ7CHPJis4paxt2A6Jl7eCpE6whYgc5DFiifUv9qdfbC22HIP0HAw==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rspack/core': '>=2.0.0-0' + '@vitejs/plugin-rsc': '>=0.5.30' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + react-server-dom-rspack: '>=0.0.2' + peerDependenciesMeta: + '@rspack/core': + optional: true + '@vitejs/plugin-rsc': + optional: true + react-server-dom-rspack: + optional: true + + '@tanstack/react-start-server@1.167.43': + resolution: {integrity: sha512-/Fc1mOEBAyXmqxnihIkS1DrCcJX8ffrJ+xyc8nnFmO9YuCdrnZJskd48R1ZiOhM7u3vCPzDxUycIhKo8HlKn2g==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start@1.168.56': + resolution: {integrity: sha512-b/jVJS+yt7J0IKwXfVoA2mlhIi92sWfwCYrCVked0WERzgbYAad6xrOwNTljz9OKyeajofGrmMdKJgRzK1Lt2w==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rsbuild/core': ^2.0.0 + '@vitejs/plugin-rsc': '*' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + vite: '>=7.0.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@vitejs/plugin-rsc': + optional: true + vite: + optional: true + + '@tanstack/react-store@0.11.1': + resolution: {integrity: sha512-HaIGKI3YLmjBYIvy5DFDY23oNaYZIsTZfngey07Uh5iLVJgM3bIGCnZeOFOqzjFld9JHWcaHJnasD/bKoGKwJQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-core@1.171.32': + resolution: {integrity: sha512-X86Jqk3vB2KJcUfS6oLi7B7yxbaa59QEhZRPPP1fRx3k+Jw/Fu1UYVBcRtdwK/OccQnrlQdVoKvNO3QXmyEBOw==} + engines: {node: '>=20.19'} + + '@tanstack/router-generator@1.167.38': + resolution: {integrity: sha512-lkqgFleDgfkW+QONVMKBs+ZGDUF0v9R9FkplS/GZvKDFpfnu+tZxmGlV2pryU1TDxyuSyjh56AtIla3NMrlScw==} + engines: {node: '>=20.19'} + + '@tanstack/router-plugin@1.168.40': + resolution: {integrity: sha512-ifiXjjR4uivxwRDhgYAcfyrcu+Mwx+vlG0QjjQ7N5C5sKmzpt5UtsL21TNVvZDSZl2Vae8DcL84Z02fOs8ZZMQ==} + engines: {node: '>=20.19'} + peerDependencies: + '@rsbuild/core': '>=1.0.2 || ^2.0.0' + '@tanstack/react-router': ^1.170.38 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0' + vite-plugin-solid: ^2.11.10 || ^3.0.0-0 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + + '@tanstack/router-utils@1.162.3': + resolution: {integrity: sha512-Icb0xGuG1+54IV0WMLRcc3ErTx2HeJWyPG661FkQ8UT6guoBq1FJjFRqlG/JS0xi4mFFkBhvwO7tbLa32f3yxA==} + engines: {node: '>=20.19'} + + '@tanstack/start-client-core@1.170.32': + resolution: {integrity: sha512-kawgeg3Ej/JvGeEN7txFdVN9kXOST9wl7yPojOGESiomwiubHKRdYUS6sBskW91j44H+HirQUppP4+Ha7kjb4A==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-fn-stubs@1.162.0': + resolution: {integrity: sha512-QWfUZ3Yo923tdQn38LyKMU8rcTw69zc+T4dAvgTWV4O56SqFRsGfS0lSWIMhJRwXIx/bvdi7nTUBDdZtTHtpTQ==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-plugin-core@1.171.46': + resolution: {integrity: sha512-d/cDPZNDdRl824mOamt6USO1ja5JAaIwkuT2FIBRkBdrKZgQ8GJWv5pqpkXMbKANc49V8bUBBOTjgiCdC7YsPw==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rsbuild/core': ^2.0.0 + vite: '>=7.0.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + vite: + optional: true + + '@tanstack/start-server-core@1.169.37': + resolution: {integrity: sha512-CjYMXg2XISMEJt9UVR4lbMRsnRyWPrdyuF8iuJ4gxTc1sVNRciwMVpMPVGQG2KuIma3ewlNxw8+Tpx2tnS3zRA==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-storage-context@1.167.34': + resolution: {integrity: sha512-cRPE0kjt1CnOIhepmNOvRFwCGhDAnCRohPD3HmzqJJThPkixLBRuLDvIYp4hRPtM1TJ9hiKbYl24IxAhjZO5TA==} + engines: {node: '>=22.12.0'} + + '@tanstack/store@0.11.1': + resolution: {integrity: sha512-mzTOBhypOuDJAy/D8n2MfUZ1HFkXnmSETviRyhqEC8LUE7/IZQExOTxMANj3KjTofYTkFNpBY67qaVrT41YccA==} + + '@tanstack/virtual-file-routes@1.162.0': + resolution: {integrity: sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA==} + engines: {node: '>=20.19'} + + '@turbo/darwin-64@2.10.13': + resolution: {integrity: sha512-m5DBcpkxmcKpEJHHFUfqf9GLUjSmw3cDnBJM9nghPW3kjCNb5OyNKmzG7/yT5x5AwzOO+HMwKN+UI4iVKL87YA==} + cpu: [x64] + os: [darwin] + + '@turbo/darwin-arm64@2.10.13': + resolution: {integrity: sha512-vfKPvSuoY8BJGj8M8yN+a7aq1g+CejbEacDSDlJLDkSbPN3J/U1DbYAmPG8Ype5XvImI0SgfvUhIorNnFj67zw==} + cpu: [arm64] + os: [darwin] + + '@turbo/linux-64@2.10.13': + resolution: {integrity: sha512-Zp0N5M6j9iEjJtFLTA2L+52TLuYy0VbXT3zLZzfMeqqdKSUJ7pDo2pA1NhqjoQcF2dIsktZ8xZRW3pGI6T81fw==} + cpu: [x64] + os: [android, linux] + + '@turbo/linux-arm64@2.10.13': + resolution: {integrity: sha512-M8XcfWdS2D4EP3wzd5ehUyCANucMIOsRE6gZuV0Mr2NIn+FpsUjXFCh7r9HY7GnNcjXH4tUnT76OsCDg2c0Fng==} + cpu: [arm64] + os: [android, linux] + + '@turbo/windows-64@2.10.13': + resolution: {integrity: sha512-QFd3KBMpBBCc31TbxMnxgdBivie8lxJXV8EqmanH8fg8l2e1BdCLWpZJbxk48VNvcF4LHUnwO0FJOq+veKN1cw==} + cpu: [x64] + os: [win32] + + '@turbo/windows-arm64@2.10.13': + resolution: {integrity: sha512-rM+cFO1P8iAHXdasOdMyK9nDk0UGII7VEFLIA8+jwv/+rh48pnVBbF2OSjJDBOw57pxNLIOEDA0JyKcarTBcIw==} + cpu: [arm64] + os: [win32] + + '@tybys/wasm-util@0.10.4': + resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@24.10.1': + resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + + '@types/react-dom@19.3.0': + resolution: {integrity: sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==} + peerDependencies: + '@types/react': ^19.3.0 + + '@types/react@19.3.0': + resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==} + + '@vitejs/plugin-react@6.1.1': + resolution: {integrity: sha512-yxLaQV9gkhS8ezJqCM6+ndU7mDY6gqAg75NQ+0IjwEI8IYOmQCgkRwHKVSfWXW076DsqMo0Dk+0FK1U+M5RgFw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + oxc-transform-react: ^0.145.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + oxc-transform-react: + optional: true + + adm-zip@0.6.0: + resolution: {integrity: sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==} + engines: {node: '>=14.0'} + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + ansis@4.4.0: + resolution: {integrity: sha512-9k3v7xcHwgdO/DruxGIg4HtjvlAZlcnsX/mzqUb1t3NkYnl9kK2UJ+Gq0io+vQf7iT//BD/HB/NBkUR1LWxoeA==} + engines: {node: '>=14'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + babel-dead-code-elimination@1.0.12: + resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} + + baseline-browser-mapping@2.11.24: + resolution: {integrity: sha512-hYrgxie335U08WqICoGqKRzV1HFXv6zdxwJE4ekCb80CM9a0SVVsN4QPwT67RraRo+9h8IATk6uxHJw7QSkdOg==} + engines: {node: '>=6.0.0'} + hasBin: true + + browserslist@4.29.0: + resolution: {integrity: sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + + electron-to-chromium@1.5.430: + resolution: {integrity: sha512-e1QEj72Y4zd8RlNZVmoTg+iCOSVwpk05IOiiQwdrkwCSVlZfPthevErhE+nckGd2YbsXfp1SkisznhGVIXP2NQ==} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + exsolve@1.1.1: + resolution: {integrity: sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-uri@3.1.8: + resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==} + + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fetchdts@0.1.7: + resolution: {integrity: sha512-YoZjBdafyLIop9lSxXVI33oLD5kN31q4Td+CasofLLYeLXRFeOsuOw0Uo+XNRi9PZlbfdlN2GmRtm4tCEQ9/KA==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + h3@2.0.1-rc.20: + resolution: {integrity: sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg==} + engines: {node: '>=20.11.1'} + hasBin: true + peerDependencies: + crossws: ^0.4.1 + peerDependenciesMeta: + crossws: + optional: true + + human-id@4.2.1: + resolution: {integrity: sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==} + hasBin: true + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + isbot@5.2.2: + resolution: {integrity: sha512-iQcBXcd+Rv/pkubRyGh2utW2j1oPG5hZY6TUhVPpqK4G+o3IbxpJNx04hgksjc/N7GK5pEorUxDeg31cFgEk/w==} + engines: {node: '>=18'} + + isomorphic-ws@5.0.0: + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.3.2: + resolution: {integrity: sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + launch-editor@2.14.1: + resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-releases@2.0.55: + resolution: {integrity: sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==} + engines: {node: '>=18'} + + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + + prettier@3.9.7: + resolution: {integrity: sha512-T3mF5P7HFzZVLDQuCUNtJj6WK1pcpLMweMNUVwGb01bLNkH9AkKxtZvmInw8K6bnn2O3d6/5RHl6zSHiBAD0Og==} + engines: {node: '>=14'} + hasBin: true + + react-dom@19.3.0: + resolution: {integrity: sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==} + peerDependencies: + react: ^19.3.0 + + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + + react@19.3.0: + resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} + engines: {node: '>=0.10.0'} + + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + rolldown@1.2.9: + resolution: {integrity: sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rou3@0.8.1: + resolution: {integrity: sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA==} + + rsbuild-plugin-dts@1.0.1: + resolution: {integrity: sha512-vzLhs8v1SuRd0qyIWue5caaM2A1lGImuH+cQAX71KnNpS9BZHyHRBHHXBFp9jp610XJnLlkBATyPclqUfGtCAA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@microsoft/api-extractor': ^7 + '@rsbuild/core': ^2.0.0 + typescript: ^5 || ^6 || ^7 + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + typescript: + optional: true + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + scheduler@0.28.0: + resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} + + schema-utils@4.3.0: + resolution: {integrity: sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==} + engines: {node: '>= 10.13.0'} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + seroval-plugins@1.6.7: + resolution: {integrity: sha512-4Nk35ttD3DTDJW4hgw5StsVAPeU6qnDFnULAouw6tQ7oLTV/ICXrWpsXo2EE52eSP2joUMazbVf52mFEcADqRw==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.6.7: + resolution: {integrity: sha512-AeDcLh0yO2SFm9W71essgnSzLV9DI8ZH0x0knXn2DMnUZj728mpLbxjlbB6IqKCmqh8JA3cEqRyGoNkt584JcQ==} + engines: {node: '>=10'} + + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} + engines: {node: '>= 0.4'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + srvx@0.11.22: + resolution: {integrity: sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ==} + engines: {node: '>=20.16.0'} + hasBin: true + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + turbo@2.10.13: + resolution: {integrity: sha512-69MkPbjk+G8oyC7pC2DeBk0rPdbLz51wuH5eAT0Q4BVJkzq/b1XM4kUUidsARbmDjwIELSJwd1ezITULY9kWNw==} + hasBin: true + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + undici@7.29.0: + resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} + engines: {node: '>=20.18.1'} + + unplugin@3.4.0: + resolution: {integrity: sha512-9skdIFlCsPdFV7wUfZxNsFInlW+7nJmGu2gkTu0OUhF56aXGsHab9x52/QhdJ4lC7ZDPWTxbiC4ANqVlsuaW3w==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@farmfe/core': '*' + '@rsbuild/core': '*' + '@rspack/core': '*' + bun-types-no-globals: '*' + esbuild: '*' + rolldown: '*' + rollup: '*' + unloader: '*' + vite: '*' + webpack: '*' + peerDependenciesMeta: + '@farmfe/core': + optional: true + '@rsbuild/core': + optional: true + '@rspack/core': + optional: true + bun-types-no-globals: + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + unloader: + optional: true + vite: + optional: true + webpack: + optional: true + + update-browserslist-db@1.3.3: + resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + use-sync-external-store@1.7.0: + resolution: {integrity: sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + vite@8.3.0: + resolution: {integrity: sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xmlbuilder2@4.0.3: + resolution: {integrity: sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==} + engines: {node: '>=20.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} + hasBin: true + + zod@4.6.5: + resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} + +snapshots: + + '@ast-grep/napi-darwin-arm64@0.45.3': + optional: true + + '@ast-grep/napi-darwin-x64@0.45.3': + optional: true + + '@ast-grep/napi-linux-arm64-gnu@0.45.3': + optional: true + + '@ast-grep/napi-linux-arm64-musl@0.45.3': + optional: true + + '@ast-grep/napi-linux-x64-gnu@0.45.3': + optional: true + + '@ast-grep/napi-linux-x64-musl@0.45.3': + optional: true + + '@ast-grep/napi-win32-arm64-msvc@0.45.3': + optional: true + + '@ast-grep/napi-win32-ia32-msvc@0.45.3': + optional: true + + '@ast-grep/napi-win32-x64-msvc@0.45.3': + optional: true + + '@ast-grep/napi@0.45.3': + optionalDependencies: + '@ast-grep/napi-darwin-arm64': 0.45.3 + '@ast-grep/napi-darwin-x64': 0.45.3 + '@ast-grep/napi-linux-arm64-gnu': 0.45.3 + '@ast-grep/napi-linux-arm64-musl': 0.45.3 + '@ast-grep/napi-linux-x64-gnu': 0.45.3 + '@ast-grep/napi-linux-x64-musl': 0.45.3 + '@ast-grep/napi-win32-arm64-msvc': 0.45.3 + '@ast-grep/napi-win32-ia32-msvc': 0.45.3 + '@ast-grep/napi-win32-x64-msvc': 0.45.3 + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.29.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@changesets/apply-release-plan@8.1.1': + dependencies: + '@changesets/config': 4.0.1 + '@changesets/format': 0.1.2 + '@changesets/git': 4.0.1 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + import-meta-resolve: 4.2.0 + jsonc-parser: 3.3.1 + semver: 7.8.5 + + '@changesets/assemble-release-plan@7.0.0': + dependencies: + '@changesets/errors': 1.0.0 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + semver: 7.8.5 + + '@changesets/changelog-git@1.0.0': + dependencies: + '@changesets/types': 7.0.0 + + '@changesets/cli@3.0.3': + dependencies: + '@changesets/apply-release-plan': 8.1.1 + '@changesets/assemble-release-plan': 7.0.0 + '@changesets/changelog-git': 1.0.0 + '@changesets/config': 4.0.1 + '@changesets/errors': 1.0.0 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/git': 4.0.1 + '@changesets/pre': 3.0.0 + '@changesets/read': 1.0.1 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + '@changesets/write': 1.0.1 + '@clack/prompts': 1.8.1 + '@manypkg/get-packages': 3.1.0 + '@pnpm/deps.graph-sequencer': 1100.0.1 + cac: 7.0.0 + import-meta-resolve: 4.2.0 + launch-editor: 2.14.1 + package-manager-detector: 1.8.0 + semver: 7.8.5 + tinyexec: 1.3.1 + + '@changesets/config@4.0.1': + dependencies: + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + picomatch: 4.0.7 + + '@changesets/errors@1.0.0': {} + + '@changesets/format@0.1.2': + dependencies: + package-manager-detector: 1.8.0 + tinyexec: 1.3.1 + + '@changesets/get-dependents-graph@3.0.0': + dependencies: + '@changesets/types': 7.0.0 + semver: 7.8.5 + + '@changesets/git@4.0.1': + dependencies: + '@changesets/errors': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + picomatch: 4.0.7 + tinyexec: 1.3.1 + + '@changesets/parse@1.0.0': + dependencies: + '@changesets/types': 7.0.0 + yaml: 2.9.1 + + '@changesets/pre@3.0.0': + dependencies: + '@changesets/errors': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + + '@changesets/read@1.0.1': + dependencies: + '@changesets/git': 4.0.1 + '@changesets/parse': 1.0.0 + '@changesets/types': 7.0.0 + + '@changesets/should-skip-package@1.0.0': + dependencies: + '@changesets/types': 7.0.0 + + '@changesets/types@7.0.0': {} + + '@changesets/write@1.0.1': + dependencies: + '@changesets/format': 0.1.2 + '@changesets/types': 7.0.0 + human-id: 4.2.1 + + '@clack/core@1.5.1': + dependencies: + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@clack/prompts@1.8.1': + dependencies: + '@clack/core': 1.5.1 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@emnapi/core@1.11.3': + dependencies: + '@emnapi/wasi-threads': 1.2.3 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.6.0 + + '@manypkg/find-root@3.1.0': + dependencies: + '@manypkg/tools': 2.1.2 + + '@manypkg/get-packages@3.1.0': + dependencies: + '@manypkg/find-root': 3.1.0 + '@manypkg/tools': 2.1.2 + + '@manypkg/tools@2.1.2': + dependencies: + jju: 1.4.0 + tinyglobby: 0.2.17 + yaml: 2.9.1 + + '@module-federation/bridge-react-webpack-plugin@2.9.0': + dependencies: + '@module-federation/sdk': 2.9.0 + + '@module-federation/cli@2.9.0(typescript@6.0.3)': + dependencies: + '@module-federation/dts-plugin': 2.9.0(typescript@6.0.3) + '@module-federation/sdk': 2.9.0 + commander: 11.1.0 + jiti: 2.4.2 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - vue-tsc + + '@module-federation/dts-plugin@2.9.0(typescript@6.0.3)': + dependencies: + '@module-federation/error-codes': 2.9.0 + '@module-federation/managers': 2.9.0 + '@module-federation/sdk': 2.9.0 + '@module-federation/third-party-dts-extractor': 2.9.0 + adm-zip: 0.6.0 + isomorphic-ws: 5.0.0(ws@8.21.0) + typescript: 6.0.3 + undici: 7.29.0 + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@module-federation/enhanced@2.9.0(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(typescript@6.0.3)': + dependencies: + '@module-federation/bridge-react-webpack-plugin': 2.9.0 + '@module-federation/cli': 2.9.0(typescript@6.0.3) + '@module-federation/dts-plugin': 2.9.0(typescript@6.0.3) + '@module-federation/error-codes': 2.9.0 + '@module-federation/inject-external-runtime-core-plugin': 2.9.0(@module-federation/runtime-tools@2.9.0) + '@module-federation/managers': 2.9.0 + '@module-federation/manifest': 2.9.0(typescript@6.0.3) + '@module-federation/rspack': 2.9.0(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(typescript@6.0.3) + '@module-federation/runtime-tools': 2.9.0 + '@module-federation/sdk': 2.9.0 + '@module-federation/webpack-bundler-runtime': 2.9.0 + schema-utils: 4.3.0 + tapable: 2.3.0 + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - '@rspack/core' + - bufferutil + - utf-8-validate + + '@module-federation/error-codes@2.9.0': {} + + '@module-federation/inject-external-runtime-core-plugin@2.9.0(@module-federation/runtime-tools@2.9.0)': + dependencies: + '@module-federation/runtime-tools': 2.9.0 + + '@module-federation/managers@2.9.0': + dependencies: + '@module-federation/sdk': 2.9.0 + + '@module-federation/manifest@2.9.0(typescript@6.0.3)': + dependencies: + '@module-federation/dts-plugin': 2.9.0(typescript@6.0.3) + '@module-federation/managers': 2.9.0 + '@module-federation/sdk': 2.9.0 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - vue-tsc + + '@module-federation/node@2.7.50(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(typescript@6.0.3)': + dependencies: + '@module-federation/enhanced': 2.9.0(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(typescript@6.0.3) + '@module-federation/runtime': 2.9.0 + '@module-federation/sdk': 2.9.0 + encoding: 0.1.13 + node-fetch: 2.7.0(encoding@0.1.13) + tapable: 2.3.0 + transitivePeerDependencies: + - '@rspack/core' + - bufferutil + - typescript + - utf-8-validate + - vue-tsc + + '@module-federation/rsbuild-plugin@2.9.0(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(typescript@6.0.3)': + dependencies: + '@module-federation/enhanced': 2.9.0(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(typescript@6.0.3) + '@module-federation/node': 2.7.50(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(typescript@6.0.3) + '@module-federation/sdk': 2.9.0 + optionalDependencies: + '@rsbuild/core': 2.2.8(@module-federation/runtime-tools@2.9.0) + transitivePeerDependencies: + - '@rspack/core' + - bufferutil + - typescript + - utf-8-validate + - vue-tsc + - webpack + + '@module-federation/rspack@2.9.0(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(typescript@6.0.3)': + dependencies: + '@module-federation/bridge-react-webpack-plugin': 2.9.0 + '@module-federation/dts-plugin': 2.9.0(typescript@6.0.3) + '@module-federation/inject-external-runtime-core-plugin': 2.9.0(@module-federation/runtime-tools@2.9.0) + '@module-federation/managers': 2.9.0 + '@module-federation/manifest': 2.9.0(typescript@6.0.3) + '@module-federation/runtime-tools': 2.9.0 + '@module-federation/sdk': 2.9.0 + '@rspack/core': 2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23) + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@module-federation/runtime-core@2.9.0': + dependencies: + '@module-federation/error-codes': 2.9.0 + '@module-federation/sdk': 2.9.0 + + '@module-federation/runtime-tools@2.9.0': + dependencies: + '@module-federation/runtime': 2.9.0 + '@module-federation/webpack-bundler-runtime': 2.9.0 + + '@module-federation/runtime@2.9.0': + dependencies: + '@module-federation/error-codes': 2.9.0 + '@module-federation/runtime-core': 2.9.0 + '@module-federation/sdk': 2.9.0 + + '@module-federation/sdk@2.9.0': {} + + '@module-federation/third-party-dts-extractor@2.9.0': {} + + '@module-federation/vite@1.22.0(typescript@6.0.3)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1))': + dependencies: + '@module-federation/dts-plugin': 2.9.0(typescript@6.0.3) + '@module-federation/runtime': 2.9.0 + '@module-federation/sdk': 2.9.0 + vite: 8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1) + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - vue-tsc + + '@module-federation/webpack-bundler-runtime@2.9.0': + dependencies: + '@module-federation/error-codes': 2.9.0 + '@module-federation/runtime': 2.9.0 + '@module-federation/sdk': 2.9.0 + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': + dependencies: + '@emnapi/core': 1.11.3 + '@emnapi/runtime': 1.11.3 + '@tybys/wasm-util': 0.10.4 + optional: true + + '@oozcitak/dom@2.0.2': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/url': 3.0.0 + '@oozcitak/util': 10.0.0 + + '@oozcitak/infra@2.0.2': + dependencies: + '@oozcitak/util': 10.0.0 + + '@oozcitak/url@3.0.0': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + + '@oozcitak/util@10.0.0': {} + + '@oxc-project/types@0.150.0': {} + + '@pnpm/deps.graph-sequencer@1100.0.1': {} + + '@rolldown/binding-android-arm-eabi@1.2.9': + optional: true + + '@rolldown/binding-android-arm64@1.2.9': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.9': + optional: true + + '@rolldown/binding-darwin-x64@1.2.9': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.9': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.9': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.9': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.9': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.9': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.9': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.9': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.9': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.9': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.9': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.9': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0)': + dependencies: + '@rspack/core': 2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23) + '@swc/helpers': 0.5.23 + transitivePeerDependencies: + - '@module-federation/runtime-tools' + + '@rsbuild/plugin-react@2.1.0(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))': + dependencies: + '@rspack/plugin-react-refresh': 2.0.2(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(react-refresh@0.18.0) + react-refresh: 0.18.0 + optionalDependencies: + '@rsbuild/core': 2.2.8(@module-federation/runtime-tools@2.9.0) + transitivePeerDependencies: + - '@rspack/core' + + '@rslib/core@1.0.1(@module-federation/runtime-tools@2.9.0)(typescript@6.0.3)': + dependencies: + '@rsbuild/core': 2.2.8(@module-federation/runtime-tools@2.9.0) + rsbuild-plugin-dts: 1.0.1(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(typescript@6.0.3) + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - '@module-federation/runtime-tools' + - core-js + + '@rspack/binding-darwin-arm64@2.2.6': + optional: true + + '@rspack/binding-darwin-x64@2.2.6': + optional: true + + '@rspack/binding-linux-arm64-gnu@2.2.6': + optional: true + + '@rspack/binding-linux-arm64-musl@2.2.6': + optional: true + + '@rspack/binding-linux-ppc64-gnu@2.2.6': + optional: true + + '@rspack/binding-linux-riscv64-gnu@2.2.6': + optional: true + + '@rspack/binding-linux-riscv64-musl@2.2.6': + optional: true + + '@rspack/binding-linux-s390x-gnu@2.2.6': + optional: true + + '@rspack/binding-linux-x64-gnu@2.2.6': + optional: true + + '@rspack/binding-linux-x64-musl@2.2.6': + optional: true + + '@rspack/binding-wasm32-wasi@2.2.6': + dependencies: + '@emnapi/core': 1.11.3 + '@emnapi/runtime': 1.11.3 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + optional: true + + '@rspack/binding-win32-arm64-msvc@2.2.6': + optional: true + + '@rspack/binding-win32-ia32-msvc@2.2.6': + optional: true + + '@rspack/binding-win32-x64-msvc@2.2.6': + optional: true + + '@rspack/binding@2.2.6': + optionalDependencies: + '@rspack/binding-darwin-arm64': 2.2.6 + '@rspack/binding-darwin-x64': 2.2.6 + '@rspack/binding-linux-arm64-gnu': 2.2.6 + '@rspack/binding-linux-arm64-musl': 2.2.6 + '@rspack/binding-linux-ppc64-gnu': 2.2.6 + '@rspack/binding-linux-riscv64-gnu': 2.2.6 + '@rspack/binding-linux-riscv64-musl': 2.2.6 + '@rspack/binding-linux-s390x-gnu': 2.2.6 + '@rspack/binding-linux-x64-gnu': 2.2.6 + '@rspack/binding-linux-x64-musl': 2.2.6 + '@rspack/binding-wasm32-wasi': 2.2.6 + '@rspack/binding-win32-arm64-msvc': 2.2.6 + '@rspack/binding-win32-ia32-msvc': 2.2.6 + '@rspack/binding-win32-x64-msvc': 2.2.6 + + '@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23)': + dependencies: + '@rspack/binding': 2.2.6 + optionalDependencies: + '@module-federation/runtime-tools': 2.9.0 + '@swc/helpers': 0.5.23 + + '@rspack/plugin-react-refresh@2.0.2(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(react-refresh@0.18.0)': + dependencies: + react-refresh: 0.18.0 + optionalDependencies: + '@rspack/core': 2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23) + + '@swc/helpers@0.5.23': + dependencies: + tslib: 2.8.1 + + '@tanstack/history@1.162.4': {} + + '@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@tanstack/history': 1.162.4 + '@tanstack/react-store': 0.11.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/router-core': 1.171.32 + isbot: 5.2.2 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + '@tanstack/react-start-client@1.168.36(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@tanstack/react-router': 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/router-core': 1.171.32 + '@tanstack/start-client-core': 1.170.32 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + + '@tanstack/react-start-rsc@0.1.55(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1))': + dependencies: + '@tanstack/react-router': 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/router-core': 1.171.32 + '@tanstack/router-utils': 1.162.3 + '@tanstack/start-client-core': 1.170.32 + '@tanstack/start-fn-stubs': 1.162.0 + '@tanstack/start-plugin-core': 1.171.46(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + '@tanstack/start-storage-context': 1.167.34 + pathe: 2.0.3 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@rspack/core': 2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23) + transitivePeerDependencies: + - '@farmfe/core' + - '@rsbuild/core' + - bun-types-no-globals + - crossws + - esbuild + - rolldown + - rollup + - supports-color + - unloader + - vite + - vite-plugin-solid + - webpack + + '@tanstack/react-start-server@1.167.43(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@tanstack/react-router': 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/router-core': 1.171.32 + '@tanstack/start-server-core': 1.169.37 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + transitivePeerDependencies: + - crossws + + '@tanstack/react-start@1.168.56(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1))': + dependencies: + '@tanstack/react-router': 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/react-start-client': 1.168.36(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/react-start-rsc': 0.1.55(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + '@tanstack/react-start-server': 1.167.43(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@tanstack/router-utils': 1.162.3 + '@tanstack/start-client-core': 1.170.32 + '@tanstack/start-plugin-core': 1.171.46(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + '@tanstack/start-server-core': 1.169.37 + pathe: 2.0.3 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + optionalDependencies: + '@rsbuild/core': 2.2.8(@module-federation/runtime-tools@2.9.0) + vite: 8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - crossws + - esbuild + - react-server-dom-rspack + - rolldown + - rollup + - supports-color + - unloader + - vite-plugin-solid + - webpack + + '@tanstack/react-store@0.11.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': + dependencies: + '@tanstack/store': 0.11.1 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + use-sync-external-store: 1.7.0(react@19.3.0) + + '@tanstack/router-core@1.171.32': + dependencies: + '@tanstack/history': 1.162.4 + cookie-es: 3.1.1 + seroval: 1.6.7 + seroval-plugins: 1.6.7(seroval@1.6.7) + + '@tanstack/router-generator@1.167.38': + dependencies: + '@babel/types': 7.29.8 + '@tanstack/router-core': 1.171.32 + '@tanstack/router-utils': 1.162.3 + '@tanstack/virtual-file-routes': 1.162.0 + jiti: 2.7.0 + magic-string: 0.30.21 + prettier: 3.9.7 + zod: 4.6.5 + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.168.40(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1))': + dependencies: + '@babel/core': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + '@tanstack/router-core': 1.171.32 + '@tanstack/router-generator': 1.167.38 + '@tanstack/router-utils': 1.162.3 + chokidar: 5.0.0 + unplugin: 3.4.0(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + zod: 4.6.5 + optionalDependencies: + '@rsbuild/core': 2.2.8(@module-federation/runtime-tools@2.9.0) + '@tanstack/react-router': 1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + vite: 8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - supports-color + - unloader + + '@tanstack/router-utils@1.162.3': + dependencies: + '@babel/generator': 7.29.8 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + ansis: 4.4.0 + babel-dead-code-elimination: 1.0.12 + diff: 8.0.4 + pathe: 2.0.3 + tinyglobby: 0.2.17 + transitivePeerDependencies: + - supports-color + + '@tanstack/start-client-core@1.170.32': + dependencies: + '@tanstack/router-core': 1.171.32 + '@tanstack/start-fn-stubs': 1.162.0 + '@tanstack/start-storage-context': 1.167.34 + seroval: 1.6.7 + + '@tanstack/start-fn-stubs@1.162.0': {} + + '@tanstack/start-plugin-core@1.171.46(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1))': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/core': 7.29.7 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + '@tanstack/router-core': 1.171.32 + '@tanstack/router-generator': 1.167.38 + '@tanstack/router-plugin': 1.168.40(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(@tanstack/react-router@1.170.38(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + '@tanstack/router-utils': 1.162.3 + '@tanstack/start-server-core': 1.169.37 + exsolve: 1.1.1 + lightningcss: 1.33.0 + pathe: 2.0.3 + picomatch: 4.0.7 + seroval: 1.6.7 + source-map: 0.7.6 + srvx: 0.11.22 + tinyglobby: 0.2.17 + ufo: 1.6.4 + vitefu: 1.1.3(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)) + xmlbuilder2: 4.0.3 + zod: 4.6.5 + optionalDependencies: + '@rsbuild/core': 2.2.8(@module-federation/runtime-tools@2.9.0) + vite: 8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - '@tanstack/react-router' + - bun-types-no-globals + - crossws + - esbuild + - rolldown + - rollup + - supports-color + - unloader + - vite-plugin-solid + - webpack + + '@tanstack/start-server-core@1.169.37': + dependencies: + '@tanstack/history': 1.162.4 + '@tanstack/router-core': 1.171.32 + '@tanstack/start-client-core': 1.170.32 + '@tanstack/start-storage-context': 1.167.34 + fetchdts: 0.1.7 + h3-v2: h3@2.0.1-rc.20 + seroval: 1.6.7 + transitivePeerDependencies: + - crossws + + '@tanstack/start-storage-context@1.167.34': + dependencies: + '@tanstack/router-core': 1.171.32 + + '@tanstack/store@0.11.1': {} + + '@tanstack/virtual-file-routes@1.162.0': {} + + '@turbo/darwin-64@2.10.13': + optional: true + + '@turbo/darwin-arm64@2.10.13': + optional: true + + '@turbo/linux-64@2.10.13': + optional: true + + '@turbo/linux-arm64@2.10.13': + optional: true + + '@turbo/windows-64@2.10.13': + optional: true + + '@turbo/windows-arm64@2.10.13': + optional: true + + '@tybys/wasm-util@0.10.4': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/json-schema@7.0.15': {} + + '@types/node@24.10.1': + dependencies: + undici-types: 7.16.0 + + '@types/react-dom@19.3.0(@types/react@19.3.0)': + dependencies: + '@types/react': 19.3.0 + + '@types/react@19.3.0': + dependencies: + csstype: 3.2.3 + + '@vitejs/plugin-react@6.1.1(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1) + + adm-zip@0.6.0: {} + + ajv-formats@2.1.1: + dependencies: + ajv: 8.20.0 + + ajv-keywords@5.1.0(ajv@8.20.0): + dependencies: + ajv: 8.20.0 + fast-deep-equal: 3.1.3 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.8 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansis@4.4.0: {} + + argparse@2.0.1: {} + + babel-dead-code-elimination@1.0.12: + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + baseline-browser-mapping@2.11.24: {} + + browserslist@4.29.0: + dependencies: + baseline-browser-mapping: 2.11.24 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.430 + node-releases: 2.0.55 + update-browserslist-db: 1.3.3(browserslist@4.29.0) + + cac@7.0.0: {} + + caniuse-lite@1.0.30001810: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.1.1 + + commander@11.1.0: {} + + convert-source-map@2.0.0: {} + + cookie-es@3.1.1: {} + + csstype@3.2.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + detect-libc@2.1.2: {} + + diff@8.0.4: {} + + electron-to-chromium@1.5.430: {} + + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + + escalade@3.2.0: {} + + exsolve@1.1.1: {} + + fast-deep-equal@3.1.3: {} + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-uri@3.1.8: {} + + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + fetchdts@0.1.7: {} + + fsevents@2.3.3: + optional: true + + gensync@1.0.0-beta.2: {} + + h3@2.0.1-rc.20: + dependencies: + rou3: 0.8.1 + srvx: 0.11.22 + + human-id@4.2.1: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + import-meta-resolve@4.2.0: {} + + isbot@5.2.2: {} + + isomorphic-ws@5.0.0(ws@8.21.0): + dependencies: + ws: 8.21.0 + + jiti@2.4.2: {} + + jiti@2.7.0: {} + + jju@1.4.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.3.2: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-schema-traverse@1.0.0: {} + + json5@2.2.3: {} + + jsonc-parser@3.3.1: {} + + launch-editor@2.14.1: + dependencies: + picocolors: 1.1.1 + shell-quote: 1.10.0 + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + ms@2.1.3: {} + + nanoid@3.3.19: {} + + node-fetch@2.7.0(encoding@0.1.13): + dependencies: + whatwg-url: 5.0.0 + optionalDependencies: + encoding: 0.1.13 + + node-releases@2.0.55: {} + + package-manager-detector@1.8.0: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.7: {} + + postcss@8.5.28: + dependencies: + nanoid: 3.3.19 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prettier@3.9.7: {} + + react-dom@19.3.0(react@19.3.0): + dependencies: + react: 19.3.0 + scheduler: 0.28.0 + + react-refresh@0.18.0: {} + + react@19.3.0: {} + + readdirp@5.1.1: {} + + require-from-string@2.0.2: {} + + rolldown@1.2.9: + dependencies: + '@oxc-project/types': 0.150.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.9 + '@rolldown/binding-android-arm64': 1.2.9 + '@rolldown/binding-darwin-arm64': 1.2.9 + '@rolldown/binding-darwin-x64': 1.2.9 + '@rolldown/binding-freebsd-x64': 1.2.9 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.9 + '@rolldown/binding-linux-arm64-gnu': 1.2.9 + '@rolldown/binding-linux-arm64-musl': 1.2.9 + '@rolldown/binding-linux-ppc64-gnu': 1.2.9 + '@rolldown/binding-linux-s390x-gnu': 1.2.9 + '@rolldown/binding-linux-x64-gnu': 1.2.9 + '@rolldown/binding-linux-x64-musl': 1.2.9 + '@rolldown/binding-openharmony-arm64': 1.2.9 + '@rolldown/binding-win32-arm64-msvc': 1.2.9 + '@rolldown/binding-win32-x64-msvc': 1.2.9 + + rou3@0.8.1: {} + + rsbuild-plugin-dts@1.0.1(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(typescript@6.0.3): + dependencies: + '@ast-grep/napi': 0.45.3 + '@rsbuild/core': 2.2.8(@module-federation/runtime-tools@2.9.0) + optionalDependencies: + typescript: 6.0.3 + + safer-buffer@2.1.2: {} + + scheduler@0.28.0: {} + + schema-utils@4.3.0: + dependencies: + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-formats: 2.1.1 + ajv-keywords: 5.1.0(ajv@8.20.0) + + semver@6.3.1: {} + + semver@7.8.5: {} + + seroval-plugins@1.6.7(seroval@1.6.7): + dependencies: + seroval: 1.6.7 + + seroval@1.6.7: {} + + shell-quote@1.10.0: {} + + sisteransi@1.0.5: {} + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + srvx@0.11.22: {} + + tapable@2.3.0: {} + + tinyexec@1.3.1: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + tr46@0.0.3: {} + + tslib@2.8.1: {} + + turbo@2.10.13: + optionalDependencies: + '@turbo/darwin-64': 2.10.13 + '@turbo/darwin-arm64': 2.10.13 + '@turbo/linux-64': 2.10.13 + '@turbo/linux-arm64': 2.10.13 + '@turbo/windows-64': 2.10.13 + '@turbo/windows-arm64': 2.10.13 + + typescript@6.0.3: {} + + ufo@1.6.4: {} + + undici-types@7.16.0: {} + + undici@7.29.0: {} + + unplugin@3.4.0(@rsbuild/core@2.2.8(@module-federation/runtime-tools@2.9.0))(@rspack/core@2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23))(rolldown@1.2.9)(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)): + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.7 + webpack-virtual-modules: 0.6.2 + optionalDependencies: + '@rsbuild/core': 2.2.8(@module-federation/runtime-tools@2.9.0) + '@rspack/core': 2.2.6(@module-federation/runtime-tools@2.9.0)(@swc/helpers@0.5.23) + rolldown: 1.2.9 + vite: 8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1) + + update-browserslist-db@1.3.3(browserslist@4.29.0): + dependencies: + browserslist: 4.29.0 + escalade: 3.2.0 + picocolors: 1.1.1 + + use-sync-external-store@1.7.0(react@19.3.0): + dependencies: + react: 19.3.0 + + vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.28 + rolldown: 1.2.9 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.10.1 + fsevents: 2.3.3 + jiti: 2.7.0 + yaml: 2.9.1 + + vitefu@1.1.3(vite@8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1)): + optionalDependencies: + vite: 8.3.0(@types/node@24.10.1)(jiti@2.7.0)(yaml@2.9.1) + + webidl-conversions@3.0.1: {} + + webpack-virtual-modules@0.6.2: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + ws@8.21.0: {} + + xmlbuilder2@4.0.3: + dependencies: + '@oozcitak/dom': 2.0.2 + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + js-yaml: 4.3.2 + + yallist@3.1.1: {} + + yaml@2.9.1: {} + + zod@4.6.5: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..286cf7f --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - apps/* + - packages/* diff --git a/test/example-build.test.mjs b/test/example-build.test.mjs new file mode 100644 index 0000000..d362d90 --- /dev/null +++ b/test/example-build.test.mjs @@ -0,0 +1,101 @@ +import assert from "node:assert/strict"; +import { existsSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import test from "node:test"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); +const viteHostDist = join(root, "apps", "vite-host", "dist"); +const viteRemoteDist = join(root, "apps", "vite-remote", "dist"); +const rsbuildHostDist = join(root, "apps", "rsbuild-host", "dist"); +const rsbuildRemoteDist = join(root, "apps", "rsbuild-remote", "dist"); + +function readJson(path) { + return JSON.parse(readFileSync(path, "utf8")); +} + +test("all TanStack Start apps produce client and server builds", () => { + for (const [name, dist] of [ + ["Vite host", viteHostDist], + ["Vite remote", viteRemoteDist], + ["rsbuild host", rsbuildHostDist], + ["rsbuild remote", rsbuildRemoteDist], + ]) { + assert.ok(existsSync(join(dist, "client", "mf-manifest.json")), `${name} client`); + const serverEntry = name.startsWith("rsbuild") ? "index.js" : "server.js"; + assert.ok(existsSync(join(dist, "server", serverEntry)), `${name} server`); + } +}); + +test("remote publishes browser and server federation entries", () => { + const client = join(viteRemoteDist, "client"); + assert.ok(existsSync(join(client, "remoteEntry.js"))); + assert.ok(existsSync(join(client, "remoteEntry.ssr.js"))); + + const manifest = readJson(join(client, "mf-manifest.json")); + assert.equal(manifest.name, "tanstack_vite_remote"); + assert.equal(manifest.metaData.remoteEntry.name, "remoteEntry.js"); + assert.equal(manifest.metaData.ssrRemoteEntry.name, "remoteEntry.ssr.js"); + + const statusCard = manifest.exposes.find(({ path }) => path === "./StatusCard"); + assert.ok(statusCard, "StatusCard expose is listed"); + assert.ok(statusCard.assets.css.sync.length > 0, "StatusCard CSS is published"); + + for (const dependency of ["react", "react-dom"]) { + const shared = manifest.shared.find(({ name }) => name === dependency); + assert.equal(shared?.singleton, true, `${dependency} is a singleton`); + } +}); + +test("Vite host records the TanStack remote and server-side loader", () => { + const manifest = readJson(join(viteHostDist, "client", "mf-manifest.json")); + const remote = manifest.remotes.find(({ alias }) => alias === "tanstack_vite_remote"); + assert.equal(remote?.moduleName, "StatusCard"); + + const serverFiles = readFileSync(join(viteHostDist, "server", ".vite", "manifest.json"), "utf8"); + assert.match(serverFiles, /ssrEntryLoader/); + assert.match(serverFiles, /tanstack_vite_remote/); +}); + +test("Vite and Rsbuild publish reciprocal client interoperability contracts", () => { + const viteHostManifest = readJson(join(viteHostDist, "client", "mf-manifest.json")); + const rsbuildHostManifest = readJson(join(rsbuildHostDist, "client", "mf-manifest.json")); + const rsbuildRemoteClient = readJson(join(rsbuildRemoteDist, "client", "mf-manifest.json")); + + assert.equal( + viteHostManifest.remotes.find(({ alias }) => alias === "tanstack_rsbuild_remote")?.moduleName, + "StatusCard", + ); + assert.equal( + rsbuildHostManifest.remotes.find(({ alias }) => alias === "tanstack_vite_remote")?.entry, + "http://127.0.0.1:3001/mf-manifest.json", + ); + assert.equal( + rsbuildHostManifest.remotes.find(({ alias }) => alias === "tanstack_rsbuild_remote")?.entry, + "http://127.0.0.1:3002/mf-manifest.json", + ); + assert.equal(rsbuildRemoteClient.metaData.remoteEntry.type, "global"); + assert.equal(rsbuildRemoteClient.metaData.remoteEntry.name, "remoteEntry.js"); + const statusCard = rsbuildRemoteClient.exposes.find(({ path }) => path === "./StatusCard"); + assert.ok(statusCard?.assets.css.sync.length > 0, "Rsbuild StatusCard CSS is published"); + assert.equal( + existsSync(join(rsbuildRemoteDist, "server", "serverRemoteEntry.cjs")), + false, + "client-only cross-bundler remote does not publish an unused SSR container", + ); + assert.equal( + existsSync(join(rsbuildHostDist, "server", "mf-manifest.json")), + false, + "client-only cross-bundler host does not initialize remotes during SSR", + ); + + for (const manifest of [rsbuildRemoteClient]) { + for (const dependency of ["react", "react-dom"]) { + assert.equal( + manifest.shared.find(({ name }) => name === dependency)?.singleton, + true, + `${dependency} is a singleton`, + ); + } + } +}); diff --git a/test/example-runtime.test.mjs b/test/example-runtime.test.mjs new file mode 100644 index 0000000..a09ba30 --- /dev/null +++ b/test/example-runtime.test.mjs @@ -0,0 +1,144 @@ +import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import test from "node:test"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); + +function startApp(name) { + const child = spawn("pnpm", ["--filter", name, "start"], { + cwd: root, + detached: true, + env: { ...process.env, NODE_ENV: "development" }, + stdio: ["ignore", "pipe", "pipe"], + }); + child.output = ""; + + for (const stream of [child.stdout, child.stderr]) { + stream.on("data", (chunk) => { + child.output = `${child.output}${chunk}`.slice(-12_000); + }); + } + + return child; +} + +async function waitForResponse(url, child, predicate = () => true) { + const deadline = Date.now() + 20_000; + let lastError; + let lastResponse; + + while (Date.now() < deadline) { + if (child.exitCode !== null || child.signalCode !== null) { + assert.fail(`server stopped while waiting for ${url}\n${child.output}`); + } + + try { + const response = await fetch(url, { signal: AbortSignal.timeout(2_000) }); + lastResponse = { body: await response.text(), status: response.status }; + if (predicate(lastResponse)) return lastResponse; + } catch (error) { + lastError = error; + } + + await new Promise((resolve) => setTimeout(resolve, 150)); + } + + assert.fail( + [ + `timed out waiting for ${url}`, + lastResponse ? `last status: ${lastResponse.status}` : undefined, + lastError ? `last error: ${lastError.message}` : undefined, + child.output, + ] + .filter(Boolean) + .join("\n"), + ); +} + +async function stopApp(child) { + if (child.exitCode !== null || child.signalCode !== null) return; + process.kill(-child.pid, "SIGTERM"); + + await Promise.race([ + new Promise((resolve) => child.once("exit", resolve)), + new Promise((resolve) => setTimeout(resolve, 3_000)), + ]); + + if (child.exitCode === null && child.signalCode === null) { + process.kill(-child.pid, "SIGKILL"); + } +} + +test("Vite host server-renders a component from the Vite remote", async () => { + const remote = startApp("tanstack-start-vite-remote"); + let host; + + try { + const remoteEntry = await waitForResponse( + "http://127.0.0.1:3001/remoteEntry.js", + remote, + ({ status }) => status === 200, + ); + assert.match(remoteEntry.body, /virtual:mf-exposes/); + + host = startApp("tanstack-start-vite-host"); + const response = await waitForResponse( + "http://127.0.0.1:3000/", + host, + ({ body, status }) => status === 200 && body.includes("Owned by the remote app"), + ); + + assert.match(response.body, /Two full-stack apps\. One React tree\./); + assert.match(response.body, /Rendered on the server/); + } finally { + if (host) await stopApp(host); + await stopApp(remote); + } +}); + +test("Vite and Rsbuild hosts serve reciprocal browser-interoperability shells", async () => { + const viteRemote = startApp("tanstack-start-vite-remote"); + const rsbuildRemote = startApp("tanstack-start-rsbuild-remote"); + let viteHost; + let rsbuildHost; + + try { + await waitForResponse( + "http://127.0.0.1:3001/remoteEntry.js", + viteRemote, + ({ status }) => status === 200, + ); + const manifest = await waitForResponse( + "http://127.0.0.1:3002/mf-manifest.json", + rsbuildRemote, + ({ status }) => status === 200, + ); + assert.match(manifest.body, /tanstack_rsbuild_remote/); + + viteHost = startApp("tanstack-start-vite-host"); + rsbuildHost = startApp("tanstack-start-rsbuild-host"); + + const viteResponse = await waitForResponse( + "http://127.0.0.1:3000/", + viteHost, + ({ status }) => status === 200, + ); + const rsbuildResponse = await waitForResponse( + "http://127.0.0.1:3003/", + rsbuildHost, + ({ status }) => status === 200, + ); + + assert.match(viteResponse.body, /Rsbuild remote loads after hydration/); + assert.match(rsbuildResponse.body, /Rspack host, two remote formats/); + assert.match(rsbuildResponse.body, /Loading the Rsbuild remote/); + assert.match(rsbuildResponse.body, /Loading the Vite remote/); + } finally { + if (rsbuildHost) await stopApp(rsbuildHost); + if (viteHost) await stopApp(viteHost); + await stopApp(rsbuildRemote); + await stopApp(viteRemote); + } +}); diff --git a/test/release-artifacts.test.mjs b/test/release-artifacts.test.mjs new file mode 100644 index 0000000..f87a4b4 --- /dev/null +++ b/test/release-artifacts.test.mjs @@ -0,0 +1,60 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { existsSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import test from "node:test"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); +const packageRoot = join(root, "packages", "tanstack"); +const dist = join(packageRoot, "dist"); + +test("published declarations reference included source maps", () => { + for (const declarationName of [ + "index.d.cts", + "index.d.ts", + "rsbuild.d.cts", + "rsbuild.d.ts", + "vite.d.cts", + "vite.d.ts", + ]) { + const declaration = readFileSync(join(dist, declarationName), "utf8"); + const sourceMap = declaration.match(/\/\/[#@]\s*sourceMappingURL=([^\s]+)\s*$/m)?.[1]; + + assert.ok(sourceMap, `${declarationName} has no source map reference`); + assert.ok( + existsSync(join(dist, sourceMap)), + `${declarationName} references missing source map ${sourceMap}`, + ); + } +}); + +test("npm package contains only runtime, metadata, and documentation files", () => { + const packed = spawnSync("npm", ["pack", "--dry-run", "--json", "--ignore-scripts"], { + cwd: packageRoot, + encoding: "utf8", + }); + + assert.equal(packed.status, 0, packed.stderr); + const [manifest] = JSON.parse(packed.stdout); + const files = manifest.files.map(({ path }) => path).sort(); + + const runtimeFiles = ["index", "rsbuild", "shared", "vite"].flatMap((entry) => { + const sourceMaps = entry === "index" ? [] : [`dist/${entry}.js.map`]; + return [ + `dist/${entry}.cjs`, + `dist/${entry}.cjs.map`, + `dist/${entry}.d.cts`, + `dist/${entry}.d.cts.map`, + `dist/${entry}.d.ts`, + `dist/${entry}.d.ts.map`, + `dist/${entry}.js`, + ...sourceMaps, + ]; + }); + + assert.deepEqual( + files, + ["CHANGELOG.md", "LICENSE", "README.md", ...runtimeFiles, "package.json"].sort(), + ); +}); diff --git a/test/release-contracts.test.mjs b/test/release-contracts.test.mjs new file mode 100644 index 0000000..b38f9e3 --- /dev/null +++ b/test/release-contracts.test.mjs @@ -0,0 +1,323 @@ +import assert from "node:assert/strict"; +import { spawnSync } from "node:child_process"; +import { + cpSync, + existsSync, + mkdirSync, + mkdtempSync, + readFileSync, + rmSync, + symlinkSync, + writeFileSync, +} from "node:fs"; +import { createRequire } from "node:module"; +import { dirname, join } from "node:path"; +import { tmpdir } from "node:os"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import test from "node:test"; + +const root = join(dirname(fileURLToPath(import.meta.url)), ".."); +const packageRoot = join(root, "packages", "tanstack"); +const packageRequire = createRequire(join(packageRoot, "package.json")); +const packageJson = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8")); +const rootPackageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8")); +const ciWorkflow = readFileSync(join(root, ".github", "workflows", "ci.yml"), "utf8"); +const previewWorkflow = readFileSync(join(root, ".github", "workflows", "pkg-pr-new.yml"), "utf8"); +const releaseWorkflow = readFileSync(join(root, ".github", "workflows", "release.yml"), "utf8"); +const { tanstackStartModuleFederation } = await import( + pathToFileURL(join(packageRoot, "dist", "index.js")).href +); +const { eagerShared, resolveShared } = await import( + pathToFileURL(join(packageRoot, "dist", "shared.js")).href +); +const { tanstackStartModuleFederation: tanstackStartRsbuildModuleFederation } = await import( + pathToFileURL(join(packageRoot, "dist", "rsbuild.js")).href +); + +function upstreamOptions(options) { + const plugins = tanstackStartModuleFederation(options); + assert.ok(Array.isArray(plugins), "federation wrapper returns Vite plugins"); + const upstream = plugins.filter((plugin) => plugin.name === "module-federation-vite"); + assert.equal(upstream.length, 1, "wrapper delegates to one federation instance"); + assert.ok(upstream[0]._options, "upstream plugin exposes normalized options"); + return upstream[0]._options; +} + +test("applies TanStack-safe defaults", () => { + const options = upstreamOptions({ + name: "host", + filename: undefined, + manifest: undefined, + hostInitInjectLocation: undefined, + }); + assert.equal(options.filename, "remoteEntry.js"); + assert.equal(options.manifest, true); + assert.equal(options.hostInitInjectLocation, "entry"); + assert.equal(options.shared.react.shareConfig.singleton, true); + assert.equal(options.shared.react.shareConfig.eager, false, "Vite defaults stay non-eager"); + assert.equal(options.shared["react-dom"].shareConfig.singleton, true); +}); + +test("preserves explicit federation settings and shared entries", () => { + const options = upstreamOptions({ + name: "remote", + filename: "custom-entry.js", + manifest: false, + hostInitInjectLocation: "html", + shared: { + react: { singleton: false, requiredVersion: "^19.0.0" }, + lodash: { singleton: true }, + }, + }); + assert.equal(options.filename, "custom-entry.js"); + assert.equal(options.manifest, false); + assert.equal(options.hostInitInjectLocation, "html"); + assert.equal(options.shared.react.shareConfig.singleton, false); + assert.equal(options.shared.react.shareConfig.requiredVersion, "^19.0.0"); + assert.equal(options.shared.lodash.shareConfig.singleton, true); + assert.ok(options.shared["react-dom"], "unconfigured React DOM remains shared"); +}); + +test("does not mutate the caller's options", () => { + const options = { + name: "host", + shared: { react: { singleton: false } }, + remotes: { remote: { type: "module", name: "remote", entry: "remoteEntry.js" } }, + }; + const before = structuredClone(options); + upstreamOptions(options); + assert.deepEqual(options, before); +}); + +test("accepts the upstream array form", () => { + const options = upstreamOptions({ name: "remote", shared: ["lodash", "react"] }); + assert.deepEqual(Object.keys(options.shared).sort(), ["lodash", "react", "react-dom"]); + assert.equal(options.shared.react.shareConfig.singleton, true); + assert.equal(options.shared["react-dom"].shareConfig.singleton, true); +}); + +test("leaves the build target to each TanStack environment", () => { + const options = upstreamOptions({ name: "host", target: "node" }); + assert.equal(options.target, undefined); +}); + +test("Rsbuild defaults make synchronous React shares eager without mutating overrides", () => { + const callerShared = { react: { eager: false, singleton: false } }; + const before = structuredClone(callerShared); + const shared = resolveShared(callerShared, eagerShared); + + assert.deepEqual(callerShared, before); + assert.deepEqual(shared.react, { eager: false, singleton: false }); + assert.deepEqual(shared["react-dom"], { eager: true, singleton: true }); +}); + +test("Rsbuild configures separate browser and async-node federation environments", () => { + const plugins = tanstackStartRsbuildModuleFederation({ + federation: { name: "test_remote" }, + }); + + assert.deepEqual( + plugins.map(({ name }) => name), + [ + "rsbuild:module-federation-enhanced", + "tanstack-start-federation-client-compat", + "rsbuild:module-federation-enhanced", + "tanstack-start-federation-ssr-compat", + ], + ); + + const clientConfig = { name: "client", output: {} }; + const serverConfig = { + name: "ssr", + output: { chunkLoadingGlobal: "stale" }, + }; + applyCompilerHook( + plugins.find(({ name }) => name === "tanstack-start-federation-client-compat"), + [clientConfig, serverConfig], + ); + applyCompilerHook( + plugins.find(({ name }) => name === "tanstack-start-federation-ssr-compat"), + [clientConfig, serverConfig], + ); + + assert.deepEqual(clientConfig.output, { + chunkFormat: "array-push", + chunkLoading: "jsonp", + chunkLoadingGlobal: "chunk_test_remote", + module: false, + uniqueName: "test_remote", + }); + assert.equal(serverConfig.target, "async-node"); + assert.deepEqual(serverConfig.output, { + chunkFilename: "[id].test_remote.cjs", + chunkFormat: "commonjs", + chunkLoading: "async-node", + filename: "[name].cjs", + library: { type: "commonjs2" }, + module: false, + }); + + assert.equal( + tanstackStartRsbuildModuleFederation({ + federation: { name: "browser_only" }, + server: false, + }).length, + 2, + ); + + const esmPlugins = tanstackStartRsbuildModuleFederation({ + federation: { name: "esm_server" }, + server: { forceCommonJsOutput: false }, + }); + const esmServerConfig = { + name: "ssr", + output: { chunkFilename: "[id].js", filename: "[name].js" }, + }; + applyCompilerHook( + esmPlugins.find(({ name }) => name === "tanstack-start-federation-ssr-compat"), + [esmServerConfig], + ); + assert.deepEqual(esmServerConfig, { + name: "ssr", + output: { chunkFilename: "[id].js", filename: "[name].js" }, + }); +}); + +function applyCompilerHook(plugin, bundlerConfigs) { + let hook; + plugin.setup({ + onBeforeCreateCompiler(callback) { + hook = callback; + }, + }); + assert.ok(hook, `${plugin.name} registers a compiler hook`); + hook({ bundlerConfigs }); +} + +test("published metadata selects ESM and CommonJS builds with matching types", () => { + assert.equal(packageJson.type, "module"); + assert.equal(packageJson.exports["."].import.default, "./dist/index.js"); + assert.equal(packageJson.exports["."].import.types, "./dist/index.d.ts"); + assert.equal(packageJson.exports["."].require.default, "./dist/index.cjs"); + assert.equal(packageJson.exports["."].require.types, "./dist/index.d.cts"); + assert.equal( + packageRequire.resolve("@module-federation/tanstack"), + join(packageRoot, "dist", "index.cjs"), + ); + for (const file of ["index.cjs", "index.js", "index.d.cts", "index.d.ts"]) { + assert.ok(existsSync(join(packageRoot, "dist", file)), `dist/${file} exists`); + } +}); + +test("clean consumers resolve CJS and ESM entrypoints without the other adapter", () => { + const viteConsumer = createConsumer(["@module-federation/vite"]); + const rsbuildConsumer = createConsumer(["@module-federation/rsbuild-plugin", "@rsbuild/core"]); + + try { + const viteRequire = createRequire(join(viteConsumer, "consumer.cjs")); + const rsbuildRequire = createRequire(join(rsbuildConsumer, "consumer.cjs")); + const cjsRoot = viteRequire("@module-federation/tanstack"); + const cjsRsbuild = rsbuildRequire("@module-federation/tanstack/rsbuild"); + + assert.equal(typeof cjsRoot.tanstackStartModuleFederation, "function"); + assert.equal(typeof cjsRsbuild.tanstackStartModuleFederation, "function"); + assertConsumerImport(viteConsumer, "@module-federation/tanstack"); + assertConsumerImport(viteConsumer, "@module-federation/tanstack/vite"); + assertConsumerImport(rsbuildConsumer, "@module-federation/tanstack/rsbuild"); + } finally { + rmSync(viteConsumer, { force: true, recursive: true }); + rmSync(rsbuildConsumer, { force: true, recursive: true }); + } + + assert.doesNotMatch( + readFileSync(join(packageRoot, "dist", "rsbuild.js"), "utf8"), + /@module-federation\/vite/, + ); + assert.doesNotMatch( + readFileSync(join(packageRoot, "dist", "vite.js"), "utf8"), + /@module-federation\/rsbuild-plugin/, + ); +}); + +function assertConsumerImport(consumer, specifier) { + const entry = join(consumer, "consumer.mjs"); + writeFileSync( + entry, + `import { tanstackStartModuleFederation } from ${JSON.stringify(specifier)};\n` + + `if (typeof tanstackStartModuleFederation !== "function") process.exit(2);\n`, + ); + const result = spawnSync(process.execPath, [entry], { + cwd: consumer, + encoding: "utf8", + }); + assert.equal(result.status, 0, result.stderr); +} + +function createConsumer(adapterPackages) { + const consumer = mkdtempSync(join(tmpdir(), "mf-tanstack-consumer-")); + const packageTarget = join(consumer, "node_modules", "@module-federation", "tanstack"); + mkdirSync(dirname(packageTarget), { recursive: true }); + cpSync(packageRoot, packageTarget, { + filter: (source) => !source.includes("/node_modules/") && !source.includes("/.turbo/"), + recursive: true, + }); + + for (const packageName of adapterPackages) { + const adapterRoot = findPackageRoot(packageRequire.resolve(packageName)); + const adapterTarget = join(consumer, "node_modules", ...packageName.split("/")); + mkdirSync(dirname(adapterTarget), { recursive: true }); + symlinkSync(adapterRoot, adapterTarget); + } + + return consumer; +} + +function findPackageRoot(entryPoint) { + let directory = dirname(entryPoint); + while (!existsSync(join(directory, "package.json"))) { + const parent = dirname(directory); + if (parent === directory) throw new Error(`Could not find package root for ${entryPoint}`); + directory = parent; + } + return directory; +} + +test("CI uses Node versions supported by the package build toolchain", () => { + const supportedNode = "^22.18.0 || ^24.11.0 || >=26.0.0"; + assert.equal(rootPackageJson.engines.node, supportedNode); + assert.equal(packageJson.engines.node, supportedNode); + assert.match(ciWorkflow, /node: \["22\.18\.0", "24", "26"\]/); + assert.doesNotMatch(ciWorkflow, /22\.12\.0/); +}); + +test("bundler adapters are isolated optional peers", () => { + for (const dependency of [ + "@module-federation/rsbuild-plugin", + "@module-federation/vite", + "@rsbuild/core", + "vite", + ]) { + assert.ok(packageJson.peerDependencies[dependency], `${dependency} has a peer range`); + assert.equal(packageJson.peerDependenciesMeta[dependency]?.optional, true); + } + assert.equal(packageJson.optionalDependencies, undefined); +}); + +test("GitHub prereleases publish the version validated against their tag", () => { + assert.match(releaseWorkflow, /Tag\/version mismatch/); + assert.doesNotMatch(releaseWorkflow, /Set deterministic prerelease version/); + assert.doesNotMatch( + releaseWorkflow, + /github\.event\.release\.prerelease[\s\S]*npm pkg set version/, + ); +}); + +test("npm publications share one package-wide concurrency queue", () => { + assert.match(releaseWorkflow, /group: publish-module-federation-tanstack/); + assert.doesNotMatch(releaseWorkflow, /group:.*github\.(?:ref|run)/); +}); + +test("first package preview does not require an existing npm release", () => { + assert.match(previewWorkflow, /pkg-pr-new@0\.0\.54 publish \.\/packages\/tanstack/); + assert.doesNotMatch(previewWorkflow, /--compact/); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..50148eb --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "files": [], + "references": [ + { "path": "./packages/tanstack/tsconfig.json" }, + { "path": "./apps/vite-host/tsconfig.json" }, + { "path": "./apps/vite-remote/tsconfig.json" }, + { "path": "./apps/rsbuild-host/tsconfig.json" }, + { "path": "./apps/rsbuild-remote/tsconfig.json" } + ] +} diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..5c6afdd --- /dev/null +++ b/turbo.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://turborepo.com/schema.json", + "tasks": { + "build": { + "dependsOn": ["^build"], + "inputs": ["$TURBO_DEFAULT$", ".env*"], + "outputs": [".output/**", "dist/**"] + }, + "dev": { + "cache": false, + "persistent": true + }, + "start": { + "cache": false, + "persistent": true + }, + "typecheck": { + "dependsOn": ["^build", "^typecheck"], + "inputs": ["$TURBO_DEFAULT$", ".env*"], + "outputs": [] + } + } +}