From a7b9496b8c9cca8ebbe75ea72343627d75dba593 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Wed, 24 Jun 2026 12:45:11 -0400 Subject: [PATCH 01/16] feat(vite-plugin): support multiple vite environments --- .../cloudflare-rolldown-plugin/src/factory.ts | 20 +- .../cloudflare-rolldown-plugin/src/options.ts | 18 +- .../cloudflare-rolldown-plugin/src/plugin.ts | 8 +- .../src/plugins/options.ts | 205 ++++++++++++------ .../cloudflare-vite-plugin/src/dev-plugin.ts | 45 ++-- .../cloudflare-vite-plugin/src/dev-server.ts | 2 +- .../src/module-runner/module-runner.worker.ts | 12 +- packages/cloudflare-vite-plugin/src/plugin.ts | 4 +- 8 files changed, 207 insertions(+), 107 deletions(-) diff --git a/packages/cloudflare-rolldown-plugin/src/factory.ts b/packages/cloudflare-rolldown-plugin/src/factory.ts index 5b5cfcf1..bc6eda91 100644 --- a/packages/cloudflare-rolldown-plugin/src/factory.ts +++ b/packages/cloudflare-rolldown-plugin/src/factory.ts @@ -1,6 +1,6 @@ import type * as rolldown from "rolldown"; import type * as vite from "vite"; -import type { CloudflarePluginOptions } from "./options.js"; +import type { BasePluginOptions } from "./options.js"; export interface PluginInput { shared?: Omit, "name">; @@ -9,32 +9,32 @@ export interface PluginInput { } export interface NullablePluginOutput { - rolldown: (options: CloudflarePluginOptions) => rolldown.Plugin | null; - vite: (options: CloudflarePluginOptions) => vite.Plugin | null; + rolldown: (options: BasePluginOptions) => rolldown.Plugin | null; + vite: (options: BasePluginOptions) => vite.Plugin | null; } export interface PluginOutput { - rolldown: (options: CloudflarePluginOptions) => rolldown.Plugin; - vite: (options: CloudflarePluginOptions) => vite.Plugin; + rolldown: (options: BasePluginOptions) => rolldown.Plugin; + vite: (options: BasePluginOptions) => vite.Plugin; } export function createPlugin( pluginName: TName, - make: (options: CloudflarePluginOptions) => PluginInput, + make: (options: BasePluginOptions) => PluginInput, ): PluginOutput; export function createPlugin( pluginName: TName, - make: (options: CloudflarePluginOptions) => PluginInput | undefined, + make: (options: BasePluginOptions) => PluginInput | undefined, ): NullablePluginOutput; export function createPlugin( pluginName: TName, - make: (options: CloudflarePluginOptions) => PluginInput | undefined, + make: (options: BasePluginOptions) => PluginInput | undefined, ): PluginOutput | NullablePluginOutput { const name = `distilled-cloudflare:${pluginName}`; return { - rolldown: (options: CloudflarePluginOptions) => { + rolldown: (options: BasePluginOptions) => { const plugin = make(options); if (!plugin) return null; return { @@ -43,7 +43,7 @@ export function createPlugin( ...plugin.rolldown, }; }, - vite: (options: CloudflarePluginOptions) => { + vite: (options: BasePluginOptions) => { const plugin = make(options); if (!plugin) return null; return { diff --git a/packages/cloudflare-rolldown-plugin/src/options.ts b/packages/cloudflare-rolldown-plugin/src/options.ts index 9ad597fb..ae4d2f94 100644 --- a/packages/cloudflare-rolldown-plugin/src/options.ts +++ b/packages/cloudflare-rolldown-plugin/src/options.ts @@ -1,4 +1,4 @@ -export interface CloudflarePluginOptions { +export interface BasePluginOptions { /** * The main entry point to use. * Defaults to the one from your Rolldown configuration. @@ -28,4 +28,20 @@ export interface CloudflarePluginOptions { * ``` */ exports?: Array; + /** + * Which Vite environment hosts the Worker, and any child environments it + * loads at runtime. Defaults to the single `ssr` environment. + * + * `@vitejs/plugin-rsc` apps run the Worker in the `rsc` environment (resolved + * with the `react-server` condition) and load the `ssr` environment from it, + * so they set `{ name: "rsc", childEnvironments: ["ssr"] }`. Every named + * environment is given the Worker treatment (workerd resolve conditions, + * dependency pre-bundling) and a module runner in dev. + * + * @default { name: "ssr", childEnvironments: [] } + */ + viteEnvironment?: { + name?: string; + childEnvironments?: Array; + }; } diff --git a/packages/cloudflare-rolldown-plugin/src/plugin.ts b/packages/cloudflare-rolldown-plugin/src/plugin.ts index 2417f385..14044014 100644 --- a/packages/cloudflare-rolldown-plugin/src/plugin.ts +++ b/packages/cloudflare-rolldown-plugin/src/plugin.ts @@ -1,5 +1,5 @@ import type * as rolldown from "rolldown"; -import type { CloudflarePluginOptions } from "./options.js"; +import type { BasePluginOptions } from "./options.js"; import { additionalModulesPlugin, cloudflareExternalsPlugin, @@ -11,9 +11,11 @@ import { wasmInitPlugin, } from "./plugins/index.js"; -export type CloudflarePlugin = (options?: CloudflarePluginOptions) => Array; +export type RolldownPluginOptions = Omit; -const cloudflare: CloudflarePlugin = (options = {}) => { +export type RolldownPlugin = (options?: RolldownPluginOptions) => Array; + +const cloudflare: RolldownPlugin = (options = {}) => { return [ optionsPlugin.rolldown(options), cloudflareExternalsPlugin.rolldown(options), diff --git a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts index 61862ebd..25c090e9 100644 --- a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts +++ b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts @@ -1,7 +1,7 @@ import path from "node:path"; import type * as vite from "vite"; import { createPlugin } from "../factory.js"; -import type { CloudflarePluginOptions } from "../options.js"; +import type { BasePluginOptions } from "../options.js"; import { hasNodejsCompat } from "../utils.js"; import { WORKER_ENTRY_PREFIX } from "./virtual-modules.js"; @@ -23,14 +23,17 @@ const DEFAULT_RESOLVE_EXTENSIONS = [ const TARGET = "es2024"; export interface OptionsApi { + environmentNames: WorkerEnvironments; input: () => Record; } export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (pluginOptions) => { + const environmentNames = parseEnvironments(pluginOptions); let input: Record = {}; return { shared: { api: { + environmentNames, input: () => input, }, }, @@ -56,11 +59,85 @@ export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (plu const vite = await import("vite"); const isRolldown = "rolldownVersion" in this.meta; input = normalizeInput( - pluginOptions.main ?? - userConfig.environments?.ssr?.build?.rolldownOptions?.input ?? - userConfig.environments?.ssr?.build?.rollupOptions?.input ?? - {}, + pluginOptions.main ?? defaultEnvironmentEntries(environmentNames[0], userConfig) ?? {}, ); + const makeEnvironment = ({ + name, + isEntry, + }: { + name: string; + isEntry: boolean; + }): vite.EnvironmentOptions => { + const entries = isEntry + ? (pluginOptions.main ?? defaultEnvironmentEntries(name, userConfig)) + : (defaultEnvironmentEntries(name, userConfig) ?? pluginOptions.main); + return { + resolve: { + noExternal: true, + conditions: [...DEFAULT_RESOLVE_CONDITION_NAMES, "development|production"], + }, + optimizeDeps: { + noDiscovery: false, + ignoreOutdatedRequests: true, + entries: asArray(entries)?.map(vite.normalizePath), + ...(isRolldown + ? { + rolldownOptions: { + platform: "neutral", + resolve: { + conditionNames: [ + ...DEFAULT_RESOLVE_CONDITION_NAMES, + "development|production", + ], + mainFields: DEFAULT_RESOLVE_MAIN_FIELDS, + extensions: DEFAULT_RESOLVE_EXTENSIONS, + }, + transform: { + target: TARGET, + define, + }, + }, + } + : { + esbuildOptions: { + platform: "neutral", + conditions: [...DEFAULT_RESOLVE_CONDITION_NAMES, "development|production"], + resolveExtensions: DEFAULT_RESOLVE_EXTENSIONS, + mainFields: DEFAULT_RESOLVE_MAIN_FIELDS, + target: TARGET, + define, + }, + }), + }, + keepProcessEnv: true, + // The entry environment owns the Worker's build input and server + // output directory; children (e.g. `ssr`) keep their own build config + // from the framework plugin (`@vitejs/plugin-rsc`). + ...(isEntry + ? { + build: { + ssr: true, + target: TARGET, + emitAssets: true, + copyPublicDir: false, + outDir: getOutputDirectory(userConfig, name), + ...(isRolldown + ? { + rolldownOptions: { + ...rollupOptions, + platform: "neutral", + resolve: { + mainFields: DEFAULT_RESOLVE_MAIN_FIELDS, + extensions: DEFAULT_RESOLVE_EXTENSIONS, + }, + }, + } + : { rollupOptions }), + }, + } + : {}), + }; + }; const rollupOptions: vite.Rollup.RollupOptions = { input: wrapInput(input), preserveEntrySignatures: "strict", @@ -94,65 +171,12 @@ export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (plu outDir: getOutputDirectory(userConfig, "client"), }, }, - ssr: { - resolve: { - noExternal: true, - conditions: [...DEFAULT_RESOLVE_CONDITION_NAMES, "development|production"], - }, - build: { - ssr: true, - target: TARGET, - emitAssets: true, - copyPublicDir: false, - outDir: getOutputDirectory(userConfig, "server"), - ...(isRolldown - ? { - rolldownOptions: { - ...rollupOptions, - platform: "neutral", - resolve: { - mainFields: DEFAULT_RESOLVE_MAIN_FIELDS, - extensions: DEFAULT_RESOLVE_EXTENSIONS, - }, - }, - } - : { rollupOptions }), - }, - optimizeDeps: { - noDiscovery: false, - ignoreOutdatedRequests: true, - entries: pluginOptions.main ? vite.normalizePath(pluginOptions.main) : undefined, - ...(isRolldown - ? { - rolldownOptions: { - platform: "neutral", - resolve: { - conditionNames: [ - ...DEFAULT_RESOLVE_CONDITION_NAMES, - "development|production", - ], - mainFields: DEFAULT_RESOLVE_MAIN_FIELDS, - extensions: DEFAULT_RESOLVE_EXTENSIONS, - }, - transform: { - target: TARGET, - define, - }, - }, - } - : { - esbuildOptions: { - platform: "neutral", - conditions: [...DEFAULT_RESOLVE_CONDITION_NAMES, "development|production"], - resolveExtensions: DEFAULT_RESOLVE_EXTENSIONS, - mainFields: DEFAULT_RESOLVE_MAIN_FIELDS, - target: TARGET, - define, - }, - }), - }, - keepProcessEnv: true, - }, + ...Object.fromEntries( + environmentNames.map((name, index) => [ + name, + makeEnvironment({ name, isEntry: index === 0 }), + ]), + ), }, }; }, @@ -160,6 +184,51 @@ export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (plu }; }); +export type WorkerEnvironments = [string, ...Array]; + +const parseEnvironments = (options: BasePluginOptions): WorkerEnvironments => { + const entry = options.viteEnvironment?.name ?? "ssr"; + if (entry === "client") { + throw new Error( + 'The "client" environment cannot be used as a worker environment because it is reserved for the browser.', + ); + } + const children = (options.viteEnvironment?.childEnvironments ?? []).map((name, index, self) => { + if (name === "client") { + throw new Error( + 'The "client" environment cannot be used as a worker environment because it is reserved for the browser.', + ); + } else if (self.indexOf(name) !== index) { + throw new Error( + `The name "${name}" appears more than once in the Vite environment list. Worker environment names must be unique.`, + ); + } else if (name === entry) { + throw new Error( + `The child environment "${name}" cannot have the same name as the entry environment "${entry}".`, + ); + } + return name; + }); + return [entry, ...children]; +}; + +const defaultEnvironmentEntries = ( + environmentName: string, + userConfig: vite.UserConfig, +): vite.Rolldown.InputOption | undefined => { + const environment = userConfig.environments?.[environmentName]; + return environment?.build?.rolldownOptions?.input ?? environment?.build?.rollupOptions?.input; +}; + +const asArray = ( + input: string | Array | Record | undefined, +): Array | undefined => { + if (!input) return; + if (typeof input === "string") return [input]; + if (Array.isArray(input) && input.length > 0) return input; + return Object.values(input); +}; + const normalizeInput = ( input: string | Array | Record, ): Record => { @@ -177,7 +246,7 @@ const wrapInput = (input: Record) => Object.entries(input).map(([key, id]) => [key, `${WORKER_ENTRY_PREFIX}${id}` as const]), ); -function getDefine(options: CloudflarePluginOptions, nodeEnv: string): Record { +const getDefine = (options: BasePluginOptions, nodeEnv: string): Record => { return { "process.env.NODE_ENV": JSON.stringify(nodeEnv), "global.process.env.NODE_ENV": JSON.stringify(nodeEnv), @@ -200,13 +269,13 @@ function getDefine(options: CloudflarePluginOptions, nodeEnv: string): Record { const rootOutputDirectory = userConfig.build?.outDir ?? "dist"; return ( userConfig.environments?.[environmentName]?.build?.outDir ?? path.join(rootOutputDirectory, environmentName) ); -} +}; diff --git a/packages/cloudflare-vite-plugin/src/dev-plugin.ts b/packages/cloudflare-vite-plugin/src/dev-plugin.ts index 0555ecc6..4aa40150 100644 --- a/packages/cloudflare-vite-plugin/src/dev-plugin.ts +++ b/packages/cloudflare-vite-plugin/src/dev-plugin.ts @@ -28,26 +28,28 @@ export function dev(options: CloudflareVitePluginOptions): vite.Plugin { optionsApi = resolvePluginApi(plugins ?? [], "distilled-cloudflare:options"); }, config() { - return { - environments: { - ssr: { - dev: { - createEnvironment(name, config) { - const hasConfigureServer = config.plugins.some( - (plugin) => - plugin.name === "distilled-cloudflare:dev" && - plugin.configureServer !== undefined, - ); - if (!hasConfigureServer) { - return vite.createRunnableDevEnvironment(name, config); - } - - return new DistilledDevEnvironment(name, config); - }, - }, + if (!optionsApi) { + throw new Error("Cannot resolve the distilled-cloudflare:options plugin"); + } + const environment: vite.EnvironmentOptions = { + dev: { + createEnvironment(name, config) { + const hasConfigureServer = config.plugins.some( + (plugin) => + plugin.name === "distilled-cloudflare:dev" && plugin.configureServer !== undefined, + ); + if (!hasConfigureServer) { + return vite.createRunnableDevEnvironment(name, config); + } + return new DistilledDevEnvironment(name, config); }, }, }; + return { + environments: Object.fromEntries( + optionsApi.environmentNames.map((name) => [name, environment]), + ), + }; }, async buildEnd() { if (!isServerRestarting) { @@ -89,9 +91,12 @@ export function dev(options: CloudflareVitePluginOptions): vite.Plugin { options.context ?? context!, ); const address = handle.address; - const ssrEnvironment = server.environments.ssr; - if (ssrEnvironment instanceof DistilledDevEnvironment) { - await ssrEnvironment.connect(address); + for (const environmentName of optionsApi.environmentNames) { + const environment = server.environments[environmentName]; + if (environment instanceof DistilledDevEnvironment) { + await environment.depsOptimizer?.init(); + await environment.connect(address); + } } if (!input) { // If there is no input, we are in SPA mode, so we don't need to route requests to the server. diff --git a/packages/cloudflare-vite-plugin/src/dev-server.ts b/packages/cloudflare-vite-plugin/src/dev-server.ts index 41182d89..fbf8faf8 100644 --- a/packages/cloudflare-vite-plugin/src/dev-server.ts +++ b/packages/cloudflare-vite-plugin/src/dev-server.ts @@ -103,7 +103,7 @@ const serve = Effect.fn(function* ( className: "ModuleRunnerDO", }), Json.local("__DISTILLED_ENVIRONMENT__", { - environmentName: "ssr", + environmentName: options.viteEnvironment?.name ?? "ssr", entryId: entry.id, entryName: entry.name, }), diff --git a/packages/cloudflare-vite-plugin/src/module-runner/module-runner.worker.ts b/packages/cloudflare-vite-plugin/src/module-runner/module-runner.worker.ts index a7a0771d..25539e09 100644 --- a/packages/cloudflare-vite-plugin/src/module-runner/module-runner.worker.ts +++ b/packages/cloudflare-vite-plugin/src/module-runner/module-runner.worker.ts @@ -57,7 +57,7 @@ export class ModuleRunnerDO extends DurableObject { globalThis.__VITE_ENVIRONMENT_RUNNER_IMPORT__ = async (environmentName: string, id: string) => { const moduleRunner = this.moduleRunners.get(environmentName); if (!moduleRunner) { - throw new Error(`Module runner not initialized for environment: "${environmentName}"`); + throw new NotInitializedError(environmentName); } return callbacks.run(this.env, () => moduleRunner.import(id)); }; @@ -76,7 +76,7 @@ export class ModuleRunnerDO extends DurableObject { send(environmentName: string, data: string): void { const webSocket = this.webSockets.get(environmentName); if (!webSocket) { - throw new Error(`Module runner not initialized for environment: "${environmentName}"`); + throw new NotInitializedError(environmentName); } webSocket.send(data); } @@ -173,3 +173,11 @@ export class ModuleRunnerDO extends DurableObject { ); } } + +class NotInitializedError extends Error { + constructor(environmentName: string) { + super( + `Module runner not initialized for environment: "${environmentName}". If this is a child environment, make sure to set \`childEnvironments: ["${environmentName}"]\` in the plugin config.`, + ); + } +} diff --git a/packages/cloudflare-vite-plugin/src/plugin.ts b/packages/cloudflare-vite-plugin/src/plugin.ts index d5748323..27e709cb 100644 --- a/packages/cloudflare-vite-plugin/src/plugin.ts +++ b/packages/cloudflare-vite-plugin/src/plugin.ts @@ -1,4 +1,4 @@ -import type { CloudflarePluginOptions } from "@distilled.cloud/cloudflare-rolldown-plugin/options"; +import type { BasePluginOptions } from "@distilled.cloud/cloudflare-rolldown-plugin/options"; import { additionalModulesPlugin, cloudflareExternalsPlugin, @@ -20,7 +20,7 @@ import { dev } from "./dev-plugin.js"; export interface CloudflareVitePluginOptions< B extends BindingHooks = BindingHooks, -> extends CloudflarePluginOptions { +> extends BasePluginOptions { worker?: Omit, "compatibilityDate" | "compatibilityFlags" | "modules">; context?: Context.Context; } From 1f01873ce14b01a8e614c8675206995ff4f25446 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Wed, 24 Jun 2026 12:50:55 -0400 Subject: [PATCH 02/16] parse environments at top level --- .../cloudflare-rolldown-plugin/src/options.ts | 26 ++++++++++++++ .../src/plugins/options.ts | 34 ++----------------- .../cloudflare-vite-plugin/src/dev-plugin.ts | 8 ++--- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/packages/cloudflare-rolldown-plugin/src/options.ts b/packages/cloudflare-rolldown-plugin/src/options.ts index ae4d2f94..6d2fdfec 100644 --- a/packages/cloudflare-rolldown-plugin/src/options.ts +++ b/packages/cloudflare-rolldown-plugin/src/options.ts @@ -45,3 +45,29 @@ export interface BasePluginOptions { childEnvironments?: Array; }; } + +export const parseViteEnvironments = (options: BasePluginOptions): [string, ...Array] => { + const entry = options.viteEnvironment?.name ?? "ssr"; + if (entry === "client") { + throw new Error( + 'The "client" environment cannot be used as a worker environment because it is reserved for the browser.', + ); + } + const children = (options.viteEnvironment?.childEnvironments ?? []).map((name, index, self) => { + if (name === "client") { + throw new Error( + 'The "client" environment cannot be used as a worker environment because it is reserved for the browser.', + ); + } else if (self.indexOf(name) !== index) { + throw new Error( + `The name "${name}" appears more than once in the Vite environment list. Worker environment names must be unique.`, + ); + } else if (name === entry) { + throw new Error( + `The child environment "${name}" cannot have the same name as the entry environment "${entry}".`, + ); + } + return name; + }); + return [entry, ...children]; +}; diff --git a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts index 25c090e9..e77de74d 100644 --- a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts +++ b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts @@ -1,7 +1,7 @@ import path from "node:path"; import type * as vite from "vite"; import { createPlugin } from "../factory.js"; -import type { BasePluginOptions } from "../options.js"; +import { parseViteEnvironments, type BasePluginOptions } from "../options.js"; import { hasNodejsCompat } from "../utils.js"; import { WORKER_ENTRY_PREFIX } from "./virtual-modules.js"; @@ -23,17 +23,14 @@ const DEFAULT_RESOLVE_EXTENSIONS = [ const TARGET = "es2024"; export interface OptionsApi { - environmentNames: WorkerEnvironments; input: () => Record; } export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (pluginOptions) => { - const environmentNames = parseEnvironments(pluginOptions); let input: Record = {}; return { shared: { api: { - environmentNames, input: () => input, }, }, @@ -58,6 +55,7 @@ export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (plu async config(userConfig) { const vite = await import("vite"); const isRolldown = "rolldownVersion" in this.meta; + const environmentNames = parseViteEnvironments(pluginOptions); input = normalizeInput( pluginOptions.main ?? defaultEnvironmentEntries(environmentNames[0], userConfig) ?? {}, ); @@ -184,34 +182,6 @@ export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (plu }; }); -export type WorkerEnvironments = [string, ...Array]; - -const parseEnvironments = (options: BasePluginOptions): WorkerEnvironments => { - const entry = options.viteEnvironment?.name ?? "ssr"; - if (entry === "client") { - throw new Error( - 'The "client" environment cannot be used as a worker environment because it is reserved for the browser.', - ); - } - const children = (options.viteEnvironment?.childEnvironments ?? []).map((name, index, self) => { - if (name === "client") { - throw new Error( - 'The "client" environment cannot be used as a worker environment because it is reserved for the browser.', - ); - } else if (self.indexOf(name) !== index) { - throw new Error( - `The name "${name}" appears more than once in the Vite environment list. Worker environment names must be unique.`, - ); - } else if (name === entry) { - throw new Error( - `The child environment "${name}" cannot have the same name as the entry environment "${entry}".`, - ); - } - return name; - }); - return [entry, ...children]; -}; - const defaultEnvironmentEntries = ( environmentName: string, userConfig: vite.UserConfig, diff --git a/packages/cloudflare-vite-plugin/src/dev-plugin.ts b/packages/cloudflare-vite-plugin/src/dev-plugin.ts index 4aa40150..4d0a1789 100644 --- a/packages/cloudflare-vite-plugin/src/dev-plugin.ts +++ b/packages/cloudflare-vite-plugin/src/dev-plugin.ts @@ -1,3 +1,4 @@ +import { parseViteEnvironments } from "@distilled.cloud/cloudflare-rolldown-plugin/options"; import type { OptionsApi } from "@distilled.cloud/cloudflare-rolldown-plugin/plugins"; import { resolvePluginApi } from "@distilled.cloud/cloudflare-rolldown-plugin/utils"; import type { RuntimeServices } from "@distilled.cloud/cloudflare-runtime"; @@ -12,6 +13,7 @@ import { handleWebSocket } from "./websockets.js"; let context: Context.Context | undefined; export function dev(options: CloudflareVitePluginOptions): vite.Plugin { + const environmentNames = parseViteEnvironments(options); let handle: ServerHandle | undefined; let isServerRestarting = false; let removeUpgradeListener: (() => void) | undefined; @@ -46,9 +48,7 @@ export function dev(options: CloudflareVitePluginOptions): vite.Plugin { }, }; return { - environments: Object.fromEntries( - optionsApi.environmentNames.map((name) => [name, environment]), - ), + environments: Object.fromEntries(environmentNames.map((name) => [name, environment])), }; }, async buildEnd() { @@ -91,7 +91,7 @@ export function dev(options: CloudflareVitePluginOptions): vite.Plugin { options.context ?? context!, ); const address = handle.address; - for (const environmentName of optionsApi.environmentNames) { + for (const environmentName of environmentNames) { const environment = server.environments[environmentName]; if (environment instanceof DistilledDevEnvironment) { await environment.depsOptimizer?.init(); From 05352fcb6377c5963c065fa941ed053559c05af2 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Wed, 24 Jun 2026 12:54:05 -0400 Subject: [PATCH 03/16] rename environments prop --- packages/cloudflare-rolldown-plugin/src/options.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cloudflare-rolldown-plugin/src/options.ts b/packages/cloudflare-rolldown-plugin/src/options.ts index 6d2fdfec..4e3a8eb6 100644 --- a/packages/cloudflare-rolldown-plugin/src/options.ts +++ b/packages/cloudflare-rolldown-plugin/src/options.ts @@ -38,22 +38,22 @@ export interface BasePluginOptions { * environment is given the Worker treatment (workerd resolve conditions, * dependency pre-bundling) and a module runner in dev. * - * @default { name: "ssr", childEnvironments: [] } + * @default { entry: "ssr", children: [] } */ - viteEnvironment?: { - name?: string; - childEnvironments?: Array; + viteEnvironments?: { + entry?: string; + children?: Array; }; } export const parseViteEnvironments = (options: BasePluginOptions): [string, ...Array] => { - const entry = options.viteEnvironment?.name ?? "ssr"; + const entry = options.viteEnvironments?.entry ?? "ssr"; if (entry === "client") { throw new Error( 'The "client" environment cannot be used as a worker environment because it is reserved for the browser.', ); } - const children = (options.viteEnvironment?.childEnvironments ?? []).map((name, index, self) => { + const children = (options.viteEnvironments?.children ?? []).map((name, index, self) => { if (name === "client") { throw new Error( 'The "client" environment cannot be used as a worker environment because it is reserved for the browser.', From f968a52dc85b77e0fd2b23a755e173d2d32cf558 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Wed, 24 Jun 2026 12:57:55 -0400 Subject: [PATCH 04/16] correct entry environment type --- .../cloudflare-vite-plugin/src/dev-plugin.ts | 2 +- .../cloudflare-vite-plugin/src/dev-server.ts | 19 +++++-------------- .../src/module-runner/constants.shared.ts | 6 ++++++ .../src/module-runner/env.worker.ts | 7 ++----- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/packages/cloudflare-vite-plugin/src/dev-plugin.ts b/packages/cloudflare-vite-plugin/src/dev-plugin.ts index 4d0a1789..57b12a00 100644 --- a/packages/cloudflare-vite-plugin/src/dev-plugin.ts +++ b/packages/cloudflare-vite-plugin/src/dev-plugin.ts @@ -86,7 +86,7 @@ export function dev(options: CloudflareVitePluginOptions): vite.Plugin { const [input] = inputs; handle ??= await startServer( options, - { id: input, name: input }, + { environmentName: environmentNames[0], entryId: input, entryName: input }, server, options.context ?? context!, ); diff --git a/packages/cloudflare-vite-plugin/src/dev-server.ts b/packages/cloudflare-vite-plugin/src/dev-server.ts index fbf8faf8..c7e1fe3e 100644 --- a/packages/cloudflare-vite-plugin/src/dev-server.ts +++ b/packages/cloudflare-vite-plugin/src/dev-server.ts @@ -21,6 +21,7 @@ import type * as vite from "vite"; import * as ModuleRunnerWorker from "worker:./module-runner/module-runner.worker.ts"; import * as WrapperWorker from "worker:./module-runner/wrapper.worker.ts"; import * as ViteAssets from "./assets/ViteAssets"; +import type { EntryEnvironment } from "./module-runner/constants.shared.ts"; import { ENVIRONMENT_NAME_HEADER } from "./module-runner/constants.shared.ts"; import type { CloudflareVitePluginOptions } from "./plugin"; @@ -28,15 +29,12 @@ export type ServerHandle = Awaited>; export const startServer = async ( options: CloudflareVitePluginOptions, - entry: { - id: string; - name: string; - }, + entryEnvironment: EntryEnvironment, server: vite.ViteDevServer, context: Context.Context, ) => { const scope = Scope.makeUnsafe(); - const address = await serve(options, entry, server).pipe( + const address = await serve(options, entryEnvironment, server).pipe( Effect.provide(ViteAssets.ViteAssetsLive(server)), Effect.provide(context), Scope.provide(scope), @@ -83,10 +81,7 @@ const closeScope = async (scope: Scope.Scope) => { const serve = Effect.fn(function* ( options: CloudflareVitePluginOptions, - entry: { - id: string; - name: string; - }, + entryEnvironment: EntryEnvironment, server: vite.ViteDevServer, ) { const runtime = yield* Runtime; @@ -102,11 +97,7 @@ const serve = Effect.fn(function* ( binding: "__DISTILLED_MODULE_RUNNER__", className: "ModuleRunnerDO", }), - Json.local("__DISTILLED_ENVIRONMENT__", { - environmentName: options.viteEnvironment?.name ?? "ssr", - entryId: entry.id, - entryName: entry.name, - }), + Json.local("__DISTILLED_ENVIRONMENT__", entryEnvironment), Loopback.local({ binding: "__DISTILLED_INVOKE_MODULE__", name: `vite:invoke-module:${name}`, diff --git a/packages/cloudflare-vite-plugin/src/module-runner/constants.shared.ts b/packages/cloudflare-vite-plugin/src/module-runner/constants.shared.ts index a14c5e45..497775c1 100644 --- a/packages/cloudflare-vite-plugin/src/module-runner/constants.shared.ts +++ b/packages/cloudflare-vite-plugin/src/module-runner/constants.shared.ts @@ -1,3 +1,9 @@ export const INIT_PATH = "/__vite_module_runner/init"; export const ENVIRONMENT_NAME_HEADER = "distilled-environment-name"; export const WORKER_ENTRY_PATH_HEADER = "distilled-worker-entry-path"; + +export interface EntryEnvironment { + environmentName: string; + entryId: string; + entryName: string; +} diff --git a/packages/cloudflare-vite-plugin/src/module-runner/env.worker.ts b/packages/cloudflare-vite-plugin/src/module-runner/env.worker.ts index 48fca765..bb84d3c2 100644 --- a/packages/cloudflare-vite-plugin/src/module-runner/env.worker.ts +++ b/packages/cloudflare-vite-plugin/src/module-runner/env.worker.ts @@ -1,3 +1,4 @@ +import type { EntryEnvironment } from "./constants.shared.ts"; import type { ModuleRunnerDO } from "./module-runner.worker.ts"; export interface Env extends Cloudflare.Env { @@ -8,11 +9,7 @@ export interface Env extends Cloudflare.Env { eval: (code: string, id: string) => Function; }; __DISTILLED_INVOKE_MODULE__: Fetcher; - __DISTILLED_ENVIRONMENT__: { - environmentName: string; - entryId: string; - entryName: string; - }; + __DISTILLED_ENVIRONMENT__: EntryEnvironment; [key: string]: unknown; } From b7c2387978cb21c492b2dff0606ebe409461e567 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Wed, 24 Jun 2026 13:03:34 -0400 Subject: [PATCH 05/16] remove options check from hook --- packages/cloudflare-vite-plugin/src/dev-plugin.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/cloudflare-vite-plugin/src/dev-plugin.ts b/packages/cloudflare-vite-plugin/src/dev-plugin.ts index 57b12a00..23c778d1 100644 --- a/packages/cloudflare-vite-plugin/src/dev-plugin.ts +++ b/packages/cloudflare-vite-plugin/src/dev-plugin.ts @@ -30,9 +30,6 @@ export function dev(options: CloudflareVitePluginOptions): vite.Plugin { optionsApi = resolvePluginApi(plugins ?? [], "distilled-cloudflare:options"); }, config() { - if (!optionsApi) { - throw new Error("Cannot resolve the distilled-cloudflare:options plugin"); - } const environment: vite.EnvironmentOptions = { dev: { createEnvironment(name, config) { From 511822ee092dbfddf625f67c0dc8e8af5b5f2727 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:22:51 -0400 Subject: [PATCH 06/16] build result plugin --- .../src/plugins/options.ts | 10 ---- .../src/build-result.ts | 55 +++++++++++++++++++ packages/cloudflare-vite-plugin/src/plugin.ts | 3 + 3 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 packages/cloudflare-vite-plugin/src/build-result.ts diff --git a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts index e77de74d..862a44c3 100644 --- a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts +++ b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts @@ -153,16 +153,6 @@ export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (plu conditions: [...DEFAULT_RESOLVE_CONDITION_NAMES, "development|production"], }, }, - builder: - appType === "spa" - ? undefined - : { - buildApp: async (app) => { - for (const environment of Object.values(app.environments)) { - await app.build(environment); - } - }, - }, environments: { client: { build: { diff --git a/packages/cloudflare-vite-plugin/src/build-result.ts b/packages/cloudflare-vite-plugin/src/build-result.ts new file mode 100644 index 00000000..67e35486 --- /dev/null +++ b/packages/cloudflare-vite-plugin/src/build-result.ts @@ -0,0 +1,55 @@ +import { WORKER_ENTRY_PREFIX } from "@distilled.cloud/cloudflare-rolldown-plugin/plugins"; +import type * as vite from "vite"; +import type { CloudflareVitePluginOptions } from "./plugin"; + +export interface BuildResult { + client?: { dir: string }; + server?: { + entry: string; + modules: Map; + }; +} + +export const builderPlugin = (options: CloudflareVitePluginOptions): vite.Plugin => { + return { + name: "distilled-cloudflare:build-result", + buildApp: { + order: "pre", + handler: async (builder) => { + let clientDir: string | undefined; + let serverEntry: string | undefined; + const serverModules = new Map< + string, + vite.Rolldown.OutputChunk | vite.Rolldown.OutputAsset + >(); + for (const environment of Object.values(builder.environments)) { + const result = await builder.build(environment); + if (environment.name === "client") { + clientDir = environment.config.build.outDir; + continue; + } + if (!isRolldownOutput(result)) { + throw new Error("Build result is not a RolldownOutput"); + } + const chunk = result.output[0]; + if (chunk.facadeModuleId && chunk.facadeModuleId.startsWith(WORKER_ENTRY_PREFIX)) { + serverEntry = chunk.fileName; + } + for (const chunk of result.output) { + serverModules.set(chunk.fileName, chunk); + } + } + options.onBuildComplete?.({ + client: clientDir ? { dir: clientDir } : undefined, + server: serverEntry ? { entry: serverEntry, modules: serverModules } : undefined, + }); + }, + }, + }; +}; + +const isRolldownOutput = ( + result: Awaited>, +): result is vite.Rolldown.RolldownOutput => { + return "output" in result && result.output.length > 0; +}; diff --git a/packages/cloudflare-vite-plugin/src/plugin.ts b/packages/cloudflare-vite-plugin/src/plugin.ts index 27e709cb..a739e74f 100644 --- a/packages/cloudflare-vite-plugin/src/plugin.ts +++ b/packages/cloudflare-vite-plugin/src/plugin.ts @@ -16,6 +16,7 @@ import type { } from "@distilled.cloud/cloudflare-runtime"; import type * as Context from "effect/Context"; import type * as vite from "vite"; +import { builderPlugin, type BuildResult } from "./build-result.js"; import { dev } from "./dev-plugin.js"; export interface CloudflareVitePluginOptions< @@ -23,6 +24,7 @@ export interface CloudflareVitePluginOptions< > extends BasePluginOptions { worker?: Omit, "compatibilityDate" | "compatibilityFlags" | "modules">; context?: Context.Context; + onBuildComplete?: (options: BuildResult) => void; } export default function cloudflareVitePlugin( @@ -45,5 +47,6 @@ export default function cloudflareVitePlugin( }, } as vite.Plugin, dev(options), + builderPlugin(options), ]; } From a2b1a213f5a98173eaf49da678db3270fe186be6 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Wed, 24 Jun 2026 14:38:06 -0400 Subject: [PATCH 07/16] fix --- .../src/build-result.ts | 32 +++++++++++-------- packages/cloudflare-vite-plugin/src/plugin.ts | 2 ++ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/cloudflare-vite-plugin/src/build-result.ts b/packages/cloudflare-vite-plugin/src/build-result.ts index 67e35486..120b16ed 100644 --- a/packages/cloudflare-vite-plugin/src/build-result.ts +++ b/packages/cloudflare-vite-plugin/src/build-result.ts @@ -3,11 +3,11 @@ import type * as vite from "vite"; import type { CloudflareVitePluginOptions } from "./plugin"; export interface BuildResult { - client?: { dir: string }; - server?: { - entry: string; - modules: Map; - }; + assetsDir?: string; + server?: [ + vite.Rolldown.OutputChunk, + ...Array, + ]; } export const builderPlugin = (options: CloudflareVitePluginOptions): vite.Plugin => { @@ -16,8 +16,8 @@ export const builderPlugin = (options: CloudflareVitePluginOptions): vite.Plugin buildApp: { order: "pre", handler: async (builder) => { - let clientDir: string | undefined; - let serverEntry: string | undefined; + let assetsDir: string | undefined; + let serverEntry: vite.Rolldown.OutputChunk | undefined; const serverModules = new Map< string, vite.Rolldown.OutputChunk | vite.Rolldown.OutputAsset @@ -25,7 +25,7 @@ export const builderPlugin = (options: CloudflareVitePluginOptions): vite.Plugin for (const environment of Object.values(builder.environments)) { const result = await builder.build(environment); if (environment.name === "client") { - clientDir = environment.config.build.outDir; + assetsDir = environment.config.build.outDir; continue; } if (!isRolldownOutput(result)) { @@ -33,15 +33,19 @@ export const builderPlugin = (options: CloudflareVitePluginOptions): vite.Plugin } const chunk = result.output[0]; if (chunk.facadeModuleId && chunk.facadeModuleId.startsWith(WORKER_ENTRY_PREFIX)) { - serverEntry = chunk.fileName; - } - for (const chunk of result.output) { - serverModules.set(chunk.fileName, chunk); + if (serverEntry) { + throw new Error("Multiple server entries found"); + } + serverEntry = chunk; + } else { + for (const chunk of result.output) { + serverModules.set(chunk.fileName, chunk); + } } } options.onBuildComplete?.({ - client: clientDir ? { dir: clientDir } : undefined, - server: serverEntry ? { entry: serverEntry, modules: serverModules } : undefined, + assetsDir, + server: serverEntry ? [serverEntry, ...serverModules.values()] : undefined, }); }, }, diff --git a/packages/cloudflare-vite-plugin/src/plugin.ts b/packages/cloudflare-vite-plugin/src/plugin.ts index a739e74f..36dd751e 100644 --- a/packages/cloudflare-vite-plugin/src/plugin.ts +++ b/packages/cloudflare-vite-plugin/src/plugin.ts @@ -27,6 +27,8 @@ export interface CloudflareVitePluginOptions< onBuildComplete?: (options: BuildResult) => void; } +export type { BuildResult }; + export default function cloudflareVitePlugin( options: CloudflareVitePluginOptions = {}, ): Array { From 14da12479967e4efdfc7725083f81a0b69a49477 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:07:14 -0400 Subject: [PATCH 08/16] opt --- .../src/plugins/options.ts | 19 ++++++++--------- .../src/build-result.ts | 21 +++++++++++++++---- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts index 862a44c3..f31512a9 100644 --- a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts +++ b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts @@ -59,6 +59,14 @@ export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (plu input = normalizeInput( pluginOptions.main ?? defaultEnvironmentEntries(environmentNames[0], userConfig) ?? {}, ); + const rollupOptions: vite.Rollup.RollupOptions = { + input: wrapInput(input), + preserveEntrySignatures: "strict", + }; + const define = getDefine( + pluginOptions, + process.env.NODE_ENV || userConfig.mode || "production", + ); const makeEnvironment = ({ name, isEntry, @@ -136,17 +144,8 @@ export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (plu : {}), }; }; - const rollupOptions: vite.Rollup.RollupOptions = { - input: wrapInput(input), - preserveEntrySignatures: "strict", - }; - const define = getDefine( - pluginOptions, - process.env.NODE_ENV || userConfig.mode || "production", - ); - const appType = userConfig.appType ?? (Object.keys(input).length === 0 ? "spa" : "custom"); return { - appType, + appType: userConfig.appType ?? (Object.keys(input).length === 0 ? "spa" : "custom"), ssr: { noExternal: true, resolve: { diff --git a/packages/cloudflare-vite-plugin/src/build-result.ts b/packages/cloudflare-vite-plugin/src/build-result.ts index 120b16ed..707acd42 100644 --- a/packages/cloudflare-vite-plugin/src/build-result.ts +++ b/packages/cloudflare-vite-plugin/src/build-result.ts @@ -17,7 +17,7 @@ export const builderPlugin = (options: CloudflareVitePluginOptions): vite.Plugin order: "pre", handler: async (builder) => { let assetsDir: string | undefined; - let serverEntry: vite.Rolldown.OutputChunk | undefined; + let server: BuildResult["server"]; const serverModules = new Map< string, vite.Rolldown.OutputChunk | vite.Rolldown.OutputAsset @@ -33,19 +33,32 @@ export const builderPlugin = (options: CloudflareVitePluginOptions): vite.Plugin } const chunk = result.output[0]; if (chunk.facadeModuleId && chunk.facadeModuleId.startsWith(WORKER_ENTRY_PREFIX)) { - if (serverEntry) { + if (server) { throw new Error("Multiple server entries found"); } - serverEntry = chunk; + server = [chunk]; } else { for (const chunk of result.output) { serverModules.set(chunk.fileName, chunk); } } } + const keys = Array.from(serverModules.keys()).sort((a, b) => a.localeCompare(b)); + if (keys.length > 0) { + if (!server) { + throw new Error("Server entry not found"); + } + for (const key of keys) { + const chunk = serverModules.get(key); + if (!chunk) { + throw new Error(`Chunk ${key} not found`); + } + server.push(chunk); + } + } options.onBuildComplete?.({ assetsDir, - server: serverEntry ? [serverEntry, ...serverModules.values()] : undefined, + server, }); }, }, From fee17fcf74a32e33baafa0111b88270447c56fa4 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:26:04 -0400 Subject: [PATCH 09/16] remove order: pre from builder plugin --- .../src/build-result.ts | 83 +++++++++---------- 1 file changed, 40 insertions(+), 43 deletions(-) diff --git a/packages/cloudflare-vite-plugin/src/build-result.ts b/packages/cloudflare-vite-plugin/src/build-result.ts index 707acd42..f10f0d62 100644 --- a/packages/cloudflare-vite-plugin/src/build-result.ts +++ b/packages/cloudflare-vite-plugin/src/build-result.ts @@ -13,54 +13,51 @@ export interface BuildResult { export const builderPlugin = (options: CloudflareVitePluginOptions): vite.Plugin => { return { name: "distilled-cloudflare:build-result", - buildApp: { - order: "pre", - handler: async (builder) => { - let assetsDir: string | undefined; - let server: BuildResult["server"]; - const serverModules = new Map< - string, - vite.Rolldown.OutputChunk | vite.Rolldown.OutputAsset - >(); - for (const environment of Object.values(builder.environments)) { - const result = await builder.build(environment); - if (environment.name === "client") { - assetsDir = environment.config.build.outDir; - continue; - } - if (!isRolldownOutput(result)) { - throw new Error("Build result is not a RolldownOutput"); + async buildApp(builder) { + let assetsDir: string | undefined; + let server: BuildResult["server"]; + const serverModules = new Map< + string, + vite.Rolldown.OutputChunk | vite.Rolldown.OutputAsset + >(); + for (const environment of Object.values(builder.environments)) { + const result = await builder.build(environment); + if (environment.name === "client") { + assetsDir = environment.config.build.outDir; + continue; + } + if (!isRolldownOutput(result)) { + throw new Error("Build result is not a RolldownOutput"); + } + const chunk = result.output[0]; + if (chunk.facadeModuleId && chunk.facadeModuleId.startsWith(WORKER_ENTRY_PREFIX)) { + if (server) { + throw new Error("Multiple server entries found"); } - const chunk = result.output[0]; - if (chunk.facadeModuleId && chunk.facadeModuleId.startsWith(WORKER_ENTRY_PREFIX)) { - if (server) { - throw new Error("Multiple server entries found"); - } - server = [chunk]; - } else { - for (const chunk of result.output) { - serverModules.set(chunk.fileName, chunk); - } + server = [chunk]; + } else { + for (const chunk of result.output) { + serverModules.set(chunk.fileName, chunk); } } - const keys = Array.from(serverModules.keys()).sort((a, b) => a.localeCompare(b)); - if (keys.length > 0) { - if (!server) { - throw new Error("Server entry not found"); - } - for (const key of keys) { - const chunk = serverModules.get(key); - if (!chunk) { - throw new Error(`Chunk ${key} not found`); - } - server.push(chunk); + } + const keys = Array.from(serverModules.keys()).sort((a, b) => a.localeCompare(b)); + if (keys.length > 0) { + if (!server) { + throw new Error("Server entry not found"); + } + for (const key of keys) { + const chunk = serverModules.get(key); + if (!chunk) { + throw new Error(`Chunk ${key} not found`); } + server.push(chunk); } - options.onBuildComplete?.({ - assetsDir, - server, - }); - }, + } + options.onBuildComplete?.({ + assetsDir, + server, + }); }, }; }; From 121dd0f7f489e6793248520cad35025bad532847 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:32:34 -0400 Subject: [PATCH 10/16] fix(vite-plugin): router inner entrypoint missing --- packages/cloudflare-vite-plugin/src/assets/ViteAssets.ts | 6 +++++- packages/cloudflare-vite-plugin/src/assets/router.worker.ts | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/cloudflare-vite-plugin/src/assets/ViteAssets.ts b/packages/cloudflare-vite-plugin/src/assets/ViteAssets.ts index 6d00f6d7..83dbb1c1 100644 --- a/packages/cloudflare-vite-plugin/src/assets/ViteAssets.ts +++ b/packages/cloudflare-vite-plugin/src/assets/ViteAssets.ts @@ -87,7 +87,11 @@ export const ViteAssetsLive = (viteDevServer: vite.ViteDevServer) => name: "assets:router", worker: { compatibilityDate: "2024-07-31", - compatibilityFlags: ["nodejs_compat", "no_nodejs_compat_v2"], + compatibilityFlags: [ + "nodejs_compat", + "no_nodejs_compat_v2", + "enable_ctx_exports", + ], bindings: [ { name: "ASSET_WORKER", diff --git a/packages/cloudflare-vite-plugin/src/assets/router.worker.ts b/packages/cloudflare-vite-plugin/src/assets/router.worker.ts index f1f1df4b..9ee126ea 100644 --- a/packages/cloudflare-vite-plugin/src/assets/router.worker.ts +++ b/packages/cloudflare-vite-plugin/src/assets/router.worker.ts @@ -1 +1,4 @@ -export { default } from "@distilled.cloud/vendor-workers-shared/workers/router-worker"; +export { + default, + RouterInnerEntrypoint, +} from "@distilled.cloud/vendor-workers-shared/workers/router-worker"; From b946e0604d8b129f039bb61d7dec226796c0ede5 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Wed, 24 Jun 2026 17:07:45 -0400 Subject: [PATCH 11/16] test fixtures --- bun.lock | 96 +++++++++-- fixtures/react-router-rsc/README.md | 34 ++++ fixtures/react-router-rsc/app/paper.css | 150 ++++++++++++++++++ fixtures/react-router-rsc/app/root.tsx | 55 +++++++ fixtures/react-router-rsc/app/routes.ts | 21 +++ .../react-router-rsc/app/routes/about.tsx | 20 +++ .../react-router-rsc/app/routes/client.tsx | 16 ++ .../app/routes/home.actions.ts | 7 + .../app/routes/home.client.tsx | 12 ++ fixtures/react-router-rsc/app/routes/home.css | 3 + fixtures/react-router-rsc/app/routes/home.tsx | 34 ++++ .../app/routes/root.client.tsx | 44 +++++ .../app/routes/test-action-state/client.tsx | 16 ++ .../app/routes/test-action-state/server.tsx | 20 +++ fixtures/react-router-rsc/app/styles.css | 32 ++++ fixtures/react-router-rsc/package.json | 30 ++++ fixtures/react-router-rsc/public/favicon.ico | Bin 0 -> 15086 bytes .../react-router-vite/entry.browser.tsx | 49 ++++++ .../react-router-vite/entry.rsc.single.tsx | 8 + .../react-router-vite/entry.rsc.tsx | 36 +++++ .../react-router-vite/entry.ssr.tsx | 29 ++++ .../react-router-vite/entry.worker.tsx | 24 +++ .../react-router-vite/types.d.ts | 2 + .../react-router-vite/worker-ssr.tsx | 13 ++ fixtures/react-router-rsc/test/build.test.ts | 143 +++++++++++++++++ fixtures/react-router-rsc/test/dev.test.ts | 57 +++++++ fixtures/react-router-rsc/tsconfig.json | 16 ++ fixtures/react-router-rsc/vite.config.ts | 60 +++++++ fixtures/react-rsc/README.md | 40 +++++ fixtures/react-rsc/package.json | 28 ++++ fixtures/react-rsc/public/vite.svg | 1 + fixtures/react-rsc/src/action.tsx | 11 ++ fixtures/react-rsc/src/assets/react.svg | 1 + fixtures/react-rsc/src/client.tsx | 9 ++ .../react-rsc/src/framework/entry.browser.tsx | 138 ++++++++++++++++ .../react-rsc/src/framework/entry.rsc.tsx | 121 ++++++++++++++ .../react-rsc/src/framework/entry.ssr.tsx | 70 ++++++++ .../src/framework/error-boundary.tsx | 75 +++++++++ fixtures/react-rsc/src/framework/request.tsx | 58 +++++++ fixtures/react-rsc/src/index.css | 112 +++++++++++++ fixtures/react-rsc/src/root.tsx | 70 ++++++++ fixtures/react-rsc/test/dev.test.ts | 46 ++++++ fixtures/react-rsc/test/plugin-order.test.ts | 47 ++++++ fixtures/react-rsc/tsconfig.json | 17 ++ fixtures/react-rsc/vite.config.ts | 39 +++++ 45 files changed, 1896 insertions(+), 14 deletions(-) create mode 100644 fixtures/react-router-rsc/README.md create mode 100644 fixtures/react-router-rsc/app/paper.css create mode 100644 fixtures/react-router-rsc/app/root.tsx create mode 100644 fixtures/react-router-rsc/app/routes.ts create mode 100644 fixtures/react-router-rsc/app/routes/about.tsx create mode 100644 fixtures/react-router-rsc/app/routes/client.tsx create mode 100644 fixtures/react-router-rsc/app/routes/home.actions.ts create mode 100644 fixtures/react-router-rsc/app/routes/home.client.tsx create mode 100644 fixtures/react-router-rsc/app/routes/home.css create mode 100644 fixtures/react-router-rsc/app/routes/home.tsx create mode 100644 fixtures/react-router-rsc/app/routes/root.client.tsx create mode 100644 fixtures/react-router-rsc/app/routes/test-action-state/client.tsx create mode 100644 fixtures/react-router-rsc/app/routes/test-action-state/server.tsx create mode 100644 fixtures/react-router-rsc/app/styles.css create mode 100644 fixtures/react-router-rsc/package.json create mode 100644 fixtures/react-router-rsc/public/favicon.ico create mode 100644 fixtures/react-router-rsc/react-router-vite/entry.browser.tsx create mode 100644 fixtures/react-router-rsc/react-router-vite/entry.rsc.single.tsx create mode 100644 fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx create mode 100644 fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx create mode 100644 fixtures/react-router-rsc/react-router-vite/entry.worker.tsx create mode 100644 fixtures/react-router-rsc/react-router-vite/types.d.ts create mode 100644 fixtures/react-router-rsc/react-router-vite/worker-ssr.tsx create mode 100644 fixtures/react-router-rsc/test/build.test.ts create mode 100644 fixtures/react-router-rsc/test/dev.test.ts create mode 100644 fixtures/react-router-rsc/tsconfig.json create mode 100644 fixtures/react-router-rsc/vite.config.ts create mode 100644 fixtures/react-rsc/README.md create mode 100644 fixtures/react-rsc/package.json create mode 100644 fixtures/react-rsc/public/vite.svg create mode 100644 fixtures/react-rsc/src/action.tsx create mode 100644 fixtures/react-rsc/src/assets/react.svg create mode 100644 fixtures/react-rsc/src/client.tsx create mode 100644 fixtures/react-rsc/src/framework/entry.browser.tsx create mode 100644 fixtures/react-rsc/src/framework/entry.rsc.tsx create mode 100644 fixtures/react-rsc/src/framework/entry.ssr.tsx create mode 100644 fixtures/react-rsc/src/framework/error-boundary.tsx create mode 100644 fixtures/react-rsc/src/framework/request.tsx create mode 100644 fixtures/react-rsc/src/index.css create mode 100644 fixtures/react-rsc/src/root.tsx create mode 100644 fixtures/react-rsc/test/dev.test.ts create mode 100644 fixtures/react-rsc/test/plugin-order.test.ts create mode 100644 fixtures/react-rsc/tsconfig.json create mode 100644 fixtures/react-rsc/vite.config.ts diff --git a/bun.lock b/bun.lock index 7a003042..d94e35c2 100644 --- a/bun.lock +++ b/bun.lock @@ -15,6 +15,46 @@ "typescript": "catalog:", }, }, + "fixtures/react-router-rsc": { + "name": "@fixtures/react-router-rsc", + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router": "7.16.0", + }, + "devDependencies": { + "@cloudflare/workers-types": "catalog:workers", + "@distilled.cloud/cloudflare-runtime": "workspace:*", + "@distilled.cloud/cloudflare-vite-plugin": "workspace:*", + "@tailwindcss/typography": "^0.5.19", + "@tailwindcss/vite": "^4.3.0", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "latest", + "@vitejs/plugin-rsc": "latest", + "tailwindcss": "^4.3.0", + "vite": "catalog:", + }, + }, + "fixtures/react-rsc": { + "name": "@fixtures/react-rsc", + "version": "0.0.0", + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7", + }, + "devDependencies": { + "@cloudflare/workers-types": "catalog:workers", + "@distilled.cloud/cloudflare-runtime": "workspace:*", + "@distilled.cloud/cloudflare-vite-plugin": "workspace:*", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "latest", + "@vitejs/plugin-rsc": "latest", + "rsc-html-stream": "^0.0.7", + "vite": "catalog:", + }, + }, "fixtures/solid-ssr": { "name": "@fixtures/solid-ssr", "version": "0.0.0", @@ -427,6 +467,10 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + "@fixtures/react-router-rsc": ["@fixtures/react-router-rsc@workspace:fixtures/react-router-rsc"], + + "@fixtures/react-rsc": ["@fixtures/react-rsc@workspace:fixtures/react-rsc"], + "@fixtures/solid-ssr": ["@fixtures/solid-ssr@workspace:fixtures/solid-ssr"], "@fixtures/solidstart": ["@fixtures/solidstart@workspace:fixtures/solidstart"], @@ -997,7 +1041,7 @@ "@types/node": ["@types/node@24.12.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA=="], - "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + "@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], @@ -1011,7 +1055,9 @@ "@vercel/nft": ["@vercel/nft@1.5.0", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^13.0.0", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-IWTDeIoWhQ7ZtRO/JRKH+jhmeQvZYhtGPmzw/QGDY+wDCQqfm25P9yIdoAFagu4fWsK4IwZXDFIjrmp5rRm/sA=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.2", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.0" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg=="], + + "@vitejs/plugin-rsc": ["@vitejs/plugin-rsc@0.5.27", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1", "es-module-lexer": "^2.1.0", "estree-walker": "^3.0.3", "magic-string": "^0.30.21", "srvx": "^0.11.15", "strip-literal": "^3.1.0", "turbo-stream": "^3.2.0", "vitefu": "^1.1.3" }, "peerDependencies": { "react": "*", "react-dom": "*", "react-server-dom-webpack": "*", "vite": "*" }, "optionalPeers": ["react-server-dom-webpack"] }, "sha512-s1fd5DUkPXk86DDHPM/kP93WrvI0MoA8klxdDZmD1fMSaA9xujfgunsm8ZoUH0FemR+63vNalFsIDR0AJH4ktg=="], "@vitest/expect": ["@vitest/expect@4.1.6", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.6", "@vitest/utils": "4.1.6", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg=="], @@ -1411,7 +1457,7 @@ "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], @@ -1655,12 +1701,14 @@ "rc9": ["rc9@3.0.1", "", { "dependencies": { "defu": "^6.1.6", "destr": "^2.0.5" } }, "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ=="], - "react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], + "react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], - "react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], + "react-dom": ["react-dom@19.2.7", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.7" } }, "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ=="], "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], + "react-router": ["react-router@7.16.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-wArC8lVyJb3+jM9OpDyW6hLCizACWkvQR/sSGqSs+o5uEXEtGlqdZ4v8hENR3Jad6i+LRkK93q/+bQAcvl6V1A=="], + "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], "readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "^5.1.0" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="], @@ -1695,6 +1743,8 @@ "rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="], + "rsc-html-stream": ["rsc-html-stream@0.0.7", "", {}, "sha512-v9+fuY7usTgvXdNl8JmfXCvSsQbq2YMd60kOeeMIqCJFZ69fViuIxztHei7v5mlMMa2h3SqS+v44Gu9i9xANZA=="], + "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -1721,6 +1771,8 @@ "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], @@ -1761,7 +1813,7 @@ "sql-escaper": ["sql-escaper@1.3.3", "", {}, "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw=="], - "srvx": ["srvx@0.9.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-RZaxTKJEE/14HYn8COLuUOJAt0U55N9l1Xf6jj+T0GoA01EUH1Xz5JtSUOI+EHn+AEgPCVn7gk6jHJffrr06fQ=="], + "srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], @@ -1845,6 +1897,8 @@ "turbo": ["turbo@2.9.16", "", { "optionalDependencies": { "@turbo/darwin-64": "2.9.16", "@turbo/darwin-arm64": "2.9.16", "@turbo/linux-64": "2.9.16", "@turbo/linux-arm64": "2.9.16", "@turbo/windows-64": "2.9.16", "@turbo/windows-arm64": "2.9.16" }, "bin": { "turbo": "bin/turbo" } }, "sha512-NqgRQy6j6dPYcdSdv0q1g9QsZg7SWg87RERM8otw/1AtKU2yTFVClOM7cbwKzOonZr/Ek1blTBucw64L9H0Bwg=="], + "turbo-stream": ["turbo-stream@3.2.0", "", {}, "sha512-EK+bZ9UVrVh7JLslVFOV0GEMsociOqVOvEMTAd4ixMyffN5YNIEdLZWXUx5PJqDbTxSIBWw04HS9gCY4frYQDQ=="], + "type-fest": ["type-fest@5.6.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA=="], "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], @@ -1961,6 +2015,8 @@ "@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "@babel/core/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], "@babel/core/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], @@ -2005,6 +2061,14 @@ "@fixtures/tanstack-start/@types/node": ["@types/node@22.19.19", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew=="], + "@fixtures/tanstack-start/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + + "@fixtures/tanstack-start/@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], + + "@fixtures/tanstack-start/react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], + + "@fixtures/tanstack-start/react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], @@ -2027,6 +2091,8 @@ "@solidjs/start/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "@solidjs/start/srvx": ["srvx@0.9.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-RZaxTKJEE/14HYn8COLuUOJAt0U55N9l1Xf6jj+T0GoA01EUH1Xz5JtSUOI+EHn+AEgPCVn7gk6jHJffrr06fQ=="], + "@solidjs/start/vite": ["vite@7.3.3", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA=="], "@solidjs/vite-plugin-nitro-2/vite": ["vite@7.3.3", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA=="], @@ -2063,8 +2129,6 @@ "@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - "@tanstack/start-plugin-core/srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], - "@tanstack/start-plugin-core/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "@types/babel__core/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], @@ -2073,8 +2137,6 @@ "@vercel/nft/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], - "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "archiver-utils/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], @@ -2085,9 +2147,9 @@ "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="], - "h3-v2/rou3": ["rou3@0.8.1", "", {}, "sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA=="], + "h3/srvx": ["srvx@0.9.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-RZaxTKJEE/14HYn8COLuUOJAt0U55N9l1Xf6jj+T0GoA01EUH1Xz5JtSUOI+EHn+AEgPCVn7gk6jHJffrr06fQ=="], - "h3-v2/srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], + "h3-v2/rou3": ["rou3@0.8.1", "", {}, "sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA=="], "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], @@ -2133,8 +2195,6 @@ "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], - "tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "unctx/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], @@ -2247,6 +2307,8 @@ "@fixtures/tanstack-start/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "@fixtures/tanstack-start/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "@solidjs/start/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], @@ -2305,6 +2367,8 @@ "@tanstack/directive-functions-plugin/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@tanstack/directive-functions-plugin/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "@tanstack/directive-functions-plugin/@tanstack/router-utils/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], "@tanstack/directive-functions-plugin/@tanstack/router-utils/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], @@ -2313,8 +2377,12 @@ "@tanstack/server-functions-plugin/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@tanstack/server-functions-plugin/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "@tanstack/start-plugin-core/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@tanstack/start-plugin-core/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "archiver-utils/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], "archiver-utils/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], diff --git a/fixtures/react-router-rsc/README.md b/fixtures/react-router-rsc/README.md new file mode 100644 index 00000000..27e7f9c7 --- /dev/null +++ b/fixtures/react-router-rsc/README.md @@ -0,0 +1,34 @@ +# rsc react-router + +https://vite-rsc-react-router.hiro18181.workers.dev + +> [!NOTE] +> React Router now provides [official RSC support](https://reactrouter.com/how-to/react-server-components) for Vite. The example might not be kept up to date with the latest version. Please refer to React Router's official documentation for the latest integrations. + +Vite RSC example based on demo made by React router team with Parcel: + +- https://github.com/jacob-ebey/parcel-plugin-react-router/ +- https://github.com/jacob-ebey/experimental-parcel-react-router-starter +- https://github.com/remix-run/react-router/tree/rsc/playground/rsc-vite + +See also [`rsc-movies`](https://github.com/hi-ogawa/rsc-movies/). + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vitejs/vite-plugin-react/tree/main/packages/plugin-rsc/examples/react-router?file=src%2Froutes%2Froot.tsx) + +Or try it locally by: + +```sh +npx giget gh:vitejs/vite-plugin-react/packages/plugin-rsc/examples/react-router my-app +cd my-app +npm i +npm run dev +npm run build +npm run preview + +# run on @cloudflare/vite-plugin and deploy. +# a separate configuration is found in ./cf/vite.config.ts +npm run cf-dev +npm run cf-build +npm run cf-preview +npm run cf-release +``` diff --git a/fixtures/react-router-rsc/app/paper.css b/fixtures/react-router-rsc/app/paper.css new file mode 100644 index 00000000..84d3e872 --- /dev/null +++ b/fixtures/react-router-rsc/app/paper.css @@ -0,0 +1,150 @@ +@theme { + --default-font-family: "Patrick Hand SC", sans-serif; + --default-mono-font-family: "Patrick Hand SC", sans-serif; + + --color-foreground: black; + --color-danger: rgb(167, 52, 45); + --color-secondary: rgb(11, 116, 213); + --color-success: rgb(134, 163, 97); + --color-warning: rgb(221, 205, 69); + --color-border: #cdcccb; + --color-border-active: rgba(0, 0, 0, 0.2); + + --color-paper-background: white; + --color-paper-border: #cdcccb; + --shadow-paper: -1px 5px 35px -9px rgba(0, 0, 0, 0.2); + + --shadow-btn: 15px 28px 25px -18px rgba(0, 0, 0, 0.2); + --shadow-btn-hover: 2px 8px 8px -5px rgba(0, 0, 0, 0.3); + --color-btn-border: black; + --btn-color-danger: var(--color-danger); + --btn-color-secondary: var(--color-secondary); + --btn-color-success: var(--color-success); + --btn-color-warning: var(--color-warning); +} + +@utility paper-border { + @apply border-2 border-border; + border-bottom-left-radius: 25px 115px; + border-bottom-right-radius: 155px 25px; + border-top-left-radius: 15px 225px; + border-top-right-radius: 25px 150px; +} + +@utility no-paper-border { + @apply border-0; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +@utility paper-underline { + @apply border-b-3 border-[currentcolor]; + border-bottom-left-radius: 15px 3px; + border-bottom-right-radius: 15px 5px; + border-bottom-style: solid; +} + +@utility paper-underline-hover { + @apply paper-underline border-transparent; + @variant hover { + @apply border-[currentcolor]; + } +} + +@utility paper { + @apply border border-paper-border bg-paper-background p-8 shadow-paper; +} + +@utility breadcrumbs { + @apply flex flex-wrap gap-2; + & > * { + @apply inline-block after:text-lg after:content-[""] not-last:after:ml-2 not-last:after:text-foreground not-last:after:content-["/"]; + } + & > a { + @apply text-secondary; + } +} + +@utility btn { + @apply inline-block cursor-pointer bg-paper-background paper-border px-4 py-2 text-lg shadow-btn transition-[shadow_transition]; + + @variant active { + @apply border-border-active; + } + @variant hover { + @apply translate-y-1 shadow-btn-hover; + } + + &.btn-icon { + @apply aspect-square px-2 py-2; + & img, + & svg { + @apply h-7 w-7; + } + } +} + +@utility btn-* { + border-color: --value(--btn-color-*); + color: --value(--btn-color-*); +} + +@utility btn-sm { + @apply px-2 py-1 text-base; +} + +@utility btn-lg { + @apply px-6 py-3 text-2xl; +} + +@utility label { + @apply mb-1 block font-semibold; +} + +@utility input { + @apply paper-border px-3 py-2; + + @variant disabled { + @apply border-border-active; + } +} + +@utility checkbox { + @apply h-6 w-6 paper-border; + + @variant disabled { + @apply border-border-active; + } +} + +@utility select { + @apply paper-border px-3 py-2; + + @variant disabled { + @apply border-border-active; + } +} + +@layer base { + body { + @apply text-foreground; + } + + * { + @apply outline-secondary; + } +} + +@layer utilities { + .prose { + :where(u):not(:where([class~="not-prose"], [class~="not-prose"] *)) { + @apply paper-underline no-underline; + } + + :where(a):not(:where([class~="not-prose"], [class~="not-prose"] *)) { + @apply paper-underline-hover no-underline text-secondary; + } + } +} diff --git a/fixtures/react-router-rsc/app/root.tsx b/fixtures/react-router-rsc/app/root.tsx new file mode 100644 index 00000000..678b3a48 --- /dev/null +++ b/fixtures/react-router-rsc/app/root.tsx @@ -0,0 +1,55 @@ +// oxlint-disable-next-line import/no-unassigned-import +import "./styles.css"; +import { Link, Outlet } from "react-router"; +import { TestClientState, TestHydrated } from "./routes/client"; +import { DumpError, GlobalNavigationLoadingBar } from "./routes/root.client"; + +export function Layout({ children }: { children: React.ReactNode }) { + console.log("[debug] root - Layout"); + return ( + + + + + React Router Vite + + +
+ +
+ + {children} + + + ); +} + +export default function Component() { + console.log("[debug] root - Component"); + return ( + <> + + + ); +} + +export function ErrorBoundary() { + return ; +} diff --git a/fixtures/react-router-rsc/app/routes.ts b/fixtures/react-router-rsc/app/routes.ts new file mode 100644 index 00000000..5a2907fb --- /dev/null +++ b/fixtures/react-router-rsc/app/routes.ts @@ -0,0 +1,21 @@ +import type { unstable_RSCRouteConfigEntry } from "react-router"; + +export const routes: Array = [ + { + id: "root", + path: "", + lazy: () => import("./root"), + children: [ + { + id: "home", + index: true, + lazy: () => import("./routes/home"), + }, + { + id: "about", + path: "about", + lazy: () => import("./routes/about"), + }, + ], + }, +]; diff --git a/fixtures/react-router-rsc/app/routes/about.tsx b/fixtures/react-router-rsc/app/routes/about.tsx new file mode 100644 index 00000000..583f190f --- /dev/null +++ b/fixtures/react-router-rsc/app/routes/about.tsx @@ -0,0 +1,20 @@ +"use client"; + +import React from "react"; + +export function Component() { + const [count, setCount] = React.useState(0); + + return ( +
+
+

About

+

This is the about page.

+

[test-style-home]

+ +
+
+ ); +} diff --git a/fixtures/react-router-rsc/app/routes/client.tsx b/fixtures/react-router-rsc/app/routes/client.tsx new file mode 100644 index 00000000..8c23e84c --- /dev/null +++ b/fixtures/react-router-rsc/app/routes/client.tsx @@ -0,0 +1,16 @@ +"use client"; + +import React from "react"; + +export function TestHydrated() { + const hydrated = React.useSyncExternalStore( + React.useCallback(() => () => {}, []), + () => true, + () => false, + ); + return [hydrated: {hydrated ? 1 : 0}]; +} + +export function TestClientState() { + return ; +} diff --git a/fixtures/react-router-rsc/app/routes/home.actions.ts b/fixtures/react-router-rsc/app/routes/home.actions.ts new file mode 100644 index 00000000..ece1e13a --- /dev/null +++ b/fixtures/react-router-rsc/app/routes/home.actions.ts @@ -0,0 +1,7 @@ +"use server"; + +export async function sayHello(defaultName: string, formData: FormData) { + await new Promise((resolve) => setTimeout(resolve, 500)); + const name = formData.get("name") || defaultName; + console.log(`[debug] sayHello - ${name}`); +} diff --git a/fixtures/react-router-rsc/app/routes/home.client.tsx b/fixtures/react-router-rsc/app/routes/home.client.tsx new file mode 100644 index 00000000..8f2c4fad --- /dev/null +++ b/fixtures/react-router-rsc/app/routes/home.client.tsx @@ -0,0 +1,12 @@ +"use client"; + +import { useFormStatus } from "react-dom"; + +export function PendingButton() { + const status = useFormStatus(); + return ( + + ); +} diff --git a/fixtures/react-router-rsc/app/routes/home.css b/fixtures/react-router-rsc/app/routes/home.css new file mode 100644 index 00000000..7204e2fd --- /dev/null +++ b/fixtures/react-router-rsc/app/routes/home.css @@ -0,0 +1,3 @@ +.test-style-home { + color: rgb(250, 150, 0); +} diff --git a/fixtures/react-router-rsc/app/routes/home.tsx b/fixtures/react-router-rsc/app/routes/home.tsx new file mode 100644 index 00000000..f82ecd77 --- /dev/null +++ b/fixtures/react-router-rsc/app/routes/home.tsx @@ -0,0 +1,34 @@ +import { sayHello } from "./home.actions.ts"; +import { PendingButton } from "./home.client.tsx"; +// oxlint-disable-next-line import/no-unassigned-import +import "./home.css"; +import { TestActionStateServer } from "./test-action-state/server.tsx"; + +const Component = () => { + return ( +
+
+

Home

+

This is the home page.

+ [test-style-home] +

Server Action

+
+
+ + +
+
+ +
+
+
+ +
+
+
+ ); +}; + +export default Component; diff --git a/fixtures/react-router-rsc/app/routes/root.client.tsx b/fixtures/react-router-rsc/app/routes/root.client.tsx new file mode 100644 index 00000000..8729caa0 --- /dev/null +++ b/fixtures/react-router-rsc/app/routes/root.client.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { useNavigation, useRouteError } from "react-router"; + +export function GlobalNavigationLoadingBar() { + const navigation = useNavigation(); + + if (navigation.state === "idle") return null; + + return ( +
+
+
+ ); +} + +export function DumpError() { + const error = useRouteError(); + const message = + error instanceof Error ? ( +
+
+          {JSON.stringify(
+            {
+              ...error,
+              name: error.name,
+              message: error.message,
+            },
+            null,
+            2,
+          )}
+        
+ {error.stack &&
{error.stack}
} +
+ ) : ( +
Unknown Error
+ ); + return ( + <> +

Oooops

+
{message}
+ + ); +} diff --git a/fixtures/react-router-rsc/app/routes/test-action-state/client.tsx b/fixtures/react-router-rsc/app/routes/test-action-state/client.tsx new file mode 100644 index 00000000..be67c28b --- /dev/null +++ b/fixtures/react-router-rsc/app/routes/test-action-state/client.tsx @@ -0,0 +1,16 @@ +"use client"; + +import React from "react"; + +export function TestActionStateClient(props: { + action: (prev: React.ReactNode) => Promise; +}) { + const [state, formAction, isPending] = React.useActionState(props.action, null); + + return ( +
+ + {isPending ? "pending..." : state} +
+ ); +} diff --git a/fixtures/react-router-rsc/app/routes/test-action-state/server.tsx b/fixtures/react-router-rsc/app/routes/test-action-state/server.tsx new file mode 100644 index 00000000..7044c2e9 --- /dev/null +++ b/fixtures/react-router-rsc/app/routes/test-action-state/server.tsx @@ -0,0 +1,20 @@ +import { TestActionStateClient } from "./client"; + +// Test case based on +// https://github.com/remix-run/react-router/issues/13882 + +export function TestActionStateServer({ message }: { message: string }) { + return ( + { + "use server"; + await new Promise((resolve) => setTimeout(resolve, 200)); + return ( + + [(ok) ({message})] {prev} + + ); + }} + /> + ); +} diff --git a/fixtures/react-router-rsc/app/styles.css b/fixtures/react-router-rsc/app/styles.css new file mode 100644 index 00000000..c66d1648 --- /dev/null +++ b/fixtures/react-router-rsc/app/styles.css @@ -0,0 +1,32 @@ +@import "tailwindcss"; +@plugin "@tailwindcss/typography"; + +@import "./paper.css"; + +@theme { + --animate-progress: progress 1s infinite linear; + + @keyframes progress { + 0% { + transform: translateX(0) scaleX(0); + } + 40% { + transform: translateX(0) scaleX(0.4); + } + 100% { + transform: translateX(100%) scaleX(0.5); + } + } +} + +@utility vt-name { + view-transition-name: var(--vt-name); +} + +@utility no-vt { + view-transition-name: none; +} + +@view-transition { + navigation: auto; +} diff --git a/fixtures/react-router-rsc/package.json b/fixtures/react-router-rsc/package.json new file mode 100644 index 00000000..c8f30999 --- /dev/null +++ b/fixtures/react-router-rsc/package.json @@ -0,0 +1,30 @@ +{ + "name": "@fixtures/react-router-rsc", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "dev": "vite dev --port 3200", + "build": "vite build", + "preview": "vite preview", + "test": "bun test" + }, + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router": "7.16.0" + }, + "devDependencies": { + "@cloudflare/workers-types": "catalog:workers", + "@distilled.cloud/cloudflare-runtime": "workspace:*", + "@distilled.cloud/cloudflare-vite-plugin": "workspace:*", + "@tailwindcss/typography": "^0.5.19", + "@tailwindcss/vite": "^4.3.0", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "latest", + "@vitejs/plugin-rsc": "latest", + "tailwindcss": "^4.3.0", + "vite": "catalog:" + } +} diff --git a/fixtures/react-router-rsc/public/favicon.ico b/fixtures/react-router-rsc/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5dbdfcddcb14182535f6d32d1c900681321b1aa3 GIT binary patch literal 15086 zcmeI33v3ic7{|AFEmuJ-;v>ep_G*NPi6KM`qNryCe1PIJ8siIN1WZ(7qVa)RVtmC% z)Ch?tN+afMKm;5@rvorJk zcXnoOc4q51HBQnQH_jn!cAg&XI1?PlX>Kl^k8qq0;zkha`kY$Fxt#=KNJAE9CMdpW zqr4#g8`nTw191(+H4xW8Tmyru2I^3=J1G3emPxkPXA=3{vvuvse_WWSshqaqls^-m zgB7q8&Vk*aYRe?sn$n53dGH#%3y%^vxv{pL*-h0Z4bmb_(k6{FL7HWIz(V*HT#IcS z-wE{)+0x1U!RUPt3gB97%p}@oHxF4|6S*+Yw=_tLtxZ~`S=z6J?O^AfU>7qOX`JNBbV&8+bO0%@fhQitKIJ^O^ zpgIa__qD_y07t@DFlBJ)8SP_#^j{6jpaXt{U%=dx!qu=4u7^21lWEYHPPY5U3TcoQ zX_7W+lvZi>TapNk_X>k-KO%MC9iZp>1E`N34gHKd9tK&){jq2~7OsJ>!G0FzxQFw6G zm&Vb(2#-T|rM|n3>uAsG_hnbvUKFf3#ay@u4uTzia~NY%XgCHfx4^To4BDU@)HlV? z@EN=g^ymETa1sQK{kRwyE4Ax8?wT&GvaG@ASO}{&a17&^v`y z!oPdiSiia^oov(Z)QhG2&|FgE{M9_4hJROGbnj>#$~ZF$-G^|zPj*QApltKe?;u;uKHJ~-V!=VLkg7Kgct)l7u39f@%VG8e3f$N-B zAu3a4%ZGf)r+jPAYCSLt73m_J3}p>}6Tx0j(wg4vvKhP!DzgiWANiE;Ppvp}P2W@m z-VbYn+NXFF?6ngef5CfY6ZwKnWvNV4z6s^~yMXw2i5mv}jC$6$46g?G|CPAu{W5qF zDobS=zb2ILX9D827g*NtGe5w;>frjanY{f)hrBP_2ehBt1?`~ypvg_Ot4x1V+43P@Ve8>qd)9NX_jWdLo`Zfy zoeam9)@Dpym{4m@+LNxXBPjPKA7{3a&H+~xQvr>C_A;7=JrfK~$M2pCh>|xLz>W6SCs4qC|#V`)# z)0C|?$o>jzh<|-cpf

K7osU{Xp5PG4-K+L2G=)c3f&}H&M3wo7TlO_UJjQ-Oq&_ zjAc9=nNIYz{c3zxOiS5UfcE1}8#iI4@uy;$Q7>}u`j+OU0N<*Ezx$k{x_27+{s2Eg z`^=rhtIzCm!_UcJ?Db~Lh-=_))PT3{Q0{Mwdq;0>ZL%l3+;B&4!&xm#%HYAK|;b456Iv&&f$VQHf` z>$*K9w8T+paVwc7fLfMlhQ4)*zL_SG{~v4QR;IuX-(oRtYAhWOlh`NLoX0k$RUYMi z2Y!bqpdN}wz8q`-%>&Le@q|jFw92ErW-hma-le?S z-@OZt2EEUm4wLsuEMkt4zlyy29_3S50JAcQHTtgTC{P~%-mvCTzrjXOc|{}N`Cz`W zSj7CrXfa7lcsU0J(0uSX6G`54t^7}+OLM0n(|g4waOQ}bd3%!XLh?NX9|8G_|06Ie zD5F1)w5I~!et7lA{G^;uf7aqT`KE&2qx9|~O;s6t!gb`+zVLJyT2T)l*8l(j literal 0 HcmV?d00001 diff --git a/fixtures/react-router-rsc/react-router-vite/entry.browser.tsx b/fixtures/react-router-rsc/react-router-vite/entry.browser.tsx new file mode 100644 index 00000000..f1c3b792 --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/entry.browser.tsx @@ -0,0 +1,49 @@ +import { + createFromReadableStream, + createTemporaryReferenceSet, + encodeReply, + setServerCallback, +} from "@vitejs/plugin-rsc/browser"; +import { startTransition, StrictMode } from "react"; +import { hydrateRoot } from "react-dom/client"; +import type { DataRouter, unstable_RSCPayload as RSCServerPayload } from "react-router"; +import { + unstable_createCallServer as createCallServer, + unstable_getRSCStream as getRSCStream, + unstable_RSCHydratedRouter as RSCHydratedRouter, +} from "react-router/dom"; + +// Create and set the callServer function to support post-hydration server actions. +setServerCallback( + createCallServer({ + createFromReadableStream, + createTemporaryReferenceSet, + encodeReply, + }), +); + +// Get and decode the initial server payload +createFromReadableStream(getRSCStream()).then((payload) => { + startTransition(async () => { + const formState = payload.type === "render" ? await payload.formState : undefined; + + hydrateRoot( + document, + + + , + { + // @ts-expect-error - no types for this yet + formState, + }, + ); + }); +}); + +declare let __reactRouterDataRouter: DataRouter; + +if (import.meta.hot) { + import.meta.hot.on("rsc:update", () => { + __reactRouterDataRouter.revalidate(); + }); +} diff --git a/fixtures/react-router-rsc/react-router-vite/entry.rsc.single.tsx b/fixtures/react-router-rsc/react-router-vite/entry.rsc.single.tsx new file mode 100644 index 00000000..5a21ed4f --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/entry.rsc.single.tsx @@ -0,0 +1,8 @@ +import type * as EntrySsr from "./entry.ssr"; +import { fetchServer } from "./entry.rsc"; + +export default async function handler(request: Request) { + const ssr = await import.meta.viteRsc.loadModule("ssr", "index"); + + return ssr.default(request, await fetchServer(request)); +} diff --git a/fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx b/fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx new file mode 100644 index 00000000..5d3d27fa --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx @@ -0,0 +1,36 @@ +import { + createTemporaryReferenceSet, + decodeAction, + decodeFormState, + decodeReply, + loadServerAction, + renderToReadableStream, +} from "@vitejs/plugin-rsc/rsc"; +import { unstable_matchRSCServerRequest as matchRSCServerRequest } from "react-router"; +import { routes } from "../app/routes"; + +export function fetchServer(request: Request) { + return matchRSCServerRequest({ + // Provide the React Server touchpoints. + createTemporaryReferenceSet, + decodeAction, + decodeFormState, + decodeReply, + loadServerAction, + // The incoming request. + request, + // The app routes. + routes, + // Encode the match with the React Server implementation. + generateResponse(match, options) { + return new Response(renderToReadableStream(match.payload, options), { + status: match.statusCode, + headers: match.headers, + }); + }, + }); +} + +if (import.meta.hot) { + import.meta.hot.accept(); +} diff --git a/fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx b/fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx new file mode 100644 index 00000000..d4559b37 --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx @@ -0,0 +1,29 @@ +import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr"; +import { renderToReadableStream as renderHTMLToReadableStream } from "react-dom/server.edge"; +import { + unstable_routeRSCServerRequest as routeRSCServerRequest, + unstable_RSCStaticRouter as RSCStaticRouter, +} from "react-router"; + +export default async function handler( + request: Request, + serverResponse: Response, +): Promise { + const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent("index"); + + return await routeRSCServerRequest({ + request, + serverResponse, + createFromReadableStream, + async renderHTML(getPayload, options) { + const payload = getPayload(); + + return await renderHTMLToReadableStream(, { + ...options, + bootstrapScriptContent, + signal: request.signal, + formState: await payload.formState, + }); + }, + }); +} diff --git a/fixtures/react-router-rsc/react-router-vite/entry.worker.tsx b/fixtures/react-router-rsc/react-router-vite/entry.worker.tsx new file mode 100644 index 00000000..cc1f54c5 --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/entry.worker.tsx @@ -0,0 +1,24 @@ +import type * as WorkerSsr from "./worker-ssr"; +import handler from "./entry.rsc.single"; + +// The distilled Cloudflare worker wrapper expects a `{ fetch }` default export; +// the RSC single-worker handler is a bare (request) => Response function. +export default { + async fetch(request: Request): Promise { + const url = new URL(request.url); + + // Worker code that needs a non-`react-server` module (here `react-dom/server`) + // must not import it directly in this `rsc` entry — it loads it from the + // `ssr` environment via `loadModule`. Exercises a custom (non-`index`) ssr + // input + cross-environment load through the distilled plugin. + if (url.pathname === "/worker-render") { + const { renderWorkerHtml } = await import.meta.viteRsc.loadModule( + "ssr", + "worker-ssr", + ); + return Response.json({ ok: true, html: renderWorkerHtml() }); + } + + return handler(request); + }, +}; diff --git a/fixtures/react-router-rsc/react-router-vite/types.d.ts b/fixtures/react-router-rsc/react-router-vite/types.d.ts new file mode 100644 index 00000000..bb5578e1 --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/types.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/fixtures/react-router-rsc/react-router-vite/worker-ssr.tsx b/fixtures/react-router-rsc/react-router-vite/worker-ssr.tsx new file mode 100644 index 00000000..00880f6c --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/worker-ssr.tsx @@ -0,0 +1,13 @@ +import { createElement } from "react"; +import { renderToStaticMarkup } from "react-dom/server.edge"; + +// Lives in the `ssr` environment (no `react-server` condition), so it can use +// `react-dom/server` — which would fail if imported directly in the worker's +// `rsc` entry. The worker reaches it via `loadModule("ssr", "worker-ssr")`. +// This is the pattern James Opstad landed on in +// github.com/agcty/vite-rsc-worker-env-repro PR #1. +export function renderWorkerHtml(): string { + return renderToStaticMarkup( + createElement("section", null, "Worker render via the ssr environment."), + ); +} diff --git a/fixtures/react-router-rsc/test/build.test.ts b/fixtures/react-router-rsc/test/build.test.ts new file mode 100644 index 00000000..93509462 --- /dev/null +++ b/fixtures/react-router-rsc/test/build.test.ts @@ -0,0 +1,143 @@ +import { beforeAll, expect, test } from "bun:test"; +import { spawnSync } from "node:child_process"; +import * as fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +// Production-build smoke test for the distilled build manifest. Builds the +// fixture and asserts the emitted `__distilled-build.json` describes a complete, +// self-contained worker module set — the contract a deployer consumes. +const fixtureDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); +const distDir = path.join(fixtureDir, "dist"); + +type Manifest = { + version: number; + workers: { + app: { + main: string; + modules: Array<{ path: string; type: string }>; + compatibilityDate?: string; + compatibilityFlags?: Array; + }; + }; + assets?: { directory: string }; +}; +let manifest: Manifest; + +beforeAll(() => { + fs.rmSync(distDir, { recursive: true, force: true }); + const result = spawnSync("bun", ["vite", "build"], { cwd: fixtureDir, encoding: "utf8" }); + if (result.status !== 0) { + throw new Error(`vite build failed (${result.status}):\n${result.stdout}\n${result.stderr}`); + } + manifest = JSON.parse(fs.readFileSync(path.join(distDir, "__distilled-build.json"), "utf8")); +}, 120_000); + +test("emits a build manifest describing the worker entry and assets", () => { + const worker = manifest.workers.app; + + expect(manifest.version).toBe(2); + // The framework also emits `server/index.js`; the main entry must be the + // distilled worker-entry wrapper captured from the real write pass. + expect(worker.main).toBe("server/entry.worker.js"); + expect(worker.modules).toContainEqual({ path: worker.main, type: "esm" }); + expect(worker.modules.length).toBeGreaterThan(0); + expect(worker.modules.every((module) => module.path && module.type)).toBe(true); + expect(manifest.assets?.directory).toBe("client"); + expect(worker.compatibilityDate).toBe("2026-03-10"); + expect(worker.compatibilityFlags).toContain("nodejs_compat"); +}); + +test("folds the worker-loaded ssr output into the worker module set", () => { + const modulePaths = manifest.workers.app.modules.map((module) => module.path); + + // The single-worker RSC topology loads the `ssr` env at runtime via + // loadModule (`import("../../ssr/...")`), so its output must ship as part of + // the same worker — both the entry (`server/`) and child (`ssr/`) outputs. + expect(modulePaths.some((module) => module.startsWith("server/"))).toBe(true); + expect(modulePaths.some((module) => module.startsWith("ssr/"))).toBe(true); + expect(modulePaths).toContain("ssr/worker-ssr.js"); +}); + +test("worker module entries are sorted and unique", () => { + const modulePaths = manifest.workers.app.modules.map((module) => module.path); + + expect(modulePaths).toEqual([...modulePaths].sort((a, b) => a.localeCompare(b))); + expect(new Set(modulePaths).size).toBe(modulePaths.length); +}); + +test("worker module set is self-contained (every relative import resolves)", () => { + const moduleSet = new Set(manifest.workers.app.modules.map((module) => module.path)); + const transpiler = new Bun.Transpiler({ loader: "js" }); + const unresolved: Array = []; + for (const module of manifest.workers.app.modules) { + if (module.type !== "esm") continue; + for (const imported of transpiler.scanImports( + fs.readFileSync(path.join(distDir, module.path), "utf8"), + )) { + if (!imported.path.startsWith(".")) continue; + const resolved = path.posix.normalize( + path.posix.join(path.posix.dirname(module.path), imported.path), + ); + if (!moduleSet.has(resolved)) unresolved.push(`${module.path} -> ${imported.path}`); + } + } + expect(unresolved).toEqual([]); +}); + +test("client assets are not part of the worker module set", () => { + expect(manifest.workers.app.modules.some((module) => module.path.startsWith("client/"))).toBe( + false, + ); +}); + +test("a custom RSC outDir split removes stale manifest and emits no broken graph", () => { + const customDistDir = path.join(fixtureDir, "dist-custom"); + const customManifestPath = path.join(customDistDir, "__distilled-build.json"); + try { + fs.rmSync(customDistDir, { recursive: true, force: true }); + fs.mkdirSync(customDistDir, { recursive: true }); + fs.writeFileSync( + customManifestPath, + `${JSON.stringify({ + version: 2, + workers: { + app: { + main: "server/stale.js", + modules: [{ path: "server/stale.js", type: "esm" }], + }, + }, + })}\n`, + ); + + const result = spawnSync("bun", ["vite", "build", "--outDir", "dist-custom"], { + cwd: fixtureDir, + encoding: "utf8", + }); + if (result.status !== 0) { + throw new Error(`vite build failed (${result.status}):\n${result.stdout}\n${result.stderr}`); + } + + expect(result.stderr).toContain(`skipping __distilled-build.json`); + expect(fs.existsSync(customManifestPath)).toBe(false); + } finally { + fs.rmSync(customDistDir, { recursive: true, force: true }); + } +}, 120_000); + +// Must run last: it triggers a second build that rewrites `dist`. +test("a rebuild drops stale worker files left in the output", () => { + const stale = path.join(distDir, "server", "STALE_REVIEW_MARKER.js"); + fs.writeFileSync(stale, "// stale\n"); + const result = spawnSync("bun", ["vite", "build"], { cwd: fixtureDir, encoding: "utf8" }); + if (result.status !== 0) { + throw new Error(`vite build failed (${result.status}):\n${result.stdout}\n${result.stderr}`); + } + const rebuilt: Manifest = JSON.parse( + fs.readFileSync(path.join(distDir, "__distilled-build.json"), "utf8"), + ); + expect(rebuilt.workers.app.modules.map((module) => module.path)).not.toContain( + "server/STALE_REVIEW_MARKER.js", + ); + expect(fs.existsSync(stale)).toBe(false); +}, 120_000); diff --git a/fixtures/react-router-rsc/test/dev.test.ts b/fixtures/react-router-rsc/test/dev.test.ts new file mode 100644 index 00000000..434ceb6f --- /dev/null +++ b/fixtures/react-router-rsc/test/dev.test.ts @@ -0,0 +1,57 @@ +import { afterAll, beforeAll, expect, test } from "bun:test"; +import { spawn, type ChildProcess } from "node:child_process"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +// Dev-mode smoke test: boots `vite dev` (the distilled Cloudflare plugin in +// RSC mode) and asserts the RSC routes, routing, and the worker-loaded ssr +// render all respond. Build-mode tests are intentionally absent — RSC build +// is a separate track. +const fixtureDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); + +let proc: ChildProcess; +let baseUrl: string; + +beforeAll(async () => { + proc = spawn("bun", ["vite", "dev", "--port", "3251"], { + cwd: fixtureDir, + stdio: ["ignore", "pipe", "pipe"], + }); + baseUrl = await new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error("dev server did not start in time")), 90_000); + const onData = (chunk: Buffer) => { + const match = String(chunk).match(/Local:\s+(http:\/\/\S+?)\/?\s/); + if (match) { + clearTimeout(timer); + resolve(match[1].replace(/\/$/, "")); + } + }; + proc.stdout?.on("data", onData); + proc.stderr?.on("data", onData); + proc.on("exit", (code) => reject(new Error(`dev server exited early (code ${code})`))); + }); +}); + +afterAll(() => { + proc?.kill("SIGTERM"); +}); + +test("renders the RSC home route", async () => { + const res = await fetch(`${baseUrl}/`); + expect(res.status).toBe(200); + expect(await res.text()).toContain("React Router Vite"); +}); + +test("routes to /about", async () => { + const res = await fetch(`${baseUrl}/about`); + expect(res.status).toBe(200); + expect(await res.text()).toContain("About"); +}); + +test("worker loads a custom ssr module via loadModule (react-dom/server in ssr env)", async () => { + const res = await fetch(`${baseUrl}/worker-render`); + expect(res.status).toBe(200); + const body = (await res.json()) as { ok: boolean; html: string }; + expect(body.ok).toBe(true); + expect(body.html).toContain("Worker render via the ssr environment."); +}); diff --git a/fixtures/react-router-rsc/tsconfig.json b/fixtures/react-router-rsc/tsconfig.json new file mode 100644 index 00000000..c9478645 --- /dev/null +++ b/fixtures/react-router-rsc/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "allowImportingTsExtensions": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "skipLibCheck": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "moduleResolution": "Bundler", + "module": "ESNext", + "target": "ESNext", + "lib": ["ESNext", "DOM"], + "jsx": "react-jsx", + "types": ["vite/client", "bun"] + } +} diff --git a/fixtures/react-router-rsc/vite.config.ts b/fixtures/react-router-rsc/vite.config.ts new file mode 100644 index 00000000..5b0978a3 --- /dev/null +++ b/fixtures/react-router-rsc/vite.config.ts @@ -0,0 +1,60 @@ +import cloudflare from "@distilled.cloud/cloudflare-vite-plugin"; +import tailwindcss from "@tailwindcss/vite"; +import react from "@vitejs/plugin-react"; +import rsc from "@vitejs/plugin-rsc"; +import { defineConfig } from "vite"; + +// React Router (hand-rolled on @vitejs/plugin-rsc) wired to the distilled +// Cloudflare vite plugin via the single-worker child-environment model — +// the same topology vermittelbar uses with the official plugin +// (`viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] }`). The worker +// IS the `rsc` env; its handler loads `ssr` at runtime via +// `import.meta.viteRsc.loadModule("ssr", ...)`. +export default defineConfig({ + clearScreen: false, + build: { minify: false }, + plugins: [ + tailwindcss(), + { + // Workaround for https://github.com/tailwindlabs/tailwindcss/pull/19670 + name: "fix-tailwind-full-reload", + configResolved(config) { + const plugin = config.plugins.find((p) => p.name === "@tailwindcss/vite:generate:serve"); + delete plugin?.hotUpdate; + }, + }, + react(), + rsc({ + serverHandler: false, + entries: { + client: "./react-router-vite/entry.browser.tsx", + ssr: "./react-router-vite/entry.ssr.tsx", + rsc: "./react-router-vite/entry.worker.tsx", + }, + }), + cloudflare({ + main: "./react-router-vite/entry.worker.tsx", + compatibilityDate: "2026-03-10", + compatibilityFlags: ["nodejs_compat"], + viteEnvironments: { entry: "rsc", children: ["ssr"] }, + worker: { name: "fixtures-react-router-rsc" }, + onBuildComplete: (result) => { + console.log(result); + }, + }), + ], + environments: { + // A second `ssr` input the worker loads on demand via + // loadModule("ssr", "worker-ssr") — alongside the framework's `index`. + ssr: { + build: { + rollupOptions: { + input: { "worker-ssr": "./react-router-vite/worker-ssr.tsx" }, + }, + }, + }, + }, + optimizeDeps: { + include: ["react-router", "react-router/internal/react-server-client"], + }, +}); diff --git a/fixtures/react-rsc/README.md b/fixtures/react-rsc/README.md new file mode 100644 index 00000000..cd111710 --- /dev/null +++ b/fixtures/react-rsc/README.md @@ -0,0 +1,40 @@ +# Vite + RSC + +This example shows how to set up a React application with [Server Component](https://react.dev/reference/rsc/server-components) features on Vite using [`@vitejs/plugin-rsc`](https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-rsc). + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vitejs/vite-plugin-react/tree/main/packages/plugin-rsc/examples/starter) + +```sh +# run dev server +npm run dev + +# build for production and preview +npm run build +npm run preview +``` + +## API usage + +See [`@vitejs/plugin-rsc`](https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-rsc) for the documentation. + +- [`vite.config.ts`](./vite.config.ts) + - `@vitejs/plugin-rsc/plugin` +- [`./src/framework/entry.rsc.tsx`](./src/framework/entry.rsc.tsx) + - `@vitejs/plugin-rsc/rsc` + - `import.meta.viteRsc.loadModule` +- [`./src/framework/entry.ssr.tsx`](./src/framework/entry.ssr.tsx) + - `@vitejs/plugin-rsc/ssr` + - `import.meta.viteRsc.loadBootstrapScriptContent` + - `rsc-html-stream/server` +- [`./src/framework/entry.browser.tsx`](./src/framework/entry.browser.tsx) + - `@vitejs/plugin-rsc/browser` + - `rsc-html-stream/client` + +## Notes + +- [`./src/framework/entry.{browser,rsc,ssr}.tsx`](./src/framework) (with inline comments) provides an overview of how low level RSC (React flight) API can be used to build RSC framework. +- You can use [`vite-plugin-inspect`](https://github.com/antfu-collective/vite-plugin-inspect) to understand how `"use client"` and `"use server"` directives are transformed internally. + +## Deployment + +See [vite-plugin-rsc-deploy-example](https://github.com/hi-ogawa/vite-plugin-rsc-deploy-example) diff --git a/fixtures/react-rsc/package.json b/fixtures/react-rsc/package.json new file mode 100644 index 00000000..e98daaf8 --- /dev/null +++ b/fixtures/react-rsc/package.json @@ -0,0 +1,28 @@ +{ + "name": "@fixtures/react-rsc", + "version": "0.0.0", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "dev": "vite dev --port 3100", + "build": "vite build", + "preview": "vite preview", + "test": "bun test" + }, + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7" + }, + "devDependencies": { + "@cloudflare/workers-types": "catalog:workers", + "@distilled.cloud/cloudflare-runtime": "workspace:*", + "@distilled.cloud/cloudflare-vite-plugin": "workspace:*", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "latest", + "@vitejs/plugin-rsc": "latest", + "rsc-html-stream": "^0.0.7", + "vite": "catalog:" + } +} diff --git a/fixtures/react-rsc/public/vite.svg b/fixtures/react-rsc/public/vite.svg new file mode 100644 index 00000000..e7b8dfb1 --- /dev/null +++ b/fixtures/react-rsc/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fixtures/react-rsc/src/action.tsx b/fixtures/react-rsc/src/action.tsx new file mode 100644 index 00000000..6b5029dc --- /dev/null +++ b/fixtures/react-rsc/src/action.tsx @@ -0,0 +1,11 @@ +"use server"; + +let serverCounter = 0; + +export async function getServerCounter() { + return serverCounter; +} + +export async function updateServerCounter(change: number) { + serverCounter += change; +} diff --git a/fixtures/react-rsc/src/assets/react.svg b/fixtures/react-rsc/src/assets/react.svg new file mode 100644 index 00000000..6c87de9b --- /dev/null +++ b/fixtures/react-rsc/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fixtures/react-rsc/src/client.tsx b/fixtures/react-rsc/src/client.tsx new file mode 100644 index 00000000..ac69d863 --- /dev/null +++ b/fixtures/react-rsc/src/client.tsx @@ -0,0 +1,9 @@ +"use client"; + +import React from "react"; + +export function ClientCounter() { + const [count, setCount] = React.useState(0); + + return ; +} diff --git a/fixtures/react-rsc/src/framework/entry.browser.tsx b/fixtures/react-rsc/src/framework/entry.browser.tsx new file mode 100644 index 00000000..5e2c2031 --- /dev/null +++ b/fixtures/react-rsc/src/framework/entry.browser.tsx @@ -0,0 +1,138 @@ +import { + createFromReadableStream, + createFromFetch, + setServerCallback, + createTemporaryReferenceSet, + encodeReply, +} from "@vitejs/plugin-rsc/browser"; +import React from "react"; +import { createRoot, hydrateRoot } from "react-dom/client"; +import { rscStream } from "rsc-html-stream/client"; +import type { RscPayload } from "./entry.rsc"; +import { GlobalErrorBoundary } from "./error-boundary"; +import { createRscRenderRequest } from "./request"; + +async function main() { + // stash `setPayload` function to trigger re-rendering + // from outside of `BrowserRoot` component (e.g. server function call, navigation, hmr) + let setPayload: (v: RscPayload) => void; + + // deserialize RSC stream back to React VDOM for CSR + const initialPayload = await createFromReadableStream( + // initial RSC stream is injected in SSR stream as + rscStream, + ); + + // browser root component to (re-)render RSC payload as state + function BrowserRoot() { + const [payload, setPayload_] = React.useState(initialPayload); + + React.useEffect(() => { + setPayload = (v) => React.startTransition(() => setPayload_(v)); + }, [setPayload_]); + + // re-fetch/render on client side navigation + React.useEffect(() => { + return listenNavigation(() => fetchRscPayload()); + }, []); + + return payload.root; + } + + // re-fetch RSC and trigger re-rendering + async function fetchRscPayload() { + const renderRequest = createRscRenderRequest(window.location.href); + const payload = await createFromFetch(fetch(renderRequest)); + setPayload(payload); + } + + // register a handler which will be internally called by React + // on server function request after hydration. + setServerCallback(async (id, args) => { + const temporaryReferences = createTemporaryReferenceSet(); + const renderRequest = createRscRenderRequest(window.location.href, { + id, + body: await encodeReply(args, { temporaryReferences }), + }); + const payload = await createFromFetch(fetch(renderRequest), { + temporaryReferences, + }); + setPayload(payload); + const { ok, data } = payload.returnValue!; + if (!ok) throw data; + return data; + }); + + // hydration + const browserRoot = ( + + + + + + ); + if ("__NO_HYDRATE" in globalThis) { + createRoot(document).render(browserRoot); + } else { + hydrateRoot(document, browserRoot, { + formState: initialPayload.formState, + }); + } + + // implement server HMR by triggering re-fetch/render of RSC upon server code change + if (import.meta.hot) { + import.meta.hot.on("rsc:update", () => { + fetchRscPayload(); + }); + } +} + +// a little helper to setup events interception for client side navigation +function listenNavigation(onNavigation: () => void) { + window.addEventListener("popstate", onNavigation); + + const oldPushState = window.history.pushState; + window.history.pushState = function (...args) { + const res = oldPushState.apply(this, args); + onNavigation(); + return res; + }; + + const oldReplaceState = window.history.replaceState; + window.history.replaceState = function (...args) { + const res = oldReplaceState.apply(this, args); + onNavigation(); + return res; + }; + + function onClick(e: MouseEvent) { + let link = (e.target as Element).closest("a"); + if ( + link && + link instanceof HTMLAnchorElement && + link.href && + (!link.target || link.target === "_self") && + link.origin === location.origin && + !link.hasAttribute("download") && + e.button === 0 && // left clicks only + !e.metaKey && // open in new tab (mac) + !e.ctrlKey && // open in new tab (windows) + !e.altKey && // download + !e.shiftKey && + !e.defaultPrevented + ) { + e.preventDefault(); + history.pushState(null, "", link.href); + } + } + document.addEventListener("click", onClick); + + return () => { + document.removeEventListener("click", onClick); + window.removeEventListener("popstate", onNavigation); + window.history.pushState = oldPushState; + window.history.replaceState = oldReplaceState; + }; +} + +main(); diff --git a/fixtures/react-rsc/src/framework/entry.rsc.tsx b/fixtures/react-rsc/src/framework/entry.rsc.tsx new file mode 100644 index 00000000..68f4eb94 --- /dev/null +++ b/fixtures/react-rsc/src/framework/entry.rsc.tsx @@ -0,0 +1,121 @@ +import { + renderToReadableStream, + createTemporaryReferenceSet, + decodeReply, + loadServerAction, + decodeAction, + decodeFormState, +} from "@vitejs/plugin-rsc/rsc"; +import type { ReactFormState } from "react-dom/client"; +import type * as EntrySsr from "./entry.ssr.tsx"; +import { Root } from "../root.tsx"; +import { parseRenderRequest } from "./request.tsx"; + +// The schema of payload which is serialized into RSC stream on rsc environment +// and deserialized on ssr/client environments. +export type RscPayload = { + // this demo renders/serializes/deserizlies entire root html element + // but this mechanism can be changed to render/fetch different parts of components + // based on your own route conventions. + root: React.ReactNode; + // server action return value of non-progressive enhancement case + returnValue?: { ok: boolean; data: unknown }; + // server action form state (e.g. useActionState) of progressive enhancement case + formState?: ReactFormState; +}; + +// the plugin by default assumes `rsc` entry having default export of request handler. +// however, how server entries are executed can be customized by registering own server handler. +export default { fetch: handler }; + +async function handler(request: Request): Promise { + // differentiate RSC, SSR, action, etc. + const renderRequest = parseRenderRequest(request); + request = renderRequest.request; + + // handle server function request + let returnValue: RscPayload["returnValue"] | undefined; + let formState: ReactFormState | undefined; + let temporaryReferences: unknown | undefined; + let actionStatus: number | undefined; + if (renderRequest.isAction === true) { + if (renderRequest.actionId) { + // action is called via `ReactClient.setServerCallback`. + const contentType = request.headers.get("content-type"); + const body = contentType?.startsWith("multipart/form-data") + ? await request.formData() + : await request.text(); + temporaryReferences = createTemporaryReferenceSet(); + const args = await decodeReply(body, { temporaryReferences }); + const action = await loadServerAction(renderRequest.actionId); + try { + const data = await action.apply(null, args); + returnValue = { ok: true, data }; + } catch (e) { + returnValue = { ok: false, data: e }; + actionStatus = 500; + } + } else { + // otherwise server function is called via `

` + // before hydration (e.g. when javascript is disabled). + // aka progressive enhancement. + const formData = await request.formData(); + const decodedAction = await decodeAction(formData); + try { + const result = await decodedAction(); + formState = await decodeFormState(result, formData); + } catch { + // there's no single general obvious way to surface this error, + // so explicitly return classic 500 response. + return new Response("Internal Server Error: server action failed", { + status: 500, + }); + } + } + } + + // serialization from React VDOM tree to RSC stream. + // we render RSC stream after handling server function request + // so that new render reflects updated state from server function call + // to achieve single round trip to mutate and fetch from server. + const rscPayload: RscPayload = { + root: , + formState, + returnValue, + }; + const rscOptions = { temporaryReferences }; + const rscStream = renderToReadableStream(rscPayload, rscOptions); + + // Respond RSC stream without HTML rendering as decided by `RenderRequest` + if (renderRequest.isRsc) { + return new Response(rscStream, { + status: actionStatus, + headers: { + "content-type": "text/x-component;charset=utf-8", + }, + }); + } + + // Delegate to SSR environment for html rendering. + // The plugin provides `loadModule` helper to allow loading SSR environment entry module + // in RSC environment. however this can be customized by implementing own runtime communication + // e.g. `@cloudflare/vite-plugin`'s service binding. + const ssrEntryModule = await import.meta.viteRsc.loadModule("ssr", "index"); + const ssrResult = await ssrEntryModule.renderHTML(rscStream, { + formState, + // allow quick simulation of javascript disabled browser + debugNojs: renderRequest.url.searchParams.has("__nojs"), + }); + + // respond html + return new Response(ssrResult.stream, { + status: ssrResult.status, + headers: { + "Content-type": "text/html", + }, + }); +} + +if (import.meta.hot) { + import.meta.hot.accept(); +} diff --git a/fixtures/react-rsc/src/framework/entry.ssr.tsx b/fixtures/react-rsc/src/framework/entry.ssr.tsx new file mode 100644 index 00000000..27d8ae71 --- /dev/null +++ b/fixtures/react-rsc/src/framework/entry.ssr.tsx @@ -0,0 +1,70 @@ +import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr"; +import React from "react"; +import type { ReactFormState } from "react-dom/client"; +import { renderToReadableStream } from "react-dom/server.edge"; +import { injectRSCPayload } from "rsc-html-stream/server"; +import type { RscPayload } from "./entry.rsc"; + +export async function renderHTML( + rscStream: ReadableStream, + options: { + formState?: ReactFormState; + nonce?: string; + debugNojs?: boolean; + }, +): Promise<{ stream: ReadableStream; status?: number }> { + // duplicate one RSC stream into two. + // - one for SSR (ReactClient.createFromReadableStream below) + // - another for browser hydration payload by injecting . + const [rscStream1, rscStream2] = rscStream.tee(); + + // deserialize RSC stream back to React VDOM + let payload: Promise | undefined; + function SsrRoot() { + // deserialization needs to be kicked off inside ReactDOMServer context + // for ReactDomServer preinit/preloading to work + payload ??= createFromReadableStream(rscStream1); + return React.use(payload).root; + } + + // render html (traditional SSR) + const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent("index"); + let htmlStream: ReadableStream; + let status: number | undefined; + try { + htmlStream = await renderToReadableStream(, { + bootstrapScriptContent: options?.debugNojs ? undefined : bootstrapScriptContent, + nonce: options?.nonce, + formState: options?.formState, + }); + } catch { + // fallback to render an empty shell and run pure CSR on browser, + // which can replay server component error and trigger error boundary. + status = 500; + htmlStream = await renderToReadableStream( + + + + + , + { + bootstrapScriptContent: + `self.__NO_HYDRATE=1;` + (options?.debugNojs ? "" : bootstrapScriptContent), + nonce: options?.nonce, + }, + ); + } + + let responseStream: ReadableStream = htmlStream; + if (!options?.debugNojs) { + // initial RSC stream is injected in HTML stream as + // using utility made by devongovett https://github.com/devongovett/rsc-html-stream + responseStream = responseStream.pipeThrough( + injectRSCPayload(rscStream2, { + nonce: options?.nonce, + }), + ); + } + + return { stream: responseStream, status }; +} diff --git a/fixtures/react-rsc/src/framework/error-boundary.tsx b/fixtures/react-rsc/src/framework/error-boundary.tsx new file mode 100644 index 00000000..ccfb696d --- /dev/null +++ b/fixtures/react-rsc/src/framework/error-boundary.tsx @@ -0,0 +1,75 @@ +"use client"; + +import React from "react"; + +// Minimal ErrorBoundary example to handle errors globally on browser +export function GlobalErrorBoundary(props: { children?: React.ReactNode }) { + return {props.children}; +} + +// https://github.com/vercel/next.js/blob/33f8428f7066bf8b2ec61f025427ceb2a54c4bdf/packages/next/src/client/components/error-boundary.tsx +// https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary +class ErrorBoundary extends React.Component<{ + children?: React.ReactNode; + errorComponent: React.FC<{ + error: Error; + reset: () => void; + }>; +}> { + state: { error?: Error } = {}; + + static getDerivedStateFromError(error: Error) { + return { error }; + } + + reset = () => { + this.setState({ error: null }); + }; + + render() { + const error = this.state.error; + if (error) { + return ; + } + return this.props.children; + } +} + +// https://github.com/vercel/next.js/blob/677c9b372faef680d17e9ba224743f44e1107661/packages/next/src/build/webpack/loaders/next-app-loader.ts#L73 +// https://github.com/vercel/next.js/blob/677c9b372faef680d17e9ba224743f44e1107661/packages/next/src/client/components/error-boundary.tsx#L145 +function DefaultGlobalErrorPage(props: { error: Error; reset: () => void }) { + return ( + + + Unexpected Error + + +

Caught an unexpected error

+
+          Error:{" "}
+          {import.meta.env.DEV && "message" in props.error ? props.error.message : "(Unknown)"}
+        
+ + + + ); +} diff --git a/fixtures/react-rsc/src/framework/request.tsx b/fixtures/react-rsc/src/framework/request.tsx new file mode 100644 index 00000000..7d48788e --- /dev/null +++ b/fixtures/react-rsc/src/framework/request.tsx @@ -0,0 +1,58 @@ +// Framework conventions (arbitrary choices for this demo): +// - Use `_.rsc` URL suffix to differentiate RSC requests from SSR requests +// - Use `x-rsc-action` header to pass server action ID +const URL_POSTFIX = "_.rsc"; +const HEADER_ACTION_ID = "x-rsc-action"; + +// Parsed request information used to route between RSC/SSR rendering and action handling. +// Created by parseRenderRequest() from incoming HTTP requests. +type RenderRequest = { + isRsc: boolean; // true if request should return RSC payload (via _.rsc suffix) + isAction: boolean; // true if this is a server action call (POST request) + actionId?: string; // server action ID from x-rsc-action header + request: Request; // normalized Request with _.rsc suffix removed from URL + url: URL; // normalized URL with _.rsc suffix removed +}; + +export function createRscRenderRequest( + urlString: string, + action?: { id: string; body: BodyInit }, +): Request { + const url = new URL(urlString); + url.pathname += URL_POSTFIX; + const headers = new Headers(); + if (action) { + headers.set(HEADER_ACTION_ID, action.id); + } + return new Request(url.toString(), { + method: action ? "POST" : "GET", + headers, + body: action?.body, + }); +} + +export function parseRenderRequest(request: Request): RenderRequest { + const url = new URL(request.url); + const isAction = request.method === "POST"; + if (url.pathname.endsWith(URL_POSTFIX)) { + url.pathname = url.pathname.slice(0, -URL_POSTFIX.length); + const actionId = request.headers.get(HEADER_ACTION_ID) || undefined; + if (request.method === "POST" && !actionId) { + throw new Error("Missing action id header for RSC action request"); + } + return { + isRsc: true, + isAction, + actionId, + request: new Request(url, request), + url, + }; + } else { + return { + isRsc: false, + isAction, + request, + url, + }; + } +} diff --git a/fixtures/react-rsc/src/index.css b/fixtures/react-rsc/src/index.css new file mode 100644 index 00000000..f4d2128c --- /dev/null +++ b/fixtures/react-rsc/src/index.css @@ -0,0 +1,112 @@ +:root { + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} + +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 1rem; +} + +.read-the-docs { + color: #888; + text-align: left; +} diff --git a/fixtures/react-rsc/src/root.tsx b/fixtures/react-rsc/src/root.tsx new file mode 100644 index 00000000..aaf322d0 --- /dev/null +++ b/fixtures/react-rsc/src/root.tsx @@ -0,0 +1,70 @@ +// oxlint-disable-next-line import/no-unassigned-import +import "./index.css"; // css import is automatically injected in exported server components +// oxlint-disable-next-line import/no-absolute-path +import viteLogo from "/vite.svg"; +import { getServerCounter, updateServerCounter } from "./action.tsx"; +import reactLogo from "./assets/react.svg"; +import { ClientCounter } from "./client.tsx"; + +export function Root(props: { url: URL }) { + return ( + + + + + + Vite + RSC + + + + + + ); +} + +function App(props: { url: URL }) { + return ( +
+
+ + Vite logo + + + React logo + +
+

Vite + RSC

+
+ +
+
+ + + +
+
Request URL: {props.url?.href}
+
    +
  • + Edit src/client.tsx to test client HMR. +
  • +
  • + Edit src/root.tsx to test server HMR. +
  • +
  • + Visit{" "} + + _.rsc + {" "} + to view RSC stream payload. +
  • +
  • + Visit{" "} + + ?__nojs + {" "} + to test server action without js enabled. +
  • +
+
+ ); +} diff --git a/fixtures/react-rsc/test/dev.test.ts b/fixtures/react-rsc/test/dev.test.ts new file mode 100644 index 00000000..6a86c442 --- /dev/null +++ b/fixtures/react-rsc/test/dev.test.ts @@ -0,0 +1,46 @@ +import { afterAll, beforeAll, expect, test } from "bun:test"; +import { spawn, type ChildProcess } from "node:child_process"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +// Dev-mode smoke test: boots `vite dev` (the distilled Cloudflare plugin in +// RSC mode) and asserts the minimal RSC app renders — server components, +// the client component, and the server action are all present in the SSR'd +// HTML. Build-mode is a separate track and intentionally not tested here. +const fixtureDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); + +let proc: ChildProcess; +let baseUrl: string; + +beforeAll(async () => { + proc = spawn("bun", ["vite", "dev", "--port", "3151"], { + cwd: fixtureDir, + stdio: ["ignore", "pipe", "pipe"], + }); + baseUrl = await new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error("dev server did not start in time")), 90_000); + const onData = (chunk: Buffer) => { + const match = String(chunk).match(/Local:\s+(http:\/\/\S+?)\/?\s/); + if (match) { + clearTimeout(timer); + resolve(match[1].replace(/\/$/, "")); + } + }; + proc.stdout?.on("data", onData); + proc.stderr?.on("data", onData); + proc.on("exit", (code) => reject(new Error(`dev server exited early (code ${code})`))); + }); +}); + +afterAll(() => { + proc?.kill("SIGTERM"); +}); + +test("server-renders the RSC app (server component + client component + server action)", async () => { + const res = await fetch(`${baseUrl}/`); + expect(res.status).toBe(200); + const html = await res.text(); + expect(html).toContain("Vite + RSC"); // server component + expect(html).toContain("Client Counter"); // client component + expect(html).toContain("Server Counter"); // server action +}); diff --git a/fixtures/react-rsc/test/plugin-order.test.ts b/fixtures/react-rsc/test/plugin-order.test.ts new file mode 100644 index 00000000..3aa02ec4 --- /dev/null +++ b/fixtures/react-rsc/test/plugin-order.test.ts @@ -0,0 +1,47 @@ +import { afterAll, beforeAll, expect, test } from "bun:test"; +import { spawn, type ChildProcess } from "node:child_process"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +// Locks order-independence (review finding): with the Cloudflare plugin +// registered BEFORE rsc(), the rsc env's resolve.conditions lists `react-server` +// after the workerd conditions (not first). Export-condition resolution is +// set-membership, so the app must still render correctly — if `react-server` +// weren't effective in the rsc env, flight generation would 500. See +// vite.config.cf-first.ts. +const fixtureDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); + +let proc: ChildProcess; +let baseUrl: string; + +beforeAll(async () => { + proc = spawn("bun", ["vite", "dev", "-c", "vite.config.cf-first.ts", "--port", "3152"], { + cwd: fixtureDir, + stdio: ["ignore", "pipe", "pipe"], + }); + baseUrl = await new Promise((resolve, reject) => { + const timer = setTimeout(() => reject(new Error("dev server did not start in time")), 90_000); + const onData = (chunk: Buffer) => { + const match = String(chunk).match(/Local:\s+(http:\/\/\S+?)\/?\s/); + if (match) { + clearTimeout(timer); + resolve(match[1].replace(/\/$/, "")); + } + }; + proc.stdout?.on("data", onData); + proc.stderr?.on("data", onData); + proc.on("exit", (code) => reject(new Error(`dev server exited early (code ${code})`))); + }); +}); + +afterAll(() => { + proc?.kill("SIGTERM"); +}); + +test("RSC renders correctly even with cloudflare() before rsc() (react-server not first in conditions)", async () => { + const res = await fetch(`${baseUrl}/`); + expect(res.status).toBe(200); + const html = await res.text(); + expect(html).toContain("Vite + RSC"); + expect(html).toContain("Client Counter"); +}); diff --git a/fixtures/react-rsc/tsconfig.json b/fixtures/react-rsc/tsconfig.json new file mode 100644 index 00000000..b212cd7a --- /dev/null +++ b/fixtures/react-rsc/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "erasableSyntaxOnly": true, + "allowImportingTsExtensions": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "skipLibCheck": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "moduleResolution": "Bundler", + "module": "ESNext", + "target": "ESNext", + "lib": ["ESNext", "DOM"], + "types": ["vite/client", "@vitejs/plugin-rsc/types"], + "jsx": "react-jsx" + } +} diff --git a/fixtures/react-rsc/vite.config.ts b/fixtures/react-rsc/vite.config.ts new file mode 100644 index 00000000..48026b04 --- /dev/null +++ b/fixtures/react-rsc/vite.config.ts @@ -0,0 +1,39 @@ +import cloudflare from "@distilled.cloud/cloudflare-vite-plugin"; +import react from "@vitejs/plugin-react"; +import rsc from "@vitejs/plugin-rsc"; +import { defineConfig } from "vite"; + +// Minimal React Server Components app (the @vitejs/plugin-rsc `starter`) +// wired to the distilled Cloudflare vite plugin, to reproduce and then fix +// RSC dev support (cloudflare-tools#43). +// +// The worker IS the `rsc` environment: plugin-rsc resolves it with the +// `react-server` condition and its `default export` ({ fetch }) is the +// request handler. `serverHandler: false` tells plugin-rsc not to mount its +// own Node dev middleware, so requests route into workerd instead. +export default defineConfig({ + plugins: [ + cloudflare({ + main: "./src/framework/entry.rsc.tsx", + compatibilityDate: "2026-03-10", + compatibilityFlags: ["nodejs_compat"], + // The Worker is the `rsc` environment; it loads `ssr` modules at runtime. + viteEnvironments: { entry: "rsc", children: ["ssr"] }, + worker: { name: "fixtures-react-rsc", bindings: [] }, + }), + rsc(), + react(), + ], + + environments: { + rsc: { + build: { rolldownOptions: { input: { index: "./src/framework/entry.rsc.tsx" } } }, + }, + ssr: { + build: { rolldownOptions: { input: { index: "./src/framework/entry.ssr.tsx" } } }, + }, + client: { + build: { rolldownOptions: { input: { index: "./src/framework/entry.browser.tsx" } } }, + }, + }, +}); From d07bb18004c9c20b6ba8e73dc549abbdfbdde4b2 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Thu, 25 Jun 2026 11:41:14 -0400 Subject: [PATCH 12/16] remove test fixtures --- bun.lock | 94 ++--------- fixtures/react-router-rsc/README.md | 34 ---- fixtures/react-router-rsc/app/paper.css | 150 ------------------ fixtures/react-router-rsc/app/root.tsx | 55 ------- fixtures/react-router-rsc/app/routes.ts | 21 --- .../react-router-rsc/app/routes/about.tsx | 20 --- .../react-router-rsc/app/routes/client.tsx | 16 -- .../app/routes/home.actions.ts | 7 - .../app/routes/home.client.tsx | 12 -- fixtures/react-router-rsc/app/routes/home.css | 3 - fixtures/react-router-rsc/app/routes/home.tsx | 34 ---- .../app/routes/root.client.tsx | 44 ----- .../app/routes/test-action-state/client.tsx | 16 -- .../app/routes/test-action-state/server.tsx | 20 --- fixtures/react-router-rsc/app/styles.css | 32 ---- fixtures/react-router-rsc/package.json | 30 ---- fixtures/react-router-rsc/public/favicon.ico | Bin 15086 -> 0 bytes .../react-router-vite/entry.browser.tsx | 49 ------ .../react-router-vite/entry.rsc.single.tsx | 8 - .../react-router-vite/entry.rsc.tsx | 36 ----- .../react-router-vite/entry.ssr.tsx | 29 ---- .../react-router-vite/entry.worker.tsx | 24 --- .../react-router-vite/types.d.ts | 2 - .../react-router-vite/worker-ssr.tsx | 13 -- fixtures/react-router-rsc/test/build.test.ts | 143 ----------------- fixtures/react-router-rsc/test/dev.test.ts | 57 ------- fixtures/react-router-rsc/tsconfig.json | 16 -- fixtures/react-router-rsc/vite.config.ts | 60 ------- fixtures/react-rsc/README.md | 40 ----- fixtures/react-rsc/package.json | 28 ---- fixtures/react-rsc/public/vite.svg | 1 - fixtures/react-rsc/src/action.tsx | 11 -- fixtures/react-rsc/src/assets/react.svg | 1 - fixtures/react-rsc/src/client.tsx | 9 -- .../react-rsc/src/framework/entry.browser.tsx | 138 ---------------- .../react-rsc/src/framework/entry.rsc.tsx | 121 -------------- .../react-rsc/src/framework/entry.ssr.tsx | 70 -------- .../src/framework/error-boundary.tsx | 75 --------- fixtures/react-rsc/src/framework/request.tsx | 58 ------- fixtures/react-rsc/src/index.css | 112 ------------- fixtures/react-rsc/src/root.tsx | 70 -------- fixtures/react-rsc/test/dev.test.ts | 46 ------ fixtures/react-rsc/test/plugin-order.test.ts | 47 ------ fixtures/react-rsc/tsconfig.json | 17 -- fixtures/react-rsc/vite.config.ts | 39 ----- 45 files changed, 16 insertions(+), 1892 deletions(-) delete mode 100644 fixtures/react-router-rsc/README.md delete mode 100644 fixtures/react-router-rsc/app/paper.css delete mode 100644 fixtures/react-router-rsc/app/root.tsx delete mode 100644 fixtures/react-router-rsc/app/routes.ts delete mode 100644 fixtures/react-router-rsc/app/routes/about.tsx delete mode 100644 fixtures/react-router-rsc/app/routes/client.tsx delete mode 100644 fixtures/react-router-rsc/app/routes/home.actions.ts delete mode 100644 fixtures/react-router-rsc/app/routes/home.client.tsx delete mode 100644 fixtures/react-router-rsc/app/routes/home.css delete mode 100644 fixtures/react-router-rsc/app/routes/home.tsx delete mode 100644 fixtures/react-router-rsc/app/routes/root.client.tsx delete mode 100644 fixtures/react-router-rsc/app/routes/test-action-state/client.tsx delete mode 100644 fixtures/react-router-rsc/app/routes/test-action-state/server.tsx delete mode 100644 fixtures/react-router-rsc/app/styles.css delete mode 100644 fixtures/react-router-rsc/package.json delete mode 100644 fixtures/react-router-rsc/public/favicon.ico delete mode 100644 fixtures/react-router-rsc/react-router-vite/entry.browser.tsx delete mode 100644 fixtures/react-router-rsc/react-router-vite/entry.rsc.single.tsx delete mode 100644 fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx delete mode 100644 fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx delete mode 100644 fixtures/react-router-rsc/react-router-vite/entry.worker.tsx delete mode 100644 fixtures/react-router-rsc/react-router-vite/types.d.ts delete mode 100644 fixtures/react-router-rsc/react-router-vite/worker-ssr.tsx delete mode 100644 fixtures/react-router-rsc/test/build.test.ts delete mode 100644 fixtures/react-router-rsc/test/dev.test.ts delete mode 100644 fixtures/react-router-rsc/tsconfig.json delete mode 100644 fixtures/react-router-rsc/vite.config.ts delete mode 100644 fixtures/react-rsc/README.md delete mode 100644 fixtures/react-rsc/package.json delete mode 100644 fixtures/react-rsc/public/vite.svg delete mode 100644 fixtures/react-rsc/src/action.tsx delete mode 100644 fixtures/react-rsc/src/assets/react.svg delete mode 100644 fixtures/react-rsc/src/client.tsx delete mode 100644 fixtures/react-rsc/src/framework/entry.browser.tsx delete mode 100644 fixtures/react-rsc/src/framework/entry.rsc.tsx delete mode 100644 fixtures/react-rsc/src/framework/entry.ssr.tsx delete mode 100644 fixtures/react-rsc/src/framework/error-boundary.tsx delete mode 100644 fixtures/react-rsc/src/framework/request.tsx delete mode 100644 fixtures/react-rsc/src/index.css delete mode 100644 fixtures/react-rsc/src/root.tsx delete mode 100644 fixtures/react-rsc/test/dev.test.ts delete mode 100644 fixtures/react-rsc/test/plugin-order.test.ts delete mode 100644 fixtures/react-rsc/tsconfig.json delete mode 100644 fixtures/react-rsc/vite.config.ts diff --git a/bun.lock b/bun.lock index d94e35c2..fc738973 100644 --- a/bun.lock +++ b/bun.lock @@ -15,46 +15,6 @@ "typescript": "catalog:", }, }, - "fixtures/react-router-rsc": { - "name": "@fixtures/react-router-rsc", - "dependencies": { - "react": "^19.2.7", - "react-dom": "^19.2.7", - "react-router": "7.16.0", - }, - "devDependencies": { - "@cloudflare/workers-types": "catalog:workers", - "@distilled.cloud/cloudflare-runtime": "workspace:*", - "@distilled.cloud/cloudflare-vite-plugin": "workspace:*", - "@tailwindcss/typography": "^0.5.19", - "@tailwindcss/vite": "^4.3.0", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "latest", - "@vitejs/plugin-rsc": "latest", - "tailwindcss": "^4.3.0", - "vite": "catalog:", - }, - }, - "fixtures/react-rsc": { - "name": "@fixtures/react-rsc", - "version": "0.0.0", - "dependencies": { - "react": "^19.2.7", - "react-dom": "^19.2.7", - }, - "devDependencies": { - "@cloudflare/workers-types": "catalog:workers", - "@distilled.cloud/cloudflare-runtime": "workspace:*", - "@distilled.cloud/cloudflare-vite-plugin": "workspace:*", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "latest", - "@vitejs/plugin-rsc": "latest", - "rsc-html-stream": "^0.0.7", - "vite": "catalog:", - }, - }, "fixtures/solid-ssr": { "name": "@fixtures/solid-ssr", "version": "0.0.0", @@ -467,10 +427,6 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], - "@fixtures/react-router-rsc": ["@fixtures/react-router-rsc@workspace:fixtures/react-router-rsc"], - - "@fixtures/react-rsc": ["@fixtures/react-rsc@workspace:fixtures/react-rsc"], - "@fixtures/solid-ssr": ["@fixtures/solid-ssr@workspace:fixtures/solid-ssr"], "@fixtures/solidstart": ["@fixtures/solidstart@workspace:fixtures/solidstart"], @@ -1041,7 +997,7 @@ "@types/node": ["@types/node@24.12.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA=="], - "@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="], + "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], @@ -1055,7 +1011,7 @@ "@vercel/nft": ["@vercel/nft@1.5.0", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^13.0.0", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-IWTDeIoWhQ7ZtRO/JRKH+jhmeQvZYhtGPmzw/QGDY+wDCQqfm25P9yIdoAFagu4fWsK4IwZXDFIjrmp5rRm/sA=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.2", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.0" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], "@vitejs/plugin-rsc": ["@vitejs/plugin-rsc@0.5.27", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1", "es-module-lexer": "^2.1.0", "estree-walker": "^3.0.3", "magic-string": "^0.30.21", "srvx": "^0.11.15", "strip-literal": "^3.1.0", "turbo-stream": "^3.2.0", "vitefu": "^1.1.3" }, "peerDependencies": { "react": "*", "react-dom": "*", "react-server-dom-webpack": "*", "vite": "*" }, "optionalPeers": ["react-server-dom-webpack"] }, "sha512-s1fd5DUkPXk86DDHPM/kP93WrvI0MoA8klxdDZmD1fMSaA9xujfgunsm8ZoUH0FemR+63vNalFsIDR0AJH4ktg=="], @@ -1457,7 +1413,7 @@ "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], - "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], @@ -1701,14 +1657,12 @@ "rc9": ["rc9@3.0.1", "", { "dependencies": { "defu": "^6.1.6", "destr": "^2.0.5" } }, "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ=="], - "react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], + "react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], - "react-dom": ["react-dom@19.2.7", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.7" } }, "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ=="], + "react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], - "react-router": ["react-router@7.16.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-wArC8lVyJb3+jM9OpDyW6hLCizACWkvQR/sSGqSs+o5uEXEtGlqdZ4v8hENR3Jad6i+LRkK93q/+bQAcvl6V1A=="], - "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], "readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "^5.1.0" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="], @@ -1743,8 +1697,6 @@ "rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="], - "rsc-html-stream": ["rsc-html-stream@0.0.7", "", {}, "sha512-v9+fuY7usTgvXdNl8JmfXCvSsQbq2YMd60kOeeMIqCJFZ69fViuIxztHei7v5mlMMa2h3SqS+v44Gu9i9xANZA=="], - "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -1771,8 +1723,6 @@ "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], - "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], - "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], @@ -1813,7 +1763,7 @@ "sql-escaper": ["sql-escaper@1.3.3", "", {}, "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw=="], - "srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], + "srvx": ["srvx@0.9.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-RZaxTKJEE/14HYn8COLuUOJAt0U55N9l1Xf6jj+T0GoA01EUH1Xz5JtSUOI+EHn+AEgPCVn7gk6jHJffrr06fQ=="], "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], @@ -2015,8 +1965,6 @@ "@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], - "@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "@babel/core/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], "@babel/core/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], @@ -2061,14 +2009,6 @@ "@fixtures/tanstack-start/@types/node": ["@types/node@22.19.19", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew=="], - "@fixtures/tanstack-start/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], - - "@fixtures/tanstack-start/@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], - - "@fixtures/tanstack-start/react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], - - "@fixtures/tanstack-start/react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], - "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], @@ -2091,8 +2031,6 @@ "@solidjs/start/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - "@solidjs/start/srvx": ["srvx@0.9.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-RZaxTKJEE/14HYn8COLuUOJAt0U55N9l1Xf6jj+T0GoA01EUH1Xz5JtSUOI+EHn+AEgPCVn7gk6jHJffrr06fQ=="], - "@solidjs/start/vite": ["vite@7.3.3", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA=="], "@solidjs/vite-plugin-nitro-2/vite": ["vite@7.3.3", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA=="], @@ -2129,6 +2067,8 @@ "@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], + "@tanstack/start-plugin-core/srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], + "@tanstack/start-plugin-core/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "@types/babel__core/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], @@ -2137,6 +2077,10 @@ "@vercel/nft/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], + + "@vitejs/plugin-rsc/srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], + "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "archiver-utils/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], @@ -2147,10 +2091,10 @@ "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="], - "h3/srvx": ["srvx@0.9.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-RZaxTKJEE/14HYn8COLuUOJAt0U55N9l1Xf6jj+T0GoA01EUH1Xz5JtSUOI+EHn+AEgPCVn7gk6jHJffrr06fQ=="], - "h3-v2/rou3": ["rou3@0.8.1", "", {}, "sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA=="], + "h3-v2/srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], + "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], "listhen/h3": ["h3@1.15.11", "", { "dependencies": { "cookie-es": "^1.2.3", "crossws": "^0.3.5", "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", "radix3": "^1.1.2", "ufo": "^1.6.3", "uncrypto": "^0.1.3" } }, "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg=="], @@ -2195,6 +2139,8 @@ "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + "tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "unctx/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], @@ -2307,8 +2253,6 @@ "@fixtures/tanstack-start/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@fixtures/tanstack-start/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], - "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "@solidjs/start/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], @@ -2367,8 +2311,6 @@ "@tanstack/directive-functions-plugin/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], - "@tanstack/directive-functions-plugin/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "@tanstack/directive-functions-plugin/@tanstack/router-utils/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], "@tanstack/directive-functions-plugin/@tanstack/router-utils/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], @@ -2377,12 +2319,8 @@ "@tanstack/server-functions-plugin/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], - "@tanstack/server-functions-plugin/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "@tanstack/start-plugin-core/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], - "@tanstack/start-plugin-core/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "archiver-utils/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], "archiver-utils/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], diff --git a/fixtures/react-router-rsc/README.md b/fixtures/react-router-rsc/README.md deleted file mode 100644 index 27e7f9c7..00000000 --- a/fixtures/react-router-rsc/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# rsc react-router - -https://vite-rsc-react-router.hiro18181.workers.dev - -> [!NOTE] -> React Router now provides [official RSC support](https://reactrouter.com/how-to/react-server-components) for Vite. The example might not be kept up to date with the latest version. Please refer to React Router's official documentation for the latest integrations. - -Vite RSC example based on demo made by React router team with Parcel: - -- https://github.com/jacob-ebey/parcel-plugin-react-router/ -- https://github.com/jacob-ebey/experimental-parcel-react-router-starter -- https://github.com/remix-run/react-router/tree/rsc/playground/rsc-vite - -See also [`rsc-movies`](https://github.com/hi-ogawa/rsc-movies/). - -[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vitejs/vite-plugin-react/tree/main/packages/plugin-rsc/examples/react-router?file=src%2Froutes%2Froot.tsx) - -Or try it locally by: - -```sh -npx giget gh:vitejs/vite-plugin-react/packages/plugin-rsc/examples/react-router my-app -cd my-app -npm i -npm run dev -npm run build -npm run preview - -# run on @cloudflare/vite-plugin and deploy. -# a separate configuration is found in ./cf/vite.config.ts -npm run cf-dev -npm run cf-build -npm run cf-preview -npm run cf-release -``` diff --git a/fixtures/react-router-rsc/app/paper.css b/fixtures/react-router-rsc/app/paper.css deleted file mode 100644 index 84d3e872..00000000 --- a/fixtures/react-router-rsc/app/paper.css +++ /dev/null @@ -1,150 +0,0 @@ -@theme { - --default-font-family: "Patrick Hand SC", sans-serif; - --default-mono-font-family: "Patrick Hand SC", sans-serif; - - --color-foreground: black; - --color-danger: rgb(167, 52, 45); - --color-secondary: rgb(11, 116, 213); - --color-success: rgb(134, 163, 97); - --color-warning: rgb(221, 205, 69); - --color-border: #cdcccb; - --color-border-active: rgba(0, 0, 0, 0.2); - - --color-paper-background: white; - --color-paper-border: #cdcccb; - --shadow-paper: -1px 5px 35px -9px rgba(0, 0, 0, 0.2); - - --shadow-btn: 15px 28px 25px -18px rgba(0, 0, 0, 0.2); - --shadow-btn-hover: 2px 8px 8px -5px rgba(0, 0, 0, 0.3); - --color-btn-border: black; - --btn-color-danger: var(--color-danger); - --btn-color-secondary: var(--color-secondary); - --btn-color-success: var(--color-success); - --btn-color-warning: var(--color-warning); -} - -@utility paper-border { - @apply border-2 border-border; - border-bottom-left-radius: 25px 115px; - border-bottom-right-radius: 155px 25px; - border-top-left-radius: 15px 225px; - border-top-right-radius: 25px 150px; -} - -@utility no-paper-border { - @apply border-0; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} - -@utility paper-underline { - @apply border-b-3 border-[currentcolor]; - border-bottom-left-radius: 15px 3px; - border-bottom-right-radius: 15px 5px; - border-bottom-style: solid; -} - -@utility paper-underline-hover { - @apply paper-underline border-transparent; - @variant hover { - @apply border-[currentcolor]; - } -} - -@utility paper { - @apply border border-paper-border bg-paper-background p-8 shadow-paper; -} - -@utility breadcrumbs { - @apply flex flex-wrap gap-2; - & > * { - @apply inline-block after:text-lg after:content-[""] not-last:after:ml-2 not-last:after:text-foreground not-last:after:content-["/"]; - } - & > a { - @apply text-secondary; - } -} - -@utility btn { - @apply inline-block cursor-pointer bg-paper-background paper-border px-4 py-2 text-lg shadow-btn transition-[shadow_transition]; - - @variant active { - @apply border-border-active; - } - @variant hover { - @apply translate-y-1 shadow-btn-hover; - } - - &.btn-icon { - @apply aspect-square px-2 py-2; - & img, - & svg { - @apply h-7 w-7; - } - } -} - -@utility btn-* { - border-color: --value(--btn-color-*); - color: --value(--btn-color-*); -} - -@utility btn-sm { - @apply px-2 py-1 text-base; -} - -@utility btn-lg { - @apply px-6 py-3 text-2xl; -} - -@utility label { - @apply mb-1 block font-semibold; -} - -@utility input { - @apply paper-border px-3 py-2; - - @variant disabled { - @apply border-border-active; - } -} - -@utility checkbox { - @apply h-6 w-6 paper-border; - - @variant disabled { - @apply border-border-active; - } -} - -@utility select { - @apply paper-border px-3 py-2; - - @variant disabled { - @apply border-border-active; - } -} - -@layer base { - body { - @apply text-foreground; - } - - * { - @apply outline-secondary; - } -} - -@layer utilities { - .prose { - :where(u):not(:where([class~="not-prose"], [class~="not-prose"] *)) { - @apply paper-underline no-underline; - } - - :where(a):not(:where([class~="not-prose"], [class~="not-prose"] *)) { - @apply paper-underline-hover no-underline text-secondary; - } - } -} diff --git a/fixtures/react-router-rsc/app/root.tsx b/fixtures/react-router-rsc/app/root.tsx deleted file mode 100644 index 678b3a48..00000000 --- a/fixtures/react-router-rsc/app/root.tsx +++ /dev/null @@ -1,55 +0,0 @@ -// oxlint-disable-next-line import/no-unassigned-import -import "./styles.css"; -import { Link, Outlet } from "react-router"; -import { TestClientState, TestHydrated } from "./routes/client"; -import { DumpError, GlobalNavigationLoadingBar } from "./routes/root.client"; - -export function Layout({ children }: { children: React.ReactNode }) { - console.log("[debug] root - Layout"); - return ( - - - - - React Router Vite - - -
- -
- - {children} - - - ); -} - -export default function Component() { - console.log("[debug] root - Component"); - return ( - <> - - - ); -} - -export function ErrorBoundary() { - return ; -} diff --git a/fixtures/react-router-rsc/app/routes.ts b/fixtures/react-router-rsc/app/routes.ts deleted file mode 100644 index 5a2907fb..00000000 --- a/fixtures/react-router-rsc/app/routes.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { unstable_RSCRouteConfigEntry } from "react-router"; - -export const routes: Array = [ - { - id: "root", - path: "", - lazy: () => import("./root"), - children: [ - { - id: "home", - index: true, - lazy: () => import("./routes/home"), - }, - { - id: "about", - path: "about", - lazy: () => import("./routes/about"), - }, - ], - }, -]; diff --git a/fixtures/react-router-rsc/app/routes/about.tsx b/fixtures/react-router-rsc/app/routes/about.tsx deleted file mode 100644 index 583f190f..00000000 --- a/fixtures/react-router-rsc/app/routes/about.tsx +++ /dev/null @@ -1,20 +0,0 @@ -"use client"; - -import React from "react"; - -export function Component() { - const [count, setCount] = React.useState(0); - - return ( -
-
-

About

-

This is the about page.

-

[test-style-home]

- -
-
- ); -} diff --git a/fixtures/react-router-rsc/app/routes/client.tsx b/fixtures/react-router-rsc/app/routes/client.tsx deleted file mode 100644 index 8c23e84c..00000000 --- a/fixtures/react-router-rsc/app/routes/client.tsx +++ /dev/null @@ -1,16 +0,0 @@ -"use client"; - -import React from "react"; - -export function TestHydrated() { - const hydrated = React.useSyncExternalStore( - React.useCallback(() => () => {}, []), - () => true, - () => false, - ); - return [hydrated: {hydrated ? 1 : 0}]; -} - -export function TestClientState() { - return ; -} diff --git a/fixtures/react-router-rsc/app/routes/home.actions.ts b/fixtures/react-router-rsc/app/routes/home.actions.ts deleted file mode 100644 index ece1e13a..00000000 --- a/fixtures/react-router-rsc/app/routes/home.actions.ts +++ /dev/null @@ -1,7 +0,0 @@ -"use server"; - -export async function sayHello(defaultName: string, formData: FormData) { - await new Promise((resolve) => setTimeout(resolve, 500)); - const name = formData.get("name") || defaultName; - console.log(`[debug] sayHello - ${name}`); -} diff --git a/fixtures/react-router-rsc/app/routes/home.client.tsx b/fixtures/react-router-rsc/app/routes/home.client.tsx deleted file mode 100644 index 8f2c4fad..00000000 --- a/fixtures/react-router-rsc/app/routes/home.client.tsx +++ /dev/null @@ -1,12 +0,0 @@ -"use client"; - -import { useFormStatus } from "react-dom"; - -export function PendingButton() { - const status = useFormStatus(); - return ( - - ); -} diff --git a/fixtures/react-router-rsc/app/routes/home.css b/fixtures/react-router-rsc/app/routes/home.css deleted file mode 100644 index 7204e2fd..00000000 --- a/fixtures/react-router-rsc/app/routes/home.css +++ /dev/null @@ -1,3 +0,0 @@ -.test-style-home { - color: rgb(250, 150, 0); -} diff --git a/fixtures/react-router-rsc/app/routes/home.tsx b/fixtures/react-router-rsc/app/routes/home.tsx deleted file mode 100644 index f82ecd77..00000000 --- a/fixtures/react-router-rsc/app/routes/home.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { sayHello } from "./home.actions.ts"; -import { PendingButton } from "./home.client.tsx"; -// oxlint-disable-next-line import/no-unassigned-import -import "./home.css"; -import { TestActionStateServer } from "./test-action-state/server.tsx"; - -const Component = () => { - return ( -
-
-

Home

-

This is the home page.

- [test-style-home] -

Server Action

-
-
- - -
-
- -
-
-
- -
-
-
- ); -}; - -export default Component; diff --git a/fixtures/react-router-rsc/app/routes/root.client.tsx b/fixtures/react-router-rsc/app/routes/root.client.tsx deleted file mode 100644 index 8729caa0..00000000 --- a/fixtures/react-router-rsc/app/routes/root.client.tsx +++ /dev/null @@ -1,44 +0,0 @@ -"use client"; - -import { useNavigation, useRouteError } from "react-router"; - -export function GlobalNavigationLoadingBar() { - const navigation = useNavigation(); - - if (navigation.state === "idle") return null; - - return ( -
-
-
- ); -} - -export function DumpError() { - const error = useRouteError(); - const message = - error instanceof Error ? ( -
-
-          {JSON.stringify(
-            {
-              ...error,
-              name: error.name,
-              message: error.message,
-            },
-            null,
-            2,
-          )}
-        
- {error.stack &&
{error.stack}
} -
- ) : ( -
Unknown Error
- ); - return ( - <> -

Oooops

-
{message}
- - ); -} diff --git a/fixtures/react-router-rsc/app/routes/test-action-state/client.tsx b/fixtures/react-router-rsc/app/routes/test-action-state/client.tsx deleted file mode 100644 index be67c28b..00000000 --- a/fixtures/react-router-rsc/app/routes/test-action-state/client.tsx +++ /dev/null @@ -1,16 +0,0 @@ -"use client"; - -import React from "react"; - -export function TestActionStateClient(props: { - action: (prev: React.ReactNode) => Promise; -}) { - const [state, formAction, isPending] = React.useActionState(props.action, null); - - return ( -
- - {isPending ? "pending..." : state} -
- ); -} diff --git a/fixtures/react-router-rsc/app/routes/test-action-state/server.tsx b/fixtures/react-router-rsc/app/routes/test-action-state/server.tsx deleted file mode 100644 index 7044c2e9..00000000 --- a/fixtures/react-router-rsc/app/routes/test-action-state/server.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { TestActionStateClient } from "./client"; - -// Test case based on -// https://github.com/remix-run/react-router/issues/13882 - -export function TestActionStateServer({ message }: { message: string }) { - return ( - { - "use server"; - await new Promise((resolve) => setTimeout(resolve, 200)); - return ( - - [(ok) ({message})] {prev} - - ); - }} - /> - ); -} diff --git a/fixtures/react-router-rsc/app/styles.css b/fixtures/react-router-rsc/app/styles.css deleted file mode 100644 index c66d1648..00000000 --- a/fixtures/react-router-rsc/app/styles.css +++ /dev/null @@ -1,32 +0,0 @@ -@import "tailwindcss"; -@plugin "@tailwindcss/typography"; - -@import "./paper.css"; - -@theme { - --animate-progress: progress 1s infinite linear; - - @keyframes progress { - 0% { - transform: translateX(0) scaleX(0); - } - 40% { - transform: translateX(0) scaleX(0.4); - } - 100% { - transform: translateX(100%) scaleX(0.5); - } - } -} - -@utility vt-name { - view-transition-name: var(--vt-name); -} - -@utility no-vt { - view-transition-name: none; -} - -@view-transition { - navigation: auto; -} diff --git a/fixtures/react-router-rsc/package.json b/fixtures/react-router-rsc/package.json deleted file mode 100644 index c8f30999..00000000 --- a/fixtures/react-router-rsc/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "@fixtures/react-router-rsc", - "private": true, - "license": "MIT", - "type": "module", - "scripts": { - "dev": "vite dev --port 3200", - "build": "vite build", - "preview": "vite preview", - "test": "bun test" - }, - "dependencies": { - "react": "^19.2.7", - "react-dom": "^19.2.7", - "react-router": "7.16.0" - }, - "devDependencies": { - "@cloudflare/workers-types": "catalog:workers", - "@distilled.cloud/cloudflare-runtime": "workspace:*", - "@distilled.cloud/cloudflare-vite-plugin": "workspace:*", - "@tailwindcss/typography": "^0.5.19", - "@tailwindcss/vite": "^4.3.0", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "latest", - "@vitejs/plugin-rsc": "latest", - "tailwindcss": "^4.3.0", - "vite": "catalog:" - } -} diff --git a/fixtures/react-router-rsc/public/favicon.ico b/fixtures/react-router-rsc/public/favicon.ico deleted file mode 100644 index 5dbdfcddcb14182535f6d32d1c900681321b1aa3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 zcmeI33v3ic7{|AFEmuJ-;v>ep_G*NPi6KM`qNryCe1PIJ8siIN1WZ(7qVa)RVtmC% z)Ch?tN+afMKm;5@rvorJk zcXnoOc4q51HBQnQH_jn!cAg&XI1?PlX>Kl^k8qq0;zkha`kY$Fxt#=KNJAE9CMdpW zqr4#g8`nTw191(+H4xW8Tmyru2I^3=J1G3emPxkPXA=3{vvuvse_WWSshqaqls^-m zgB7q8&Vk*aYRe?sn$n53dGH#%3y%^vxv{pL*-h0Z4bmb_(k6{FL7HWIz(V*HT#IcS z-wE{)+0x1U!RUPt3gB97%p}@oHxF4|6S*+Yw=_tLtxZ~`S=z6J?O^AfU>7qOX`JNBbV&8+bO0%@fhQitKIJ^O^ zpgIa__qD_y07t@DFlBJ)8SP_#^j{6jpaXt{U%=dx!qu=4u7^21lWEYHPPY5U3TcoQ zX_7W+lvZi>TapNk_X>k-KO%MC9iZp>1E`N34gHKd9tK&){jq2~7OsJ>!G0FzxQFw6G zm&Vb(2#-T|rM|n3>uAsG_hnbvUKFf3#ay@u4uTzia~NY%XgCHfx4^To4BDU@)HlV? z@EN=g^ymETa1sQK{kRwyE4Ax8?wT&GvaG@ASO}{&a17&^v`y z!oPdiSiia^oov(Z)QhG2&|FgE{M9_4hJROGbnj>#$~ZF$-G^|zPj*QApltKe?;u;uKHJ~-V!=VLkg7Kgct)l7u39f@%VG8e3f$N-B zAu3a4%ZGf)r+jPAYCSLt73m_J3}p>}6Tx0j(wg4vvKhP!DzgiWANiE;Ppvp}P2W@m z-VbYn+NXFF?6ngef5CfY6ZwKnWvNV4z6s^~yMXw2i5mv}jC$6$46g?G|CPAu{W5qF zDobS=zb2ILX9D827g*NtGe5w;>frjanY{f)hrBP_2ehBt1?`~ypvg_Ot4x1V+43P@Ve8>qd)9NX_jWdLo`Zfy zoeam9)@Dpym{4m@+LNxXBPjPKA7{3a&H+~xQvr>C_A;7=JrfK~$M2pCh>|xLz>W6SCs4qC|#V`)# z)0C|?$o>jzh<|-cpf

K7osU{Xp5PG4-K+L2G=)c3f&}H&M3wo7TlO_UJjQ-Oq&_ zjAc9=nNIYz{c3zxOiS5UfcE1}8#iI4@uy;$Q7>}u`j+OU0N<*Ezx$k{x_27+{s2Eg z`^=rhtIzCm!_UcJ?Db~Lh-=_))PT3{Q0{Mwdq;0>ZL%l3+;B&4!&xm#%HYAK|;b456Iv&&f$VQHf` z>$*K9w8T+paVwc7fLfMlhQ4)*zL_SG{~v4QR;IuX-(oRtYAhWOlh`NLoX0k$RUYMi z2Y!bqpdN}wz8q`-%>&Le@q|jFw92ErW-hma-le?S z-@OZt2EEUm4wLsuEMkt4zlyy29_3S50JAcQHTtgTC{P~%-mvCTzrjXOc|{}N`Cz`W zSj7CrXfa7lcsU0J(0uSX6G`54t^7}+OLM0n(|g4waOQ}bd3%!XLh?NX9|8G_|06Ie zD5F1)w5I~!et7lA{G^;uf7aqT`KE&2qx9|~O;s6t!gb`+zVLJyT2T)l*8l(j diff --git a/fixtures/react-router-rsc/react-router-vite/entry.browser.tsx b/fixtures/react-router-rsc/react-router-vite/entry.browser.tsx deleted file mode 100644 index f1c3b792..00000000 --- a/fixtures/react-router-rsc/react-router-vite/entry.browser.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { - createFromReadableStream, - createTemporaryReferenceSet, - encodeReply, - setServerCallback, -} from "@vitejs/plugin-rsc/browser"; -import { startTransition, StrictMode } from "react"; -import { hydrateRoot } from "react-dom/client"; -import type { DataRouter, unstable_RSCPayload as RSCServerPayload } from "react-router"; -import { - unstable_createCallServer as createCallServer, - unstable_getRSCStream as getRSCStream, - unstable_RSCHydratedRouter as RSCHydratedRouter, -} from "react-router/dom"; - -// Create and set the callServer function to support post-hydration server actions. -setServerCallback( - createCallServer({ - createFromReadableStream, - createTemporaryReferenceSet, - encodeReply, - }), -); - -// Get and decode the initial server payload -createFromReadableStream(getRSCStream()).then((payload) => { - startTransition(async () => { - const formState = payload.type === "render" ? await payload.formState : undefined; - - hydrateRoot( - document, - - - , - { - // @ts-expect-error - no types for this yet - formState, - }, - ); - }); -}); - -declare let __reactRouterDataRouter: DataRouter; - -if (import.meta.hot) { - import.meta.hot.on("rsc:update", () => { - __reactRouterDataRouter.revalidate(); - }); -} diff --git a/fixtures/react-router-rsc/react-router-vite/entry.rsc.single.tsx b/fixtures/react-router-rsc/react-router-vite/entry.rsc.single.tsx deleted file mode 100644 index 5a21ed4f..00000000 --- a/fixtures/react-router-rsc/react-router-vite/entry.rsc.single.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import type * as EntrySsr from "./entry.ssr"; -import { fetchServer } from "./entry.rsc"; - -export default async function handler(request: Request) { - const ssr = await import.meta.viteRsc.loadModule("ssr", "index"); - - return ssr.default(request, await fetchServer(request)); -} diff --git a/fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx b/fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx deleted file mode 100644 index 5d3d27fa..00000000 --- a/fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { - createTemporaryReferenceSet, - decodeAction, - decodeFormState, - decodeReply, - loadServerAction, - renderToReadableStream, -} from "@vitejs/plugin-rsc/rsc"; -import { unstable_matchRSCServerRequest as matchRSCServerRequest } from "react-router"; -import { routes } from "../app/routes"; - -export function fetchServer(request: Request) { - return matchRSCServerRequest({ - // Provide the React Server touchpoints. - createTemporaryReferenceSet, - decodeAction, - decodeFormState, - decodeReply, - loadServerAction, - // The incoming request. - request, - // The app routes. - routes, - // Encode the match with the React Server implementation. - generateResponse(match, options) { - return new Response(renderToReadableStream(match.payload, options), { - status: match.statusCode, - headers: match.headers, - }); - }, - }); -} - -if (import.meta.hot) { - import.meta.hot.accept(); -} diff --git a/fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx b/fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx deleted file mode 100644 index d4559b37..00000000 --- a/fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr"; -import { renderToReadableStream as renderHTMLToReadableStream } from "react-dom/server.edge"; -import { - unstable_routeRSCServerRequest as routeRSCServerRequest, - unstable_RSCStaticRouter as RSCStaticRouter, -} from "react-router"; - -export default async function handler( - request: Request, - serverResponse: Response, -): Promise { - const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent("index"); - - return await routeRSCServerRequest({ - request, - serverResponse, - createFromReadableStream, - async renderHTML(getPayload, options) { - const payload = getPayload(); - - return await renderHTMLToReadableStream(, { - ...options, - bootstrapScriptContent, - signal: request.signal, - formState: await payload.formState, - }); - }, - }); -} diff --git a/fixtures/react-router-rsc/react-router-vite/entry.worker.tsx b/fixtures/react-router-rsc/react-router-vite/entry.worker.tsx deleted file mode 100644 index cc1f54c5..00000000 --- a/fixtures/react-router-rsc/react-router-vite/entry.worker.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type * as WorkerSsr from "./worker-ssr"; -import handler from "./entry.rsc.single"; - -// The distilled Cloudflare worker wrapper expects a `{ fetch }` default export; -// the RSC single-worker handler is a bare (request) => Response function. -export default { - async fetch(request: Request): Promise { - const url = new URL(request.url); - - // Worker code that needs a non-`react-server` module (here `react-dom/server`) - // must not import it directly in this `rsc` entry — it loads it from the - // `ssr` environment via `loadModule`. Exercises a custom (non-`index`) ssr - // input + cross-environment load through the distilled plugin. - if (url.pathname === "/worker-render") { - const { renderWorkerHtml } = await import.meta.viteRsc.loadModule( - "ssr", - "worker-ssr", - ); - return Response.json({ ok: true, html: renderWorkerHtml() }); - } - - return handler(request); - }, -}; diff --git a/fixtures/react-router-rsc/react-router-vite/types.d.ts b/fixtures/react-router-rsc/react-router-vite/types.d.ts deleted file mode 100644 index bb5578e1..00000000 --- a/fixtures/react-router-rsc/react-router-vite/types.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// -/// diff --git a/fixtures/react-router-rsc/react-router-vite/worker-ssr.tsx b/fixtures/react-router-rsc/react-router-vite/worker-ssr.tsx deleted file mode 100644 index 00880f6c..00000000 --- a/fixtures/react-router-rsc/react-router-vite/worker-ssr.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { createElement } from "react"; -import { renderToStaticMarkup } from "react-dom/server.edge"; - -// Lives in the `ssr` environment (no `react-server` condition), so it can use -// `react-dom/server` — which would fail if imported directly in the worker's -// `rsc` entry. The worker reaches it via `loadModule("ssr", "worker-ssr")`. -// This is the pattern James Opstad landed on in -// github.com/agcty/vite-rsc-worker-env-repro PR #1. -export function renderWorkerHtml(): string { - return renderToStaticMarkup( - createElement("section", null, "Worker render via the ssr environment."), - ); -} diff --git a/fixtures/react-router-rsc/test/build.test.ts b/fixtures/react-router-rsc/test/build.test.ts deleted file mode 100644 index 93509462..00000000 --- a/fixtures/react-router-rsc/test/build.test.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { beforeAll, expect, test } from "bun:test"; -import { spawnSync } from "node:child_process"; -import * as fs from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -// Production-build smoke test for the distilled build manifest. Builds the -// fixture and asserts the emitted `__distilled-build.json` describes a complete, -// self-contained worker module set — the contract a deployer consumes. -const fixtureDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); -const distDir = path.join(fixtureDir, "dist"); - -type Manifest = { - version: number; - workers: { - app: { - main: string; - modules: Array<{ path: string; type: string }>; - compatibilityDate?: string; - compatibilityFlags?: Array; - }; - }; - assets?: { directory: string }; -}; -let manifest: Manifest; - -beforeAll(() => { - fs.rmSync(distDir, { recursive: true, force: true }); - const result = spawnSync("bun", ["vite", "build"], { cwd: fixtureDir, encoding: "utf8" }); - if (result.status !== 0) { - throw new Error(`vite build failed (${result.status}):\n${result.stdout}\n${result.stderr}`); - } - manifest = JSON.parse(fs.readFileSync(path.join(distDir, "__distilled-build.json"), "utf8")); -}, 120_000); - -test("emits a build manifest describing the worker entry and assets", () => { - const worker = manifest.workers.app; - - expect(manifest.version).toBe(2); - // The framework also emits `server/index.js`; the main entry must be the - // distilled worker-entry wrapper captured from the real write pass. - expect(worker.main).toBe("server/entry.worker.js"); - expect(worker.modules).toContainEqual({ path: worker.main, type: "esm" }); - expect(worker.modules.length).toBeGreaterThan(0); - expect(worker.modules.every((module) => module.path && module.type)).toBe(true); - expect(manifest.assets?.directory).toBe("client"); - expect(worker.compatibilityDate).toBe("2026-03-10"); - expect(worker.compatibilityFlags).toContain("nodejs_compat"); -}); - -test("folds the worker-loaded ssr output into the worker module set", () => { - const modulePaths = manifest.workers.app.modules.map((module) => module.path); - - // The single-worker RSC topology loads the `ssr` env at runtime via - // loadModule (`import("../../ssr/...")`), so its output must ship as part of - // the same worker — both the entry (`server/`) and child (`ssr/`) outputs. - expect(modulePaths.some((module) => module.startsWith("server/"))).toBe(true); - expect(modulePaths.some((module) => module.startsWith("ssr/"))).toBe(true); - expect(modulePaths).toContain("ssr/worker-ssr.js"); -}); - -test("worker module entries are sorted and unique", () => { - const modulePaths = manifest.workers.app.modules.map((module) => module.path); - - expect(modulePaths).toEqual([...modulePaths].sort((a, b) => a.localeCompare(b))); - expect(new Set(modulePaths).size).toBe(modulePaths.length); -}); - -test("worker module set is self-contained (every relative import resolves)", () => { - const moduleSet = new Set(manifest.workers.app.modules.map((module) => module.path)); - const transpiler = new Bun.Transpiler({ loader: "js" }); - const unresolved: Array = []; - for (const module of manifest.workers.app.modules) { - if (module.type !== "esm") continue; - for (const imported of transpiler.scanImports( - fs.readFileSync(path.join(distDir, module.path), "utf8"), - )) { - if (!imported.path.startsWith(".")) continue; - const resolved = path.posix.normalize( - path.posix.join(path.posix.dirname(module.path), imported.path), - ); - if (!moduleSet.has(resolved)) unresolved.push(`${module.path} -> ${imported.path}`); - } - } - expect(unresolved).toEqual([]); -}); - -test("client assets are not part of the worker module set", () => { - expect(manifest.workers.app.modules.some((module) => module.path.startsWith("client/"))).toBe( - false, - ); -}); - -test("a custom RSC outDir split removes stale manifest and emits no broken graph", () => { - const customDistDir = path.join(fixtureDir, "dist-custom"); - const customManifestPath = path.join(customDistDir, "__distilled-build.json"); - try { - fs.rmSync(customDistDir, { recursive: true, force: true }); - fs.mkdirSync(customDistDir, { recursive: true }); - fs.writeFileSync( - customManifestPath, - `${JSON.stringify({ - version: 2, - workers: { - app: { - main: "server/stale.js", - modules: [{ path: "server/stale.js", type: "esm" }], - }, - }, - })}\n`, - ); - - const result = spawnSync("bun", ["vite", "build", "--outDir", "dist-custom"], { - cwd: fixtureDir, - encoding: "utf8", - }); - if (result.status !== 0) { - throw new Error(`vite build failed (${result.status}):\n${result.stdout}\n${result.stderr}`); - } - - expect(result.stderr).toContain(`skipping __distilled-build.json`); - expect(fs.existsSync(customManifestPath)).toBe(false); - } finally { - fs.rmSync(customDistDir, { recursive: true, force: true }); - } -}, 120_000); - -// Must run last: it triggers a second build that rewrites `dist`. -test("a rebuild drops stale worker files left in the output", () => { - const stale = path.join(distDir, "server", "STALE_REVIEW_MARKER.js"); - fs.writeFileSync(stale, "// stale\n"); - const result = spawnSync("bun", ["vite", "build"], { cwd: fixtureDir, encoding: "utf8" }); - if (result.status !== 0) { - throw new Error(`vite build failed (${result.status}):\n${result.stdout}\n${result.stderr}`); - } - const rebuilt: Manifest = JSON.parse( - fs.readFileSync(path.join(distDir, "__distilled-build.json"), "utf8"), - ); - expect(rebuilt.workers.app.modules.map((module) => module.path)).not.toContain( - "server/STALE_REVIEW_MARKER.js", - ); - expect(fs.existsSync(stale)).toBe(false); -}, 120_000); diff --git a/fixtures/react-router-rsc/test/dev.test.ts b/fixtures/react-router-rsc/test/dev.test.ts deleted file mode 100644 index 434ceb6f..00000000 --- a/fixtures/react-router-rsc/test/dev.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { afterAll, beforeAll, expect, test } from "bun:test"; -import { spawn, type ChildProcess } from "node:child_process"; -import { fileURLToPath } from "node:url"; -import path from "node:path"; - -// Dev-mode smoke test: boots `vite dev` (the distilled Cloudflare plugin in -// RSC mode) and asserts the RSC routes, routing, and the worker-loaded ssr -// render all respond. Build-mode tests are intentionally absent — RSC build -// is a separate track. -const fixtureDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); - -let proc: ChildProcess; -let baseUrl: string; - -beforeAll(async () => { - proc = spawn("bun", ["vite", "dev", "--port", "3251"], { - cwd: fixtureDir, - stdio: ["ignore", "pipe", "pipe"], - }); - baseUrl = await new Promise((resolve, reject) => { - const timer = setTimeout(() => reject(new Error("dev server did not start in time")), 90_000); - const onData = (chunk: Buffer) => { - const match = String(chunk).match(/Local:\s+(http:\/\/\S+?)\/?\s/); - if (match) { - clearTimeout(timer); - resolve(match[1].replace(/\/$/, "")); - } - }; - proc.stdout?.on("data", onData); - proc.stderr?.on("data", onData); - proc.on("exit", (code) => reject(new Error(`dev server exited early (code ${code})`))); - }); -}); - -afterAll(() => { - proc?.kill("SIGTERM"); -}); - -test("renders the RSC home route", async () => { - const res = await fetch(`${baseUrl}/`); - expect(res.status).toBe(200); - expect(await res.text()).toContain("React Router Vite"); -}); - -test("routes to /about", async () => { - const res = await fetch(`${baseUrl}/about`); - expect(res.status).toBe(200); - expect(await res.text()).toContain("About"); -}); - -test("worker loads a custom ssr module via loadModule (react-dom/server in ssr env)", async () => { - const res = await fetch(`${baseUrl}/worker-render`); - expect(res.status).toBe(200); - const body = (await res.json()) as { ok: boolean; html: string }; - expect(body.ok).toBe(true); - expect(body.html).toContain("Worker render via the ssr environment."); -}); diff --git a/fixtures/react-router-rsc/tsconfig.json b/fixtures/react-router-rsc/tsconfig.json deleted file mode 100644 index c9478645..00000000 --- a/fixtures/react-router-rsc/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "compilerOptions": { - "allowImportingTsExtensions": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "noEmit": true, - "moduleResolution": "Bundler", - "module": "ESNext", - "target": "ESNext", - "lib": ["ESNext", "DOM"], - "jsx": "react-jsx", - "types": ["vite/client", "bun"] - } -} diff --git a/fixtures/react-router-rsc/vite.config.ts b/fixtures/react-router-rsc/vite.config.ts deleted file mode 100644 index 5b0978a3..00000000 --- a/fixtures/react-router-rsc/vite.config.ts +++ /dev/null @@ -1,60 +0,0 @@ -import cloudflare from "@distilled.cloud/cloudflare-vite-plugin"; -import tailwindcss from "@tailwindcss/vite"; -import react from "@vitejs/plugin-react"; -import rsc from "@vitejs/plugin-rsc"; -import { defineConfig } from "vite"; - -// React Router (hand-rolled on @vitejs/plugin-rsc) wired to the distilled -// Cloudflare vite plugin via the single-worker child-environment model — -// the same topology vermittelbar uses with the official plugin -// (`viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] }`). The worker -// IS the `rsc` env; its handler loads `ssr` at runtime via -// `import.meta.viteRsc.loadModule("ssr", ...)`. -export default defineConfig({ - clearScreen: false, - build: { minify: false }, - plugins: [ - tailwindcss(), - { - // Workaround for https://github.com/tailwindlabs/tailwindcss/pull/19670 - name: "fix-tailwind-full-reload", - configResolved(config) { - const plugin = config.plugins.find((p) => p.name === "@tailwindcss/vite:generate:serve"); - delete plugin?.hotUpdate; - }, - }, - react(), - rsc({ - serverHandler: false, - entries: { - client: "./react-router-vite/entry.browser.tsx", - ssr: "./react-router-vite/entry.ssr.tsx", - rsc: "./react-router-vite/entry.worker.tsx", - }, - }), - cloudflare({ - main: "./react-router-vite/entry.worker.tsx", - compatibilityDate: "2026-03-10", - compatibilityFlags: ["nodejs_compat"], - viteEnvironments: { entry: "rsc", children: ["ssr"] }, - worker: { name: "fixtures-react-router-rsc" }, - onBuildComplete: (result) => { - console.log(result); - }, - }), - ], - environments: { - // A second `ssr` input the worker loads on demand via - // loadModule("ssr", "worker-ssr") — alongside the framework's `index`. - ssr: { - build: { - rollupOptions: { - input: { "worker-ssr": "./react-router-vite/worker-ssr.tsx" }, - }, - }, - }, - }, - optimizeDeps: { - include: ["react-router", "react-router/internal/react-server-client"], - }, -}); diff --git a/fixtures/react-rsc/README.md b/fixtures/react-rsc/README.md deleted file mode 100644 index cd111710..00000000 --- a/fixtures/react-rsc/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Vite + RSC - -This example shows how to set up a React application with [Server Component](https://react.dev/reference/rsc/server-components) features on Vite using [`@vitejs/plugin-rsc`](https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-rsc). - -[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vitejs/vite-plugin-react/tree/main/packages/plugin-rsc/examples/starter) - -```sh -# run dev server -npm run dev - -# build for production and preview -npm run build -npm run preview -``` - -## API usage - -See [`@vitejs/plugin-rsc`](https://github.com/vitejs/vite-plugin-react/tree/main/packages/plugin-rsc) for the documentation. - -- [`vite.config.ts`](./vite.config.ts) - - `@vitejs/plugin-rsc/plugin` -- [`./src/framework/entry.rsc.tsx`](./src/framework/entry.rsc.tsx) - - `@vitejs/plugin-rsc/rsc` - - `import.meta.viteRsc.loadModule` -- [`./src/framework/entry.ssr.tsx`](./src/framework/entry.ssr.tsx) - - `@vitejs/plugin-rsc/ssr` - - `import.meta.viteRsc.loadBootstrapScriptContent` - - `rsc-html-stream/server` -- [`./src/framework/entry.browser.tsx`](./src/framework/entry.browser.tsx) - - `@vitejs/plugin-rsc/browser` - - `rsc-html-stream/client` - -## Notes - -- [`./src/framework/entry.{browser,rsc,ssr}.tsx`](./src/framework) (with inline comments) provides an overview of how low level RSC (React flight) API can be used to build RSC framework. -- You can use [`vite-plugin-inspect`](https://github.com/antfu-collective/vite-plugin-inspect) to understand how `"use client"` and `"use server"` directives are transformed internally. - -## Deployment - -See [vite-plugin-rsc-deploy-example](https://github.com/hi-ogawa/vite-plugin-rsc-deploy-example) diff --git a/fixtures/react-rsc/package.json b/fixtures/react-rsc/package.json deleted file mode 100644 index e98daaf8..00000000 --- a/fixtures/react-rsc/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "@fixtures/react-rsc", - "version": "0.0.0", - "private": true, - "license": "MIT", - "type": "module", - "scripts": { - "dev": "vite dev --port 3100", - "build": "vite build", - "preview": "vite preview", - "test": "bun test" - }, - "dependencies": { - "react": "^19.2.7", - "react-dom": "^19.2.7" - }, - "devDependencies": { - "@cloudflare/workers-types": "catalog:workers", - "@distilled.cloud/cloudflare-runtime": "workspace:*", - "@distilled.cloud/cloudflare-vite-plugin": "workspace:*", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "latest", - "@vitejs/plugin-rsc": "latest", - "rsc-html-stream": "^0.0.7", - "vite": "catalog:" - } -} diff --git a/fixtures/react-rsc/public/vite.svg b/fixtures/react-rsc/public/vite.svg deleted file mode 100644 index e7b8dfb1..00000000 --- a/fixtures/react-rsc/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/fixtures/react-rsc/src/action.tsx b/fixtures/react-rsc/src/action.tsx deleted file mode 100644 index 6b5029dc..00000000 --- a/fixtures/react-rsc/src/action.tsx +++ /dev/null @@ -1,11 +0,0 @@ -"use server"; - -let serverCounter = 0; - -export async function getServerCounter() { - return serverCounter; -} - -export async function updateServerCounter(change: number) { - serverCounter += change; -} diff --git a/fixtures/react-rsc/src/assets/react.svg b/fixtures/react-rsc/src/assets/react.svg deleted file mode 100644 index 6c87de9b..00000000 --- a/fixtures/react-rsc/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/fixtures/react-rsc/src/client.tsx b/fixtures/react-rsc/src/client.tsx deleted file mode 100644 index ac69d863..00000000 --- a/fixtures/react-rsc/src/client.tsx +++ /dev/null @@ -1,9 +0,0 @@ -"use client"; - -import React from "react"; - -export function ClientCounter() { - const [count, setCount] = React.useState(0); - - return ; -} diff --git a/fixtures/react-rsc/src/framework/entry.browser.tsx b/fixtures/react-rsc/src/framework/entry.browser.tsx deleted file mode 100644 index 5e2c2031..00000000 --- a/fixtures/react-rsc/src/framework/entry.browser.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { - createFromReadableStream, - createFromFetch, - setServerCallback, - createTemporaryReferenceSet, - encodeReply, -} from "@vitejs/plugin-rsc/browser"; -import React from "react"; -import { createRoot, hydrateRoot } from "react-dom/client"; -import { rscStream } from "rsc-html-stream/client"; -import type { RscPayload } from "./entry.rsc"; -import { GlobalErrorBoundary } from "./error-boundary"; -import { createRscRenderRequest } from "./request"; - -async function main() { - // stash `setPayload` function to trigger re-rendering - // from outside of `BrowserRoot` component (e.g. server function call, navigation, hmr) - let setPayload: (v: RscPayload) => void; - - // deserialize RSC stream back to React VDOM for CSR - const initialPayload = await createFromReadableStream( - // initial RSC stream is injected in SSR stream as - rscStream, - ); - - // browser root component to (re-)render RSC payload as state - function BrowserRoot() { - const [payload, setPayload_] = React.useState(initialPayload); - - React.useEffect(() => { - setPayload = (v) => React.startTransition(() => setPayload_(v)); - }, [setPayload_]); - - // re-fetch/render on client side navigation - React.useEffect(() => { - return listenNavigation(() => fetchRscPayload()); - }, []); - - return payload.root; - } - - // re-fetch RSC and trigger re-rendering - async function fetchRscPayload() { - const renderRequest = createRscRenderRequest(window.location.href); - const payload = await createFromFetch(fetch(renderRequest)); - setPayload(payload); - } - - // register a handler which will be internally called by React - // on server function request after hydration. - setServerCallback(async (id, args) => { - const temporaryReferences = createTemporaryReferenceSet(); - const renderRequest = createRscRenderRequest(window.location.href, { - id, - body: await encodeReply(args, { temporaryReferences }), - }); - const payload = await createFromFetch(fetch(renderRequest), { - temporaryReferences, - }); - setPayload(payload); - const { ok, data } = payload.returnValue!; - if (!ok) throw data; - return data; - }); - - // hydration - const browserRoot = ( - - - - - - ); - if ("__NO_HYDRATE" in globalThis) { - createRoot(document).render(browserRoot); - } else { - hydrateRoot(document, browserRoot, { - formState: initialPayload.formState, - }); - } - - // implement server HMR by triggering re-fetch/render of RSC upon server code change - if (import.meta.hot) { - import.meta.hot.on("rsc:update", () => { - fetchRscPayload(); - }); - } -} - -// a little helper to setup events interception for client side navigation -function listenNavigation(onNavigation: () => void) { - window.addEventListener("popstate", onNavigation); - - const oldPushState = window.history.pushState; - window.history.pushState = function (...args) { - const res = oldPushState.apply(this, args); - onNavigation(); - return res; - }; - - const oldReplaceState = window.history.replaceState; - window.history.replaceState = function (...args) { - const res = oldReplaceState.apply(this, args); - onNavigation(); - return res; - }; - - function onClick(e: MouseEvent) { - let link = (e.target as Element).closest("a"); - if ( - link && - link instanceof HTMLAnchorElement && - link.href && - (!link.target || link.target === "_self") && - link.origin === location.origin && - !link.hasAttribute("download") && - e.button === 0 && // left clicks only - !e.metaKey && // open in new tab (mac) - !e.ctrlKey && // open in new tab (windows) - !e.altKey && // download - !e.shiftKey && - !e.defaultPrevented - ) { - e.preventDefault(); - history.pushState(null, "", link.href); - } - } - document.addEventListener("click", onClick); - - return () => { - document.removeEventListener("click", onClick); - window.removeEventListener("popstate", onNavigation); - window.history.pushState = oldPushState; - window.history.replaceState = oldReplaceState; - }; -} - -main(); diff --git a/fixtures/react-rsc/src/framework/entry.rsc.tsx b/fixtures/react-rsc/src/framework/entry.rsc.tsx deleted file mode 100644 index 68f4eb94..00000000 --- a/fixtures/react-rsc/src/framework/entry.rsc.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { - renderToReadableStream, - createTemporaryReferenceSet, - decodeReply, - loadServerAction, - decodeAction, - decodeFormState, -} from "@vitejs/plugin-rsc/rsc"; -import type { ReactFormState } from "react-dom/client"; -import type * as EntrySsr from "./entry.ssr.tsx"; -import { Root } from "../root.tsx"; -import { parseRenderRequest } from "./request.tsx"; - -// The schema of payload which is serialized into RSC stream on rsc environment -// and deserialized on ssr/client environments. -export type RscPayload = { - // this demo renders/serializes/deserizlies entire root html element - // but this mechanism can be changed to render/fetch different parts of components - // based on your own route conventions. - root: React.ReactNode; - // server action return value of non-progressive enhancement case - returnValue?: { ok: boolean; data: unknown }; - // server action form state (e.g. useActionState) of progressive enhancement case - formState?: ReactFormState; -}; - -// the plugin by default assumes `rsc` entry having default export of request handler. -// however, how server entries are executed can be customized by registering own server handler. -export default { fetch: handler }; - -async function handler(request: Request): Promise { - // differentiate RSC, SSR, action, etc. - const renderRequest = parseRenderRequest(request); - request = renderRequest.request; - - // handle server function request - let returnValue: RscPayload["returnValue"] | undefined; - let formState: ReactFormState | undefined; - let temporaryReferences: unknown | undefined; - let actionStatus: number | undefined; - if (renderRequest.isAction === true) { - if (renderRequest.actionId) { - // action is called via `ReactClient.setServerCallback`. - const contentType = request.headers.get("content-type"); - const body = contentType?.startsWith("multipart/form-data") - ? await request.formData() - : await request.text(); - temporaryReferences = createTemporaryReferenceSet(); - const args = await decodeReply(body, { temporaryReferences }); - const action = await loadServerAction(renderRequest.actionId); - try { - const data = await action.apply(null, args); - returnValue = { ok: true, data }; - } catch (e) { - returnValue = { ok: false, data: e }; - actionStatus = 500; - } - } else { - // otherwise server function is called via `

` - // before hydration (e.g. when javascript is disabled). - // aka progressive enhancement. - const formData = await request.formData(); - const decodedAction = await decodeAction(formData); - try { - const result = await decodedAction(); - formState = await decodeFormState(result, formData); - } catch { - // there's no single general obvious way to surface this error, - // so explicitly return classic 500 response. - return new Response("Internal Server Error: server action failed", { - status: 500, - }); - } - } - } - - // serialization from React VDOM tree to RSC stream. - // we render RSC stream after handling server function request - // so that new render reflects updated state from server function call - // to achieve single round trip to mutate and fetch from server. - const rscPayload: RscPayload = { - root: , - formState, - returnValue, - }; - const rscOptions = { temporaryReferences }; - const rscStream = renderToReadableStream(rscPayload, rscOptions); - - // Respond RSC stream without HTML rendering as decided by `RenderRequest` - if (renderRequest.isRsc) { - return new Response(rscStream, { - status: actionStatus, - headers: { - "content-type": "text/x-component;charset=utf-8", - }, - }); - } - - // Delegate to SSR environment for html rendering. - // The plugin provides `loadModule` helper to allow loading SSR environment entry module - // in RSC environment. however this can be customized by implementing own runtime communication - // e.g. `@cloudflare/vite-plugin`'s service binding. - const ssrEntryModule = await import.meta.viteRsc.loadModule("ssr", "index"); - const ssrResult = await ssrEntryModule.renderHTML(rscStream, { - formState, - // allow quick simulation of javascript disabled browser - debugNojs: renderRequest.url.searchParams.has("__nojs"), - }); - - // respond html - return new Response(ssrResult.stream, { - status: ssrResult.status, - headers: { - "Content-type": "text/html", - }, - }); -} - -if (import.meta.hot) { - import.meta.hot.accept(); -} diff --git a/fixtures/react-rsc/src/framework/entry.ssr.tsx b/fixtures/react-rsc/src/framework/entry.ssr.tsx deleted file mode 100644 index 27d8ae71..00000000 --- a/fixtures/react-rsc/src/framework/entry.ssr.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr"; -import React from "react"; -import type { ReactFormState } from "react-dom/client"; -import { renderToReadableStream } from "react-dom/server.edge"; -import { injectRSCPayload } from "rsc-html-stream/server"; -import type { RscPayload } from "./entry.rsc"; - -export async function renderHTML( - rscStream: ReadableStream, - options: { - formState?: ReactFormState; - nonce?: string; - debugNojs?: boolean; - }, -): Promise<{ stream: ReadableStream; status?: number }> { - // duplicate one RSC stream into two. - // - one for SSR (ReactClient.createFromReadableStream below) - // - another for browser hydration payload by injecting . - const [rscStream1, rscStream2] = rscStream.tee(); - - // deserialize RSC stream back to React VDOM - let payload: Promise | undefined; - function SsrRoot() { - // deserialization needs to be kicked off inside ReactDOMServer context - // for ReactDomServer preinit/preloading to work - payload ??= createFromReadableStream(rscStream1); - return React.use(payload).root; - } - - // render html (traditional SSR) - const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent("index"); - let htmlStream: ReadableStream; - let status: number | undefined; - try { - htmlStream = await renderToReadableStream(, { - bootstrapScriptContent: options?.debugNojs ? undefined : bootstrapScriptContent, - nonce: options?.nonce, - formState: options?.formState, - }); - } catch { - // fallback to render an empty shell and run pure CSR on browser, - // which can replay server component error and trigger error boundary. - status = 500; - htmlStream = await renderToReadableStream( - - - - - , - { - bootstrapScriptContent: - `self.__NO_HYDRATE=1;` + (options?.debugNojs ? "" : bootstrapScriptContent), - nonce: options?.nonce, - }, - ); - } - - let responseStream: ReadableStream = htmlStream; - if (!options?.debugNojs) { - // initial RSC stream is injected in HTML stream as - // using utility made by devongovett https://github.com/devongovett/rsc-html-stream - responseStream = responseStream.pipeThrough( - injectRSCPayload(rscStream2, { - nonce: options?.nonce, - }), - ); - } - - return { stream: responseStream, status }; -} diff --git a/fixtures/react-rsc/src/framework/error-boundary.tsx b/fixtures/react-rsc/src/framework/error-boundary.tsx deleted file mode 100644 index ccfb696d..00000000 --- a/fixtures/react-rsc/src/framework/error-boundary.tsx +++ /dev/null @@ -1,75 +0,0 @@ -"use client"; - -import React from "react"; - -// Minimal ErrorBoundary example to handle errors globally on browser -export function GlobalErrorBoundary(props: { children?: React.ReactNode }) { - return {props.children}; -} - -// https://github.com/vercel/next.js/blob/33f8428f7066bf8b2ec61f025427ceb2a54c4bdf/packages/next/src/client/components/error-boundary.tsx -// https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary -class ErrorBoundary extends React.Component<{ - children?: React.ReactNode; - errorComponent: React.FC<{ - error: Error; - reset: () => void; - }>; -}> { - state: { error?: Error } = {}; - - static getDerivedStateFromError(error: Error) { - return { error }; - } - - reset = () => { - this.setState({ error: null }); - }; - - render() { - const error = this.state.error; - if (error) { - return ; - } - return this.props.children; - } -} - -// https://github.com/vercel/next.js/blob/677c9b372faef680d17e9ba224743f44e1107661/packages/next/src/build/webpack/loaders/next-app-loader.ts#L73 -// https://github.com/vercel/next.js/blob/677c9b372faef680d17e9ba224743f44e1107661/packages/next/src/client/components/error-boundary.tsx#L145 -function DefaultGlobalErrorPage(props: { error: Error; reset: () => void }) { - return ( - - - Unexpected Error - - -

Caught an unexpected error

-
-          Error:{" "}
-          {import.meta.env.DEV && "message" in props.error ? props.error.message : "(Unknown)"}
-        
- - - - ); -} diff --git a/fixtures/react-rsc/src/framework/request.tsx b/fixtures/react-rsc/src/framework/request.tsx deleted file mode 100644 index 7d48788e..00000000 --- a/fixtures/react-rsc/src/framework/request.tsx +++ /dev/null @@ -1,58 +0,0 @@ -// Framework conventions (arbitrary choices for this demo): -// - Use `_.rsc` URL suffix to differentiate RSC requests from SSR requests -// - Use `x-rsc-action` header to pass server action ID -const URL_POSTFIX = "_.rsc"; -const HEADER_ACTION_ID = "x-rsc-action"; - -// Parsed request information used to route between RSC/SSR rendering and action handling. -// Created by parseRenderRequest() from incoming HTTP requests. -type RenderRequest = { - isRsc: boolean; // true if request should return RSC payload (via _.rsc suffix) - isAction: boolean; // true if this is a server action call (POST request) - actionId?: string; // server action ID from x-rsc-action header - request: Request; // normalized Request with _.rsc suffix removed from URL - url: URL; // normalized URL with _.rsc suffix removed -}; - -export function createRscRenderRequest( - urlString: string, - action?: { id: string; body: BodyInit }, -): Request { - const url = new URL(urlString); - url.pathname += URL_POSTFIX; - const headers = new Headers(); - if (action) { - headers.set(HEADER_ACTION_ID, action.id); - } - return new Request(url.toString(), { - method: action ? "POST" : "GET", - headers, - body: action?.body, - }); -} - -export function parseRenderRequest(request: Request): RenderRequest { - const url = new URL(request.url); - const isAction = request.method === "POST"; - if (url.pathname.endsWith(URL_POSTFIX)) { - url.pathname = url.pathname.slice(0, -URL_POSTFIX.length); - const actionId = request.headers.get(HEADER_ACTION_ID) || undefined; - if (request.method === "POST" && !actionId) { - throw new Error("Missing action id header for RSC action request"); - } - return { - isRsc: true, - isAction, - actionId, - request: new Request(url, request), - url, - }; - } else { - return { - isRsc: false, - isAction, - request, - url, - }; - } -} diff --git a/fixtures/react-rsc/src/index.css b/fixtures/react-rsc/src/index.css deleted file mode 100644 index f4d2128c..00000000 --- a/fixtures/react-rsc/src/index.css +++ /dev/null @@ -1,112 +0,0 @@ -:root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} - -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 1rem; -} - -.read-the-docs { - color: #888; - text-align: left; -} diff --git a/fixtures/react-rsc/src/root.tsx b/fixtures/react-rsc/src/root.tsx deleted file mode 100644 index aaf322d0..00000000 --- a/fixtures/react-rsc/src/root.tsx +++ /dev/null @@ -1,70 +0,0 @@ -// oxlint-disable-next-line import/no-unassigned-import -import "./index.css"; // css import is automatically injected in exported server components -// oxlint-disable-next-line import/no-absolute-path -import viteLogo from "/vite.svg"; -import { getServerCounter, updateServerCounter } from "./action.tsx"; -import reactLogo from "./assets/react.svg"; -import { ClientCounter } from "./client.tsx"; - -export function Root(props: { url: URL }) { - return ( - - - - - - Vite + RSC - - - - - - ); -} - -function App(props: { url: URL }) { - return ( -
- -

Vite + RSC

-
- -
-
- - - -
-
Request URL: {props.url?.href}
-
    -
  • - Edit src/client.tsx to test client HMR. -
  • -
  • - Edit src/root.tsx to test server HMR. -
  • -
  • - Visit{" "} - - _.rsc - {" "} - to view RSC stream payload. -
  • -
  • - Visit{" "} - - ?__nojs - {" "} - to test server action without js enabled. -
  • -
-
- ); -} diff --git a/fixtures/react-rsc/test/dev.test.ts b/fixtures/react-rsc/test/dev.test.ts deleted file mode 100644 index 6a86c442..00000000 --- a/fixtures/react-rsc/test/dev.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { afterAll, beforeAll, expect, test } from "bun:test"; -import { spawn, type ChildProcess } from "node:child_process"; -import { fileURLToPath } from "node:url"; -import path from "node:path"; - -// Dev-mode smoke test: boots `vite dev` (the distilled Cloudflare plugin in -// RSC mode) and asserts the minimal RSC app renders — server components, -// the client component, and the server action are all present in the SSR'd -// HTML. Build-mode is a separate track and intentionally not tested here. -const fixtureDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); - -let proc: ChildProcess; -let baseUrl: string; - -beforeAll(async () => { - proc = spawn("bun", ["vite", "dev", "--port", "3151"], { - cwd: fixtureDir, - stdio: ["ignore", "pipe", "pipe"], - }); - baseUrl = await new Promise((resolve, reject) => { - const timer = setTimeout(() => reject(new Error("dev server did not start in time")), 90_000); - const onData = (chunk: Buffer) => { - const match = String(chunk).match(/Local:\s+(http:\/\/\S+?)\/?\s/); - if (match) { - clearTimeout(timer); - resolve(match[1].replace(/\/$/, "")); - } - }; - proc.stdout?.on("data", onData); - proc.stderr?.on("data", onData); - proc.on("exit", (code) => reject(new Error(`dev server exited early (code ${code})`))); - }); -}); - -afterAll(() => { - proc?.kill("SIGTERM"); -}); - -test("server-renders the RSC app (server component + client component + server action)", async () => { - const res = await fetch(`${baseUrl}/`); - expect(res.status).toBe(200); - const html = await res.text(); - expect(html).toContain("Vite + RSC"); // server component - expect(html).toContain("Client Counter"); // client component - expect(html).toContain("Server Counter"); // server action -}); diff --git a/fixtures/react-rsc/test/plugin-order.test.ts b/fixtures/react-rsc/test/plugin-order.test.ts deleted file mode 100644 index 3aa02ec4..00000000 --- a/fixtures/react-rsc/test/plugin-order.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { afterAll, beforeAll, expect, test } from "bun:test"; -import { spawn, type ChildProcess } from "node:child_process"; -import { fileURLToPath } from "node:url"; -import path from "node:path"; - -// Locks order-independence (review finding): with the Cloudflare plugin -// registered BEFORE rsc(), the rsc env's resolve.conditions lists `react-server` -// after the workerd conditions (not first). Export-condition resolution is -// set-membership, so the app must still render correctly — if `react-server` -// weren't effective in the rsc env, flight generation would 500. See -// vite.config.cf-first.ts. -const fixtureDir = path.dirname(path.dirname(fileURLToPath(import.meta.url))); - -let proc: ChildProcess; -let baseUrl: string; - -beforeAll(async () => { - proc = spawn("bun", ["vite", "dev", "-c", "vite.config.cf-first.ts", "--port", "3152"], { - cwd: fixtureDir, - stdio: ["ignore", "pipe", "pipe"], - }); - baseUrl = await new Promise((resolve, reject) => { - const timer = setTimeout(() => reject(new Error("dev server did not start in time")), 90_000); - const onData = (chunk: Buffer) => { - const match = String(chunk).match(/Local:\s+(http:\/\/\S+?)\/?\s/); - if (match) { - clearTimeout(timer); - resolve(match[1].replace(/\/$/, "")); - } - }; - proc.stdout?.on("data", onData); - proc.stderr?.on("data", onData); - proc.on("exit", (code) => reject(new Error(`dev server exited early (code ${code})`))); - }); -}); - -afterAll(() => { - proc?.kill("SIGTERM"); -}); - -test("RSC renders correctly even with cloudflare() before rsc() (react-server not first in conditions)", async () => { - const res = await fetch(`${baseUrl}/`); - expect(res.status).toBe(200); - const html = await res.text(); - expect(html).toContain("Vite + RSC"); - expect(html).toContain("Client Counter"); -}); diff --git a/fixtures/react-rsc/tsconfig.json b/fixtures/react-rsc/tsconfig.json deleted file mode 100644 index b212cd7a..00000000 --- a/fixtures/react-rsc/tsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "erasableSyntaxOnly": true, - "allowImportingTsExtensions": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "skipLibCheck": true, - "verbatimModuleSyntax": true, - "noEmit": true, - "moduleResolution": "Bundler", - "module": "ESNext", - "target": "ESNext", - "lib": ["ESNext", "DOM"], - "types": ["vite/client", "@vitejs/plugin-rsc/types"], - "jsx": "react-jsx" - } -} diff --git a/fixtures/react-rsc/vite.config.ts b/fixtures/react-rsc/vite.config.ts deleted file mode 100644 index 48026b04..00000000 --- a/fixtures/react-rsc/vite.config.ts +++ /dev/null @@ -1,39 +0,0 @@ -import cloudflare from "@distilled.cloud/cloudflare-vite-plugin"; -import react from "@vitejs/plugin-react"; -import rsc from "@vitejs/plugin-rsc"; -import { defineConfig } from "vite"; - -// Minimal React Server Components app (the @vitejs/plugin-rsc `starter`) -// wired to the distilled Cloudflare vite plugin, to reproduce and then fix -// RSC dev support (cloudflare-tools#43). -// -// The worker IS the `rsc` environment: plugin-rsc resolves it with the -// `react-server` condition and its `default export` ({ fetch }) is the -// request handler. `serverHandler: false` tells plugin-rsc not to mount its -// own Node dev middleware, so requests route into workerd instead. -export default defineConfig({ - plugins: [ - cloudflare({ - main: "./src/framework/entry.rsc.tsx", - compatibilityDate: "2026-03-10", - compatibilityFlags: ["nodejs_compat"], - // The Worker is the `rsc` environment; it loads `ssr` modules at runtime. - viteEnvironments: { entry: "rsc", children: ["ssr"] }, - worker: { name: "fixtures-react-rsc", bindings: [] }, - }), - rsc(), - react(), - ], - - environments: { - rsc: { - build: { rolldownOptions: { input: { index: "./src/framework/entry.rsc.tsx" } } }, - }, - ssr: { - build: { rolldownOptions: { input: { index: "./src/framework/entry.ssr.tsx" } } }, - }, - client: { - build: { rolldownOptions: { input: { index: "./src/framework/entry.browser.tsx" } } }, - }, - }, -}); From 8920dd2ff8ce6c85bbb5b8d026e7a93ba25d90c1 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Thu, 25 Jun 2026 11:41:42 -0400 Subject: [PATCH 13/16] remove bun.lock from fixture --- fixtures/tanstack-start/bun.lock | 792 ------------------------------- 1 file changed, 792 deletions(-) delete mode 100644 fixtures/tanstack-start/bun.lock diff --git a/fixtures/tanstack-start/bun.lock b/fixtures/tanstack-start/bun.lock deleted file mode 100644 index e446be5b..00000000 --- a/fixtures/tanstack-start/bun.lock +++ /dev/null @@ -1,792 +0,0 @@ -{ - "lockfileVersion": 1, - "configVersion": 1, - "workspaces": { - "": { - "name": "tanstack-app", - "dependencies": { - "@tailwindcss/vite": "^4.1.18", - "@tanstack/react-devtools": "latest", - "@tanstack/react-router": "latest", - "@tanstack/react-router-devtools": "latest", - "@tanstack/react-router-ssr-query": "latest", - "@tanstack/react-start": "latest", - "@tanstack/router-plugin": "^1.132.0", - "lucide-react": "^0.545.0", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "tailwindcss": "^4.1.18", - }, - "devDependencies": { - "@tailwindcss/typography": "^0.5.16", - "@tanstack/devtools-vite": "latest", - "@testing-library/dom": "^10.4.1", - "@testing-library/react": "^16.3.0", - "@types/node": "^22.10.2", - "@types/react": "^19.2.0", - "@types/react-dom": "^19.2.0", - "@vitejs/plugin-react": "^5.1.4", - "jsdom": "^28.1.0", - "typescript": "^5.7.2", - "vite": "^7.3.1", - "vite-tsconfig-paths": "^5.1.4", - "vitest": "^3.0.5", - }, - }, - }, - "packages": { - "@acemir/cssom": ["@acemir/cssom@0.9.31", "", {}, "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA=="], - - "@asamuzakjp/css-color": ["@asamuzakjp/css-color@5.1.6", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-BXWCh8dHs9GOfpo/fWGDJtDmleta2VePN9rn6WQt3GjEbxzutVF4t0x2pmH+7dbMCLtuv3MlwqRsAuxlzFXqFg=="], - - "@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@6.8.1", "", { "dependencies": { "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.1.0", "is-potential-custom-element-name": "^1.0.1", "lru-cache": "^11.2.6" } }, "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ=="], - - "@asamuzakjp/nwsapi": ["@asamuzakjp/nwsapi@2.3.9", "", {}, "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q=="], - - "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], - - "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], - - "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], - - "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], - - "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], - - "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], - - "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], - - "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], - - "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="], - - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], - - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], - - "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], - - "@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="], - - "@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], - - "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w=="], - - "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A=="], - - "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], - - "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], - - "@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="], - - "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], - - "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], - - "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], - - "@bramus/specificity": ["@bramus/specificity@2.4.2", "", { "dependencies": { "css-tree": "^3.0.0" }, "bin": { "specificity": "bin/cli.js" } }, "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw=="], - - "@csstools/color-helpers": ["@csstools/color-helpers@6.0.2", "", {}, "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q=="], - - "@csstools/css-calc": ["@csstools/css-calc@3.1.1", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ=="], - - "@csstools/css-color-parser": ["@csstools/css-color-parser@4.0.2", "", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "@csstools/css-calc": "^3.1.1" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw=="], - - "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@4.0.0", "", { "peerDependencies": { "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w=="], - - "@csstools/css-syntax-patches-for-csstree": ["@csstools/css-syntax-patches-for-csstree@1.1.2", "", { "peerDependencies": { "css-tree": "^3.2.1" }, "optionalPeers": ["css-tree"] }, "sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA=="], - - "@csstools/css-tokenizer": ["@csstools/css-tokenizer@4.0.0", "", {}, "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA=="], - - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], - - "@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="], - - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.7", "", { "os": "android", "cpu": "arm64" }, "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ=="], - - "@esbuild/android-x64": ["@esbuild/android-x64@0.27.7", "", { "os": "android", "cpu": "x64" }, "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg=="], - - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw=="], - - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ=="], - - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.7", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w=="], - - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ=="], - - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.7", "", { "os": "linux", "cpu": "arm" }, "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA=="], - - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A=="], - - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.7", "", { "os": "linux", "cpu": "ia32" }, "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg=="], - - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q=="], - - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw=="], - - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ=="], - - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.7", "", { "os": "linux", "cpu": "none" }, "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ=="], - - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw=="], - - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.7", "", { "os": "linux", "cpu": "x64" }, "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA=="], - - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w=="], - - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.7", "", { "os": "none", "cpu": "x64" }, "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw=="], - - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.7", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A=="], - - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.7", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg=="], - - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.7", "", { "os": "none", "cpu": "arm64" }, "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw=="], - - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.7", "", { "os": "sunos", "cpu": "x64" }, "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA=="], - - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA=="], - - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.7", "", { "os": "win32", "cpu": "ia32" }, "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw=="], - - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], - - "@exodus/bytes": ["@exodus/bytes@1.15.0", "", { "peerDependencies": { "@noble/hashes": "^1.8.0 || ^2.0.0" }, "optionalPeers": ["@noble/hashes"] }, "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ=="], - - "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], - - "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], - - "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], - - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - - "@oozcitak/dom": ["@oozcitak/dom@2.0.2", "", { "dependencies": { "@oozcitak/infra": "^2.0.2", "@oozcitak/url": "^3.0.0", "@oozcitak/util": "^10.0.0" } }, "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w=="], - - "@oozcitak/infra": ["@oozcitak/infra@2.0.2", "", { "dependencies": { "@oozcitak/util": "^10.0.0" } }, "sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA=="], - - "@oozcitak/url": ["@oozcitak/url@3.0.0", "", { "dependencies": { "@oozcitak/infra": "^2.0.2", "@oozcitak/util": "^10.0.0" } }, "sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ=="], - - "@oozcitak/util": ["@oozcitak/util@10.0.0", "", {}, "sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA=="], - - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], - - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.1", "", { "os": "android", "cpu": "arm" }, "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA=="], - - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.1", "", { "os": "android", "cpu": "arm64" }, "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA=="], - - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.60.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw=="], - - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.60.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew=="], - - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.60.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w=="], - - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.60.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g=="], - - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.60.1", "", { "os": "linux", "cpu": "arm" }, "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g=="], - - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.60.1", "", { "os": "linux", "cpu": "arm" }, "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg=="], - - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.60.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ=="], - - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.60.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA=="], - - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ=="], - - "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw=="], - - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.60.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw=="], - - "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.60.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg=="], - - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg=="], - - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.60.1", "", { "os": "linux", "cpu": "none" }, "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg=="], - - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.60.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ=="], - - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.60.1", "", { "os": "linux", "cpu": "x64" }, "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg=="], - - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.60.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w=="], - - "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.60.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw=="], - - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.60.1", "", { "os": "none", "cpu": "arm64" }, "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA=="], - - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.60.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g=="], - - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.60.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg=="], - - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.60.1", "", { "os": "win32", "cpu": "x64" }, "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg=="], - - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ=="], - - "@solid-primitives/event-listener": ["@solid-primitives/event-listener@2.4.5", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-nwRV558mIabl4yVAhZKY8cb6G+O1F0M6Z75ttTu5hk+SxdOnKSGj+eetDIu7Oax1P138ZdUU01qnBPR8rnxaEA=="], - - "@solid-primitives/keyboard": ["@solid-primitives/keyboard@1.3.5", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.5", "@solid-primitives/rootless": "^1.5.3", "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-sav+l+PL+74z3yaftVs7qd8c2SXkqzuxPOVibUe5wYMt+U5Hxp3V3XCPgBPN2I6cANjvoFtz0NiU8uHVLdi9FQ=="], - - "@solid-primitives/resize-observer": ["@solid-primitives/resize-observer@2.1.5", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.5", "@solid-primitives/rootless": "^1.5.3", "@solid-primitives/static-store": "^0.1.3", "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-AiyTknKcNBaKHbcSMuxtSNM8FjIuiSuFyFghdD0TcCMU9hKi9EmsC5pjfjDwxE+5EueB1a+T/34PLRI5vbBbKw=="], - - "@solid-primitives/rootless": ["@solid-primitives/rootless@1.5.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA=="], - - "@solid-primitives/static-store": ["@solid-primitives/static-store@0.1.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-uxez7SXnr5GiRnzqO2IEDjOJRIXaG+0LZLBizmUA1FwSi+hrpuMzVBwyk70m4prcl8X6FDDXUl9O8hSq8wHbBQ=="], - - "@solid-primitives/utils": ["@solid-primitives/utils@6.4.0", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-AeGTBg8Wtkh/0s+evyLtP8piQoS4wyqqQaAFs2HJcFMMjYAtUgo+ZPduRXLjPlqKVc2ejeR544oeqpbn8Egn8A=="], - - "@tailwindcss/node": ["@tailwindcss/node@4.2.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.2" } }, "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA=="], - - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.2", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.2", "@tailwindcss/oxide-darwin-arm64": "4.2.2", "@tailwindcss/oxide-darwin-x64": "4.2.2", "@tailwindcss/oxide-freebsd-x64": "4.2.2", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", "@tailwindcss/oxide-linux-x64-musl": "4.2.2", "@tailwindcss/oxide-wasm32-wasi": "4.2.2", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" } }, "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg=="], - - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.2", "", { "os": "android", "cpu": "arm64" }, "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg=="], - - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg=="], - - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw=="], - - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ=="], - - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2", "", { "os": "linux", "cpu": "arm" }, "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ=="], - - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw=="], - - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag=="], - - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg=="], - - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ=="], - - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.2", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q=="], - - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ=="], - - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.2", "", { "os": "win32", "cpu": "x64" }, "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA=="], - - "@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="], - - "@tailwindcss/vite": ["@tailwindcss/vite@4.2.2", "", { "dependencies": { "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "tailwindcss": "4.2.2" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w=="], - - "@tanstack/devtools": ["@tanstack/devtools@0.11.1", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/keyboard": "^1.3.3", "@solid-primitives/resize-observer": "^2.1.3", "@tanstack/devtools-client": "0.0.6", "@tanstack/devtools-event-bus": "0.4.1", "@tanstack/devtools-ui": "0.5.1", "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.9" }, "bin": { "intent": "bin/intent.js" } }, "sha512-g3nHgVP76kT9190d6O32AjANoEnujLEB+51PDtBzlah8hvKeEygK53cunN+HXhjlfhM4PoOCi8/B96cdJVSnLg=="], - - "@tanstack/devtools-client": ["@tanstack/devtools-client@0.0.6", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.4.1" } }, "sha512-f85ZJXJnDIFOoykG/BFIixuAevJovCvJF391LPs6YjBAPhGYC50NWlx1y4iF/UmK5/cCMx+/JqI5SBOz7FanQQ=="], - - "@tanstack/devtools-event-bus": ["@tanstack/devtools-event-bus@0.4.1", "", { "dependencies": { "ws": "^8.18.3" } }, "sha512-cNnJ89Q021Zf883rlbBTfsaxTfi2r73/qejGtyTa7ksErF3hyDyAq1aTbo5crK9dAL7zSHh9viKY1BtMls1QOA=="], - - "@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.4.3", "", { "bin": { "intent": "bin/intent.js" } }, "sha512-OZI6QyULw0FI0wjgmeYzCIfbgPsOEzwJtCpa69XrfLMtNXLGnz3d/dIabk7frg0TmHo+Ah49w5I4KC7Tufwsvw=="], - - "@tanstack/devtools-ui": ["@tanstack/devtools-ui@0.5.1", "", { "dependencies": { "clsx": "^2.1.1", "dayjs": "^1.11.19", "goober": "^2.1.16", "solid-js": "^1.9.9" } }, "sha512-T9JjAdqMSnxsVO6AQykD5vhxPF4iFLKtbYxee/bU3OLlk446F5C1220GdCmhDSz7y4lx+m8AvIS0bq6zzvdDUA=="], - - "@tanstack/devtools-vite": ["@tanstack/devtools-vite@0.6.0", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/generator": "^7.28.3", "@babel/parser": "^7.28.4", "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@tanstack/devtools-client": "0.0.6", "@tanstack/devtools-event-bus": "0.4.1", "chalk": "^5.6.2", "launch-editor": "^2.11.1", "picomatch": "^4.0.3" }, "peerDependencies": { "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "bin": { "intent": "bin/intent.js" } }, "sha512-h0r0ct7zlrgjkhmn4QW6wRjgUXd4JMs+r7gtx+BXo9f5H9Y+jtUdtvC0rnZcPto6gw/9yMUq7yOmMK5qDWRExg=="], - - "@tanstack/history": ["@tanstack/history@1.161.6", "", {}, "sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg=="], - - "@tanstack/query-core": ["@tanstack/query-core@5.96.2", "", {}, "sha512-hzI6cTVh4KNRk8UtoIBS7Lv9g6BnJPXvBKsvYH1aGWvv0347jT3BnSvztOE+kD76XGvZnRC/t6qdW1CaIfwCeA=="], - - "@tanstack/react-devtools": ["@tanstack/react-devtools@0.10.1", "", { "dependencies": { "@tanstack/devtools": "0.11.1" }, "peerDependencies": { "@types/react": ">=16.8", "@types/react-dom": ">=16.8", "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-cvcd0EqN7Q2LYatQXxFhOkEa9RUQXZlhXnM1mwuibxmyRX+CMyohUZcgjodtIfgh+RT0Pmvt49liTdZby5ovZw=="], - - "@tanstack/react-query": ["@tanstack/react-query@5.96.2", "", { "dependencies": { "@tanstack/query-core": "5.96.2" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-sYyzzJT4G0g02azzJ8o55VFFV31XvFpdUpG+unxS0vSaYsJnSPKGoI6WdPwUucJL1wpgGfwfmntNX/Ub1uOViA=="], - - "@tanstack/react-router": ["@tanstack/react-router@1.168.10", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-store": "^0.9.3", "@tanstack/router-core": "1.168.9", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-/RmDlOwDkCug609KdPB3U+U1zmrtadJpvsmRg2zEn8TRCKRNri7dYZIjQZbNg8PgUiRL4T6njrZBV1ChzblNaA=="], - - "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.166.11", "", { "dependencies": { "@tanstack/router-devtools-core": "1.167.1" }, "peerDependencies": { "@tanstack/react-router": "^1.168.2", "@tanstack/router-core": "^1.168.2", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" }, "optionalPeers": ["@tanstack/router-core"] }, "sha512-WYR3q4Xui5yPT/5PXtQh8i03iUA7q8dONBjWpV3nsGdM8Cs1FxpfhLstW0wZO1dOvSyElscwTRCJ6nO5N8r3Lg=="], - - "@tanstack/react-router-ssr-query": ["@tanstack/react-router-ssr-query@1.166.10", "", { "dependencies": { "@tanstack/router-ssr-query-core": "1.167.0" }, "peerDependencies": { "@tanstack/query-core": ">=5.90.0", "@tanstack/react-query": ">=5.90.0", "@tanstack/react-router": ">=1.127.0", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-Ny5jKZPSy+RBXICJBJkW2q3SKjEwVooIn2zuWfIFL1MNVImQPh/p+yvqDqKdJseIQ45B4JsqFtWVcdy/6rQ0Rg=="], - - "@tanstack/react-start": ["@tanstack/react-start@1.167.16", "", { "dependencies": { "@tanstack/react-router": "1.168.10", "@tanstack/react-start-client": "1.166.25", "@tanstack/react-start-server": "1.166.25", "@tanstack/router-utils": "^1.161.6", "@tanstack/start-client-core": "1.167.9", "@tanstack/start-plugin-core": "1.167.17", "@tanstack/start-server-core": "1.167.9", "pathe": "^2.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0", "vite": ">=7.0.0" }, "bin": { "intent": "bin/intent.js" } }, "sha512-vHIhn+FTWfAVhRus1BZEaBZPhnYL+StDuMlShslIBPEGGTCRt11BxNUfV/iDpr7zbxw36Snj7zGfI7DwfjjlDQ=="], - - "@tanstack/react-start-client": ["@tanstack/react-start-client@1.166.25", "", { "dependencies": { "@tanstack/react-router": "1.168.10", "@tanstack/router-core": "1.168.9", "@tanstack/start-client-core": "1.167.9" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-FvD279zzneUtsfhaTv2c29qhE1Z3wHy3dt3cCjn9LzWZehOgn5Ij78s0YpmQaQ8lSF3YL7CySE3pDk9XHE6YeA=="], - - "@tanstack/react-start-server": ["@tanstack/react-start-server@1.166.25", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/react-router": "1.168.10", "@tanstack/router-core": "1.168.9", "@tanstack/start-client-core": "1.167.9", "@tanstack/start-server-core": "1.167.9" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-bPLADxlplvcnAcnZvBjJl2MzgUnB85d7Mu5aEkYoOFxhz0WiG6mZp7BDadIJuCd33NYMirsd3XrjfCHNzrMTyg=="], - - "@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "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" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="], - - "@tanstack/router-core": ["@tanstack/router-core@1.168.9", "", { "dependencies": { "@tanstack/history": "1.161.6", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-18oeEwEDyXOIuO1VBP9ACaK7tYHZUjynGDCoUh/5c/BNhia9vCJCp9O0LfhZXOorDc/PmLSgvmweFhVmIxF10g=="], - - "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.167.1", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16" }, "peerDependencies": { "@tanstack/router-core": "^1.168.2", "csstype": "^3.0.10" }, "optionalPeers": ["csstype"] }, "sha512-ECMM47J4KmifUvJguGituSiBpfN8SyCUEoxQks5RY09hpIBfR2eswCv2e6cJimjkKwBQXOVTPkTUk/yRvER+9w=="], - - "@tanstack/router-generator": ["@tanstack/router-generator@1.166.24", "", { "dependencies": { "@tanstack/router-core": "1.168.9", "@tanstack/router-utils": "1.161.6", "@tanstack/virtual-file-routes": "1.161.7", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-vdaGKwuH+r+DPe6R1mjk+TDDmDH6NTG7QqwxHqGEvOH4aGf9sPjhmRKNJZqQr8cPIbfp6u5lXyZ1TeDcSNMVEA=="], - - "@tanstack/router-plugin": ["@tanstack/router-plugin@1.167.12", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.168.9", "@tanstack/router-generator": "1.166.24", "@tanstack/router-utils": "1.161.6", "@tanstack/virtual-file-routes": "1.161.7", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.168.10", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"], "bin": { "intent": "bin/intent.js" } }, "sha512-StEHcctCuFI5taSjO+lhR/yQ+EK63BdyYa+ne6FoNQPB3MMrOUrz2ZVnbqILRLkh2b+p2EfBKt65sgAKdKygPQ=="], - - "@tanstack/router-ssr-query-core": ["@tanstack/router-ssr-query-core@1.167.0", "", { "peerDependencies": { "@tanstack/query-core": ">=5.90.0", "@tanstack/router-core": ">=1.127.0" } }, "sha512-+fpK1U+NR8YzcUmXhEy2tdPfT/XxIn1AMd/ODkYGMExAAUWnV8Zptptf41djK5eBj6718P6YTfxLRkxtfUdnVA=="], - - "@tanstack/router-utils": ["@tanstack/router-utils@1.161.6", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ansis": "^4.1.0", "babel-dead-code-elimination": "^1.0.12", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-nRcYw+w2OEgK6VfjirYvGyPLOK+tZQz1jkYcmH5AjMamQ9PycnlxZF2aEZtPpNoUsaceX2bHptn6Ub5hGXqNvw=="], - - "@tanstack/start-client-core": ["@tanstack/start-client-core@1.167.9", "", { "dependencies": { "@tanstack/router-core": "1.168.9", "@tanstack/start-fn-stubs": "1.161.6", "@tanstack/start-storage-context": "1.166.23", "seroval": "^1.4.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-2ETQO/bxiZGsoTdPxZb7xR8YqCy5l4kv/QPkwIXuvx/A4BjufngXfgISjXUicXsFRIBZeiFnBzp9A38UMsS2iA=="], - - "@tanstack/start-fn-stubs": ["@tanstack/start-fn-stubs@1.161.6", "", {}, "sha512-Y6QSlGiLga8cHfvxGGaonXIlt2bIUTVdH6AMjmpMp7+ANNCp+N96GQbjjhLye3JkaxDfP68x5iZA8NK4imgRig=="], - - "@tanstack/start-plugin-core": ["@tanstack/start-plugin-core@1.167.17", "", { "dependencies": { "@babel/code-frame": "7.27.1", "@babel/core": "^7.28.5", "@babel/types": "^7.28.5", "@rolldown/pluginutils": "1.0.0-beta.40", "@tanstack/router-core": "1.168.9", "@tanstack/router-generator": "1.166.24", "@tanstack/router-plugin": "1.167.12", "@tanstack/router-utils": "1.161.6", "@tanstack/start-client-core": "1.167.9", "@tanstack/start-server-core": "1.167.9", "cheerio": "^1.0.0", "exsolve": "^1.0.7", "pathe": "^2.0.3", "picomatch": "^4.0.3", "source-map": "^0.7.6", "srvx": "^0.11.9", "tinyglobby": "^0.2.15", "ufo": "^1.5.4", "vitefu": "^1.1.1", "xmlbuilder2": "^4.0.3", "zod": "^3.24.2" }, "peerDependencies": { "vite": ">=7.0.0" } }, "sha512-OkorpOobGOEDVr72QUmkzKjbawKC05CSz+1B3OObB/AxBIIw+lLLhTXbV45QkX2LZA7dcRvPJYZGOH1pkFqA1g=="], - - "@tanstack/start-server-core": ["@tanstack/start-server-core@1.167.9", "", { "dependencies": { "@tanstack/history": "1.161.6", "@tanstack/router-core": "1.168.9", "@tanstack/start-client-core": "1.167.9", "@tanstack/start-storage-context": "1.166.23", "h3-v2": "npm:h3@2.0.1-rc.16", "seroval": "^1.4.2" }, "bin": { "intent": "bin/intent.js" } }, "sha512-vKkslQIihoDDVumF73VXT7PVFmN7Nea0nKhZx7gMbc0m09yPQYYR1dn86/dz14k6/7cDkJ+qKXa09rlVlN/i9Q=="], - - "@tanstack/start-storage-context": ["@tanstack/start-storage-context@1.166.23", "", { "dependencies": { "@tanstack/router-core": "1.168.9" } }, "sha512-3vEdiYRMx+r+Q7Xqxj3YmADPIpMm7fkKxDa8ITwodGXiw+SBJCGkpBXGUWjOXyXkIyqGHKM5UrReTcVUTkmaug=="], - - "@tanstack/store": ["@tanstack/store@0.9.3", "", {}, "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw=="], - - "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.161.7", "", { "bin": { "intent": "bin/intent.js" } }, "sha512-olW33+Cn+bsCsZKPwEGhlkqS6w3M2slFv11JIobdnCFKMLG97oAI2kWKdx5/zsywTL8flpnoIgaZZPlQTFYhdQ=="], - - "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], - - "@testing-library/react": ["@testing-library/react@16.3.2", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g=="], - - "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], - - "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], - - "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], - - "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], - - "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], - - "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], - - "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], - - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - - "@types/node": ["@types/node@22.19.17", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q=="], - - "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], - - "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], - - "@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], - - "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - - "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], - - "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], - - "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - - "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - - "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - - "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], - - "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - - "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], - - "ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="], - - "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], - - "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - - "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], - - "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], - - "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="], - - "baseline-browser-mapping": ["baseline-browser-mapping@2.10.16", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA=="], - - "bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="], - - "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], - - "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], - - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - - "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], - - "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], - - "caniuse-lite": ["caniuse-lite@1.0.30001786", "", {}, "sha512-4oxTZEvqmLLrERwxO76yfKM7acZo310U+v4kqexI2TL1DkkUEMT8UijrxxcnVdxR3qkVf5awGRX+4Z6aPHVKrA=="], - - "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], - - "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], - - "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], - - "cheerio": ["cheerio@1.2.0", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.1.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.19.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg=="], - - "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], - - "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], - - "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], - - "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], - - "cookie-es": ["cookie-es@2.0.1", "", {}, "sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA=="], - - "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], - - "css-tree": ["css-tree@3.2.1", "", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="], - - "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], - - "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], - - "cssstyle": ["cssstyle@6.2.0", "", { "dependencies": { "@asamuzakjp/css-color": "^5.0.1", "@csstools/css-syntax-patches-for-csstree": "^1.0.28", "css-tree": "^3.1.0", "lru-cache": "^11.2.6" } }, "sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig=="], - - "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], - - "data-urls": ["data-urls@7.0.0", "", { "dependencies": { "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.0" } }, "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA=="], - - "dayjs": ["dayjs@1.11.20", "", {}, "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ=="], - - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], - - "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], - - "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], - - "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - - "diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="], - - "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], - - "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], - - "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], - - "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], - - "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], - - "electron-to-chromium": ["electron-to-chromium@1.5.332", "", {}, "sha512-7OOtytmh/rINMLwaFTbcMVvYXO3AUm029X0LcyfYk0B557RlPkdpTpnH9+htMlfu5dKwOmT0+Zs2Aw+lnn6TeQ=="], - - "encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="], - - "enhanced-resolve": ["enhanced-resolve@5.20.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA=="], - - "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], - - "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], - - "esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="], - - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - - "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], - - "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - - "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], - - "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], - - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - - "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - - "get-tsconfig": ["get-tsconfig@4.13.7", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q=="], - - "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="], - - "goober": ["goober@2.1.18", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw=="], - - "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - - "h3-v2": ["h3@2.0.1-rc.16", "", { "dependencies": { "rou3": "^0.8.0", "srvx": "^0.11.9" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"], "bin": { "h3": "bin/h3.mjs" } }, "sha512-h+pjvyujdo9way8qj6FUbhaQcHlR8FEq65EhTX9ViT5pK8aLj68uFl4hBkF+hsTJAH+H1END2Yv6hTIsabGfag=="], - - "html-encoding-sniffer": ["html-encoding-sniffer@6.0.0", "", { "dependencies": { "@exodus/bytes": "^1.6.0" } }, "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg=="], - - "htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="], - - "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], - - "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], - - "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], - - "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], - - "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], - - "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - - "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], - - "isbot": ["isbot@5.1.37", "", {}, "sha512-5bcicX81xf6NlTEV8rWdg7Pk01LFizDetuYGHx6d/f6y3lR2/oo8IfxjzJqn1UdDEyCcwT9e7NRloj8DwCYujQ=="], - - "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], - - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - - "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - - "jsdom": ["jsdom@28.1.0", "", { "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", "@bramus/specificity": "^2.4.2", "@exodus/bytes": "^1.11.0", "cssstyle": "^6.0.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.0", "undici": "^7.21.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", "whatwg-url": "^16.0.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug=="], - - "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - - "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], - - "launch-editor": ["launch-editor@2.13.2", "", { "dependencies": { "picocolors": "^1.1.1", "shell-quote": "^1.8.3" } }, "sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg=="], - - "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], - - "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], - - "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], - - "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], - - "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], - - "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], - - "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], - - "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], - - "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], - - "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], - - "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], - - "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], - - "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - - "lru-cache": ["lru-cache@11.3.2", "", {}, "sha512-wgWa6FWQ3QRRJbIjbsldRJZxdxYngT/dO0I5Ynmlnin8qy7tC6xYzbcJjtN4wHLXtkbVwHzk0C+OejVw1XM+DQ=="], - - "lucide-react": ["lucide-react@0.545.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-7r1/yUuflQDSt4f1bpn5ZAocyIxcTyVyBBChSVtBKn5M+392cPmI5YJMWOJKk/HUWGm5wg83chlAZtCcGbEZtw=="], - - "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], - - "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], - - "mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - - "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "node-releases": ["node-releases@2.0.37", "", {}, "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg=="], - - "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], - - "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], - - "parse5": ["parse5@8.0.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA=="], - - "parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="], - - "parse5-parser-stream": ["parse5-parser-stream@7.1.2", "", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="], - - "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], - - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - - "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], - - "postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], - - "postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], - - "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], - - "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], - - "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - - "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], - - "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], - - "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], - - "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], - - "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], - - "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], - - "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - - "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - - "rollup": ["rollup@4.60.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.1", "@rollup/rollup-android-arm64": "4.60.1", "@rollup/rollup-darwin-arm64": "4.60.1", "@rollup/rollup-darwin-x64": "4.60.1", "@rollup/rollup-freebsd-arm64": "4.60.1", "@rollup/rollup-freebsd-x64": "4.60.1", "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", "@rollup/rollup-linux-arm-musleabihf": "4.60.1", "@rollup/rollup-linux-arm64-gnu": "4.60.1", "@rollup/rollup-linux-arm64-musl": "4.60.1", "@rollup/rollup-linux-loong64-gnu": "4.60.1", "@rollup/rollup-linux-loong64-musl": "4.60.1", "@rollup/rollup-linux-ppc64-gnu": "4.60.1", "@rollup/rollup-linux-ppc64-musl": "4.60.1", "@rollup/rollup-linux-riscv64-gnu": "4.60.1", "@rollup/rollup-linux-riscv64-musl": "4.60.1", "@rollup/rollup-linux-s390x-gnu": "4.60.1", "@rollup/rollup-linux-x64-gnu": "4.60.1", "@rollup/rollup-linux-x64-musl": "4.60.1", "@rollup/rollup-openbsd-x64": "4.60.1", "@rollup/rollup-openharmony-arm64": "4.60.1", "@rollup/rollup-win32-arm64-msvc": "4.60.1", "@rollup/rollup-win32-ia32-msvc": "4.60.1", "@rollup/rollup-win32-x64-gnu": "4.60.1", "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w=="], - - "rou3": ["rou3@0.8.1", "", {}, "sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA=="], - - "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - - "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], - - "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], - - "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - - "seroval": ["seroval@1.5.2", "", {}, "sha512-xcRN39BdsnO9Tf+VzsE7b3JyTJASItIV1FVFewJKCFcW4s4haIKS3e6vj8PGB9qBwC7tnuOywQMdv5N4qkzi7Q=="], - - "seroval-plugins": ["seroval-plugins@1.5.2", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-qpY0Cl+fKYFn4GOf3cMiq6l72CpuVaawb6ILjubOQ+diJ54LfOWaSSPsaswN8DRPIPW4Yq+tE1k5aKd7ILyaFg=="], - - "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], - - "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], - - "solid-js": ["solid-js@1.9.12", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", "seroval-plugins": "~1.5.0" } }, "sha512-QzKaSJq2/iDrWR1As6MHZQ8fQkdOBf8GReYb7L5iKwMGceg7HxDcaOHk0at66tNgn9U2U7dXo8ZZpLIAmGMzgw=="], - - "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], - - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - - "srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], - - "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], - - "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], - - "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], - - "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], - - "tailwindcss": ["tailwindcss@4.2.2", "", {}, "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q=="], - - "tapable": ["tapable@2.3.2", "", {}, "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA=="], - - "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], - - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], - - "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], - - "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - - "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], - - "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], - - "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], - - "tldts": ["tldts@7.0.28", "", { "dependencies": { "tldts-core": "^7.0.28" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw=="], - - "tldts-core": ["tldts-core@7.0.28", "", {}, "sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ=="], - - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - - "tough-cookie": ["tough-cookie@6.0.1", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw=="], - - "tr46": ["tr46@6.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="], - - "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], - - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - - "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], - - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - - "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], - - "undici": ["undici@7.24.7", "", {}, "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ=="], - - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], - - "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], - - "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], - - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - - "vite": ["vite@7.3.2", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg=="], - - "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], - - "vite-tsconfig-paths": ["vite-tsconfig-paths@5.1.4", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" }, "optionalPeers": ["vite"] }, "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w=="], - - "vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], - - "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], - - "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], - - "webidl-conversions": ["webidl-conversions@8.0.1", "", {}, "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ=="], - - "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], - - "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], - - "whatwg-mimetype": ["whatwg-mimetype@5.0.0", "", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="], - - "whatwg-url": ["whatwg-url@16.0.1", "", { "dependencies": { "@exodus/bytes": "^1.11.0", "tr46": "^6.0.0", "webidl-conversions": "^8.0.1" } }, "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw=="], - - "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], - - "ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], - - "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], - - "xmlbuilder2": ["xmlbuilder2@4.0.3", "", { "dependencies": { "@oozcitak/dom": "^2.0.2", "@oozcitak/infra": "^2.0.2", "@oozcitak/util": "^10.0.0", "js-yaml": "^4.1.1" } }, "sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA=="], - - "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], - - "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - - "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - - "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], - - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], - - "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="], - - "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], - - "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.2", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw=="], - - "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], - - "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - - "@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - - "@tanstack/start-plugin-core/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.40", "", {}, "sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w=="], - - "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], - - "cheerio/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], - - "cheerio/whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], - - "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], - - "htmlparser2/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], - - "parse5-htmlparser2-tree-adapter/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], - - "parse5-parser-stream/parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], - - "readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], - - "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], - - "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], - } -} From 23e4824f21b35bfec388399e8c27efe3cb8fbd58 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Thu, 25 Jun 2026 11:49:35 -0400 Subject: [PATCH 14/16] remove build result hook --- .../src/build-result.ts | 69 ------------------- packages/cloudflare-vite-plugin/src/plugin.ts | 5 -- 2 files changed, 74 deletions(-) delete mode 100644 packages/cloudflare-vite-plugin/src/build-result.ts diff --git a/packages/cloudflare-vite-plugin/src/build-result.ts b/packages/cloudflare-vite-plugin/src/build-result.ts deleted file mode 100644 index f10f0d62..00000000 --- a/packages/cloudflare-vite-plugin/src/build-result.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { WORKER_ENTRY_PREFIX } from "@distilled.cloud/cloudflare-rolldown-plugin/plugins"; -import type * as vite from "vite"; -import type { CloudflareVitePluginOptions } from "./plugin"; - -export interface BuildResult { - assetsDir?: string; - server?: [ - vite.Rolldown.OutputChunk, - ...Array, - ]; -} - -export const builderPlugin = (options: CloudflareVitePluginOptions): vite.Plugin => { - return { - name: "distilled-cloudflare:build-result", - async buildApp(builder) { - let assetsDir: string | undefined; - let server: BuildResult["server"]; - const serverModules = new Map< - string, - vite.Rolldown.OutputChunk | vite.Rolldown.OutputAsset - >(); - for (const environment of Object.values(builder.environments)) { - const result = await builder.build(environment); - if (environment.name === "client") { - assetsDir = environment.config.build.outDir; - continue; - } - if (!isRolldownOutput(result)) { - throw new Error("Build result is not a RolldownOutput"); - } - const chunk = result.output[0]; - if (chunk.facadeModuleId && chunk.facadeModuleId.startsWith(WORKER_ENTRY_PREFIX)) { - if (server) { - throw new Error("Multiple server entries found"); - } - server = [chunk]; - } else { - for (const chunk of result.output) { - serverModules.set(chunk.fileName, chunk); - } - } - } - const keys = Array.from(serverModules.keys()).sort((a, b) => a.localeCompare(b)); - if (keys.length > 0) { - if (!server) { - throw new Error("Server entry not found"); - } - for (const key of keys) { - const chunk = serverModules.get(key); - if (!chunk) { - throw new Error(`Chunk ${key} not found`); - } - server.push(chunk); - } - } - options.onBuildComplete?.({ - assetsDir, - server, - }); - }, - }; -}; - -const isRolldownOutput = ( - result: Awaited>, -): result is vite.Rolldown.RolldownOutput => { - return "output" in result && result.output.length > 0; -}; diff --git a/packages/cloudflare-vite-plugin/src/plugin.ts b/packages/cloudflare-vite-plugin/src/plugin.ts index 36dd751e..27e709cb 100644 --- a/packages/cloudflare-vite-plugin/src/plugin.ts +++ b/packages/cloudflare-vite-plugin/src/plugin.ts @@ -16,7 +16,6 @@ import type { } from "@distilled.cloud/cloudflare-runtime"; import type * as Context from "effect/Context"; import type * as vite from "vite"; -import { builderPlugin, type BuildResult } from "./build-result.js"; import { dev } from "./dev-plugin.js"; export interface CloudflareVitePluginOptions< @@ -24,11 +23,8 @@ export interface CloudflareVitePluginOptions< > extends BasePluginOptions { worker?: Omit, "compatibilityDate" | "compatibilityFlags" | "modules">; context?: Context.Context; - onBuildComplete?: (options: BuildResult) => void; } -export type { BuildResult }; - export default function cloudflareVitePlugin( options: CloudflareVitePluginOptions = {}, ): Array { @@ -49,6 +45,5 @@ export default function cloudflareVitePlugin( }, } as vite.Plugin, dev(options), - builderPlugin(options), ]; } From 910d67fe25110bff3f55a5afafce1369ccc49081 Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Thu, 25 Jun 2026 11:53:50 -0400 Subject: [PATCH 15/16] add back buildApp hook --- .../src/plugins/options.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts index f31512a9..c13375cf 100644 --- a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts +++ b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts @@ -144,8 +144,19 @@ export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (plu : {}), }; }; + const appType = userConfig.appType ?? (Object.keys(input).length === 0 ? "spa" : "custom"); return { - appType: userConfig.appType ?? (Object.keys(input).length === 0 ? "spa" : "custom"), + appType, + builder: + appType === "spa" + ? undefined + : { + buildApp: async (builder) => { + for (const environment of Object.values(builder.environments)) { + await builder.build(environment); + } + }, + }, ssr: { noExternal: true, resolve: { From d8a74bd33a73d8fcb4fe0bdfe597dd8c5b87f00d Mon Sep 17 00:00:00 2001 From: John Royal <34844819+john-royal@users.noreply.github.com> Date: Thu, 25 Jun 2026 12:22:37 -0400 Subject: [PATCH 16/16] fixture + fix define --- bun.lock | 71 ++++++++++++---- fixtures/react-router-rsc/app/root.tsx | 34 ++++++++ fixtures/react-router-rsc/app/routes.ts | 21 +++++ .../react-router-rsc/app/routes/about.tsx | 17 ++++ fixtures/react-router-rsc/app/routes/home.tsx | 10 +++ fixtures/react-router-rsc/app/styles.css | 44 ++++++++++ fixtures/react-router-rsc/package.json | 32 +++++++ .../react-router-rsc/playwright.config.ts | 21 +++++ .../react-router-vite/entry.browser.tsx | 47 +++++++++++ .../react-router-vite/entry.rsc.tsx | 32 +++++++ .../react-router-vite/entry.ssr.tsx | 29 +++++++ .../react-router-vite/entry.worker.tsx | 12 +++ .../react-router-vite/types.d.ts | 2 + .../__snapshots__/smoke.test.ts/about.png | Bin 0 -> 15226 bytes .../__snapshots__/smoke.test.ts/index.png | Bin 0 -> 13398 bytes fixtures/react-router-rsc/test/smoke.test.ts | 79 ++++++++++++++++++ fixtures/react-router-rsc/tsconfig.json | 15 ++++ fixtures/react-router-rsc/vite.config.ts | 33 ++++++++ .../src/plugins/options.ts | 7 ++ 19 files changed, 490 insertions(+), 16 deletions(-) create mode 100644 fixtures/react-router-rsc/app/root.tsx create mode 100644 fixtures/react-router-rsc/app/routes.ts create mode 100644 fixtures/react-router-rsc/app/routes/about.tsx create mode 100644 fixtures/react-router-rsc/app/routes/home.tsx create mode 100644 fixtures/react-router-rsc/app/styles.css create mode 100644 fixtures/react-router-rsc/package.json create mode 100644 fixtures/react-router-rsc/playwright.config.ts create mode 100644 fixtures/react-router-rsc/react-router-vite/entry.browser.tsx create mode 100644 fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx create mode 100644 fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx create mode 100644 fixtures/react-router-rsc/react-router-vite/entry.worker.tsx create mode 100644 fixtures/react-router-rsc/react-router-vite/types.d.ts create mode 100644 fixtures/react-router-rsc/test/__snapshots__/smoke.test.ts/about.png create mode 100644 fixtures/react-router-rsc/test/__snapshots__/smoke.test.ts/index.png create mode 100644 fixtures/react-router-rsc/test/smoke.test.ts create mode 100644 fixtures/react-router-rsc/tsconfig.json create mode 100644 fixtures/react-router-rsc/vite.config.ts diff --git a/bun.lock b/bun.lock index fc738973..52c70d4e 100644 --- a/bun.lock +++ b/bun.lock @@ -15,6 +15,27 @@ "typescript": "catalog:", }, }, + "fixtures/react-router-rsc": { + "name": "@fixtures/react-router-rsc", + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router": "7.16.0", + }, + "devDependencies": { + "@cloudflare/workers-types": "catalog:workers", + "@distilled.cloud/cloudflare-runtime": "workspace:*", + "@distilled.cloud/cloudflare-vite-plugin": "workspace:*", + "@distilled.cloud/test-utils": "workspace:*", + "@playwright/test": "catalog:", + "@types/node": "^24.12.0", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "latest", + "@vitejs/plugin-rsc": "latest", + "vite": "catalog:", + }, + }, "fixtures/solid-ssr": { "name": "@fixtures/solid-ssr", "version": "0.0.0", @@ -427,6 +448,8 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="], + "@fixtures/react-router-rsc": ["@fixtures/react-router-rsc@workspace:fixtures/react-router-rsc"], + "@fixtures/solid-ssr": ["@fixtures/solid-ssr@workspace:fixtures/solid-ssr"], "@fixtures/solidstart": ["@fixtures/solidstart@workspace:fixtures/solidstart"], @@ -997,7 +1020,7 @@ "@types/node": ["@types/node@24.12.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA=="], - "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + "@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="], "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], @@ -1011,7 +1034,7 @@ "@vercel/nft": ["@vercel/nft@1.5.0", "", { "dependencies": { "@mapbox/node-pre-gyp": "^2.0.0", "@rollup/pluginutils": "^5.1.3", "acorn": "^8.6.0", "acorn-import-attributes": "^1.9.5", "async-sema": "^3.1.1", "bindings": "^1.4.0", "estree-walker": "2.0.2", "glob": "^13.0.0", "graceful-fs": "^4.2.9", "node-gyp-build": "^4.2.2", "picomatch": "^4.0.2", "resolve-from": "^5.0.0" }, "bin": { "nft": "out/cli.js" } }, "sha512-IWTDeIoWhQ7ZtRO/JRKH+jhmeQvZYhtGPmzw/QGDY+wDCQqfm25P9yIdoAFagu4fWsK4IwZXDFIjrmp5rRm/sA=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.2", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.0" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg=="], "@vitejs/plugin-rsc": ["@vitejs/plugin-rsc@0.5.27", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1", "es-module-lexer": "^2.1.0", "estree-walker": "^3.0.3", "magic-string": "^0.30.21", "srvx": "^0.11.15", "strip-literal": "^3.1.0", "turbo-stream": "^3.2.0", "vitefu": "^1.1.3" }, "peerDependencies": { "react": "*", "react-dom": "*", "react-server-dom-webpack": "*", "vite": "*" }, "optionalPeers": ["react-server-dom-webpack"] }, "sha512-s1fd5DUkPXk86DDHPM/kP93WrvI0MoA8klxdDZmD1fMSaA9xujfgunsm8ZoUH0FemR+63vNalFsIDR0AJH4ktg=="], @@ -1413,7 +1436,7 @@ "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], @@ -1657,12 +1680,14 @@ "rc9": ["rc9@3.0.1", "", { "dependencies": { "defu": "^6.1.6", "destr": "^2.0.5" } }, "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ=="], - "react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], + "react": ["react@19.2.7", "", {}, "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ=="], - "react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], + "react-dom": ["react-dom@19.2.7", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.7" } }, "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ=="], "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], + "react-router": ["react-router@7.16.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-wArC8lVyJb3+jM9OpDyW6hLCizACWkvQR/sSGqSs+o5uEXEtGlqdZ4v8hENR3Jad6i+LRkK93q/+bQAcvl6V1A=="], + "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], "readdir-glob": ["readdir-glob@1.1.3", "", { "dependencies": { "minimatch": "^5.1.0" } }, "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA=="], @@ -1723,6 +1748,8 @@ "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], @@ -1763,7 +1790,7 @@ "sql-escaper": ["sql-escaper@1.3.3", "", {}, "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw=="], - "srvx": ["srvx@0.9.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-RZaxTKJEE/14HYn8COLuUOJAt0U55N9l1Xf6jj+T0GoA01EUH1Xz5JtSUOI+EHn+AEgPCVn7gk6jHJffrr06fQ=="], + "srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], @@ -1965,6 +1992,8 @@ "@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "@babel/core/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], "@babel/core/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], @@ -2009,6 +2038,14 @@ "@fixtures/tanstack-start/@types/node": ["@types/node@22.19.19", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew=="], + "@fixtures/tanstack-start/@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], + + "@fixtures/tanstack-start/@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], + + "@fixtures/tanstack-start/react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="], + + "@fixtures/tanstack-start/react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="], + "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], @@ -2031,6 +2068,8 @@ "@solidjs/start/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "@solidjs/start/srvx": ["srvx@0.9.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-RZaxTKJEE/14HYn8COLuUOJAt0U55N9l1Xf6jj+T0GoA01EUH1Xz5JtSUOI+EHn+AEgPCVn7gk6jHJffrr06fQ=="], + "@solidjs/start/vite": ["vite@7.3.3", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA=="], "@solidjs/vite-plugin-nitro-2/vite": ["vite@7.3.3", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA=="], @@ -2067,8 +2106,6 @@ "@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - "@tanstack/start-plugin-core/srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], - "@tanstack/start-plugin-core/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "@types/babel__core/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], @@ -2077,10 +2114,6 @@ "@vercel/nft/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], - - "@vitejs/plugin-rsc/srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], - "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "archiver-utils/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], @@ -2091,9 +2124,9 @@ "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="], - "h3-v2/rou3": ["rou3@0.8.1", "", {}, "sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA=="], + "h3/srvx": ["srvx@0.9.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-RZaxTKJEE/14HYn8COLuUOJAt0U55N9l1Xf6jj+T0GoA01EUH1Xz5JtSUOI+EHn+AEgPCVn7gk6jHJffrr06fQ=="], - "h3-v2/srvx": ["srvx@0.11.15", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg=="], + "h3-v2/rou3": ["rou3@0.8.1", "", {}, "sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA=="], "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], @@ -2139,8 +2172,6 @@ "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], - "tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], "unctx/unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], @@ -2253,6 +2284,8 @@ "@fixtures/tanstack-start/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "@fixtures/tanstack-start/@vitejs/plugin-react/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "@solidjs/start/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], @@ -2311,6 +2344,8 @@ "@tanstack/directive-functions-plugin/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@tanstack/directive-functions-plugin/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "@tanstack/directive-functions-plugin/@tanstack/router-utils/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], "@tanstack/directive-functions-plugin/@tanstack/router-utils/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], @@ -2319,8 +2354,12 @@ "@tanstack/server-functions-plugin/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@tanstack/server-functions-plugin/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "@tanstack/start-plugin-core/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@tanstack/start-plugin-core/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "archiver-utils/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], "archiver-utils/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], diff --git a/fixtures/react-router-rsc/app/root.tsx b/fixtures/react-router-rsc/app/root.tsx new file mode 100644 index 00000000..23770414 --- /dev/null +++ b/fixtures/react-router-rsc/app/root.tsx @@ -0,0 +1,34 @@ +// oxlint-disable-next-line import/no-unassigned-import +import "./styles.css"; +import { Link, Outlet } from "react-router"; + +export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + React Router Vite + + +
+ +
+ {children} + + + ); +} + +export default function Component() { + return ; +} diff --git a/fixtures/react-router-rsc/app/routes.ts b/fixtures/react-router-rsc/app/routes.ts new file mode 100644 index 00000000..5a2907fb --- /dev/null +++ b/fixtures/react-router-rsc/app/routes.ts @@ -0,0 +1,21 @@ +import type { unstable_RSCRouteConfigEntry } from "react-router"; + +export const routes: Array = [ + { + id: "root", + path: "", + lazy: () => import("./root"), + children: [ + { + id: "home", + index: true, + lazy: () => import("./routes/home"), + }, + { + id: "about", + path: "about", + lazy: () => import("./routes/about"), + }, + ], + }, +]; diff --git a/fixtures/react-router-rsc/app/routes/about.tsx b/fixtures/react-router-rsc/app/routes/about.tsx new file mode 100644 index 00000000..57d3ee21 --- /dev/null +++ b/fixtures/react-router-rsc/app/routes/about.tsx @@ -0,0 +1,17 @@ +"use client"; + +import React from "react"; + +export function Component() { + const [count, setCount] = React.useState(0); + + return ( +
+

About

+

This is the about page, rendered as a client component.

+ +
+ ); +} diff --git a/fixtures/react-router-rsc/app/routes/home.tsx b/fixtures/react-router-rsc/app/routes/home.tsx new file mode 100644 index 00000000..6e26e46c --- /dev/null +++ b/fixtures/react-router-rsc/app/routes/home.tsx @@ -0,0 +1,10 @@ +const Component = () => { + return ( +
+

Home

+

This is the home page, rendered as a React Server Component.

+
+ ); +}; + +export default Component; diff --git a/fixtures/react-router-rsc/app/styles.css b/fixtures/react-router-rsc/app/styles.css new file mode 100644 index 00000000..6d9a020b --- /dev/null +++ b/fixtures/react-router-rsc/app/styles.css @@ -0,0 +1,44 @@ +:root { + color-scheme: light; + font-family: + system-ui, + -apple-system, + "Segoe UI", + Roboto, + sans-serif; +} + +body { + margin: 0; + color: #111; + background: #fff; +} + +nav ul { + display: flex; + gap: 1rem; + list-style: none; + margin: 0; + padding: 1rem 2rem; + border-bottom: 1px solid #e5e5e5; +} + +nav a { + color: #2563eb; + text-decoration: none; +} + +main { + max-width: 48rem; + margin: 0 auto; + padding: 2rem; +} + +button { + cursor: pointer; + padding: 0.5rem 1rem; + border: 1px solid #d4d4d4; + border-radius: 0.375rem; + background: #f5f5f5; + font: inherit; +} diff --git a/fixtures/react-router-rsc/package.json b/fixtures/react-router-rsc/package.json new file mode 100644 index 00000000..69c76f0e --- /dev/null +++ b/fixtures/react-router-rsc/package.json @@ -0,0 +1,32 @@ +{ + "name": "@fixtures/react-router-rsc", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "dev": "vite dev --port 3200", + "build": "vite build", + "preview": "vite preview", + "test": "playwright test", + "test:update-snapshots": "playwright test --update-snapshots", + "pretest": "playwright install chromium" + }, + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router": "7.16.0" + }, + "devDependencies": { + "@cloudflare/workers-types": "catalog:workers", + "@distilled.cloud/cloudflare-runtime": "workspace:*", + "@distilled.cloud/cloudflare-vite-plugin": "workspace:*", + "@distilled.cloud/test-utils": "workspace:*", + "@playwright/test": "catalog:", + "@types/node": "^24.12.0", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "latest", + "@vitejs/plugin-rsc": "latest", + "vite": "catalog:" + } +} diff --git a/fixtures/react-router-rsc/playwright.config.ts b/fixtures/react-router-rsc/playwright.config.ts new file mode 100644 index 00000000..57892762 --- /dev/null +++ b/fixtures/react-router-rsc/playwright.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from "@playwright/test"; + +export default defineConfig({ + testDir: "./test", + timeout: 60_000, + expect: { + timeout: 10_000, + }, + snapshotPathTemplate: "{testDir}/__snapshots__/{testFileName}/{arg}{ext}", + projects: [ + { + name: "chromium", + use: { + browserName: "chromium", + colorScheme: "light", + deviceScaleFactor: 1, + viewport: { width: 1280, height: 720 }, + }, + }, + ], +}); diff --git a/fixtures/react-router-rsc/react-router-vite/entry.browser.tsx b/fixtures/react-router-rsc/react-router-vite/entry.browser.tsx new file mode 100644 index 00000000..1bae409b --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/entry.browser.tsx @@ -0,0 +1,47 @@ +import { + createFromReadableStream, + createTemporaryReferenceSet, + encodeReply, + setServerCallback, +} from "@vitejs/plugin-rsc/browser"; +import { startTransition, StrictMode } from "react"; +import { hydrateRoot } from "react-dom/client"; +import type { DataRouter, unstable_RSCPayload as RSCServerPayload } from "react-router"; +import { + unstable_createCallServer as createCallServer, + unstable_getRSCStream as getRSCStream, + unstable_RSCHydratedRouter as RSCHydratedRouter, +} from "react-router/dom"; + +setServerCallback( + createCallServer({ + createFromReadableStream, + createTemporaryReferenceSet, + encodeReply, + }), +); + +createFromReadableStream(getRSCStream()).then((payload) => { + startTransition(async () => { + const formState = payload.type === "render" ? await payload.formState : undefined; + + hydrateRoot( + document, + + + , + { + // @ts-expect-error - no types for this yet + formState, + }, + ); + }); +}); + +declare let __reactRouterDataRouter: DataRouter; + +if (import.meta.hot) { + import.meta.hot.on("rsc:update", () => { + __reactRouterDataRouter.revalidate(); + }); +} diff --git a/fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx b/fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx new file mode 100644 index 00000000..2fc19475 --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/entry.rsc.tsx @@ -0,0 +1,32 @@ +import { + createTemporaryReferenceSet, + decodeAction, + decodeFormState, + decodeReply, + loadServerAction, + renderToReadableStream, +} from "@vitejs/plugin-rsc/rsc"; +import { unstable_matchRSCServerRequest as matchRSCServerRequest } from "react-router"; +import { routes } from "../app/routes"; + +export function fetchServer(request: Request) { + return matchRSCServerRequest({ + createTemporaryReferenceSet, + decodeAction, + decodeFormState, + decodeReply, + loadServerAction, + request, + routes, + generateResponse(match, options) { + return new Response(renderToReadableStream(match.payload, options), { + status: match.statusCode, + headers: match.headers, + }); + }, + }); +} + +if (import.meta.hot) { + import.meta.hot.accept(); +} diff --git a/fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx b/fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx new file mode 100644 index 00000000..d4559b37 --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/entry.ssr.tsx @@ -0,0 +1,29 @@ +import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr"; +import { renderToReadableStream as renderHTMLToReadableStream } from "react-dom/server.edge"; +import { + unstable_routeRSCServerRequest as routeRSCServerRequest, + unstable_RSCStaticRouter as RSCStaticRouter, +} from "react-router"; + +export default async function handler( + request: Request, + serverResponse: Response, +): Promise { + const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent("index"); + + return await routeRSCServerRequest({ + request, + serverResponse, + createFromReadableStream, + async renderHTML(getPayload, options) { + const payload = getPayload(); + + return await renderHTMLToReadableStream(, { + ...options, + bootstrapScriptContent, + signal: request.signal, + formState: await payload.formState, + }); + }, + }); +} diff --git a/fixtures/react-router-rsc/react-router-vite/entry.worker.tsx b/fixtures/react-router-rsc/react-router-vite/entry.worker.tsx new file mode 100644 index 00000000..8c6886a7 --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/entry.worker.tsx @@ -0,0 +1,12 @@ +import type * as EntrySsr from "./entry.ssr"; +import { fetchServer } from "./entry.rsc"; + +// The distilled Cloudflare worker wrapper expects a `{ fetch }` default export. +// The worker runs in the `rsc` environment and loads the `ssr` environment at +// runtime to render HTML from the RSC payload. +export default { + async fetch(request: Request): Promise { + const ssr = await import.meta.viteRsc.loadModule("ssr", "index"); + return ssr.default(request, await fetchServer(request)); + }, +}; diff --git a/fixtures/react-router-rsc/react-router-vite/types.d.ts b/fixtures/react-router-rsc/react-router-vite/types.d.ts new file mode 100644 index 00000000..bb5578e1 --- /dev/null +++ b/fixtures/react-router-rsc/react-router-vite/types.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/fixtures/react-router-rsc/test/__snapshots__/smoke.test.ts/about.png b/fixtures/react-router-rsc/test/__snapshots__/smoke.test.ts/about.png new file mode 100644 index 0000000000000000000000000000000000000000..da3f527579bef3e37a17beed2bf64d400dbf0b12 GIT binary patch literal 15226 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 { + const [rsc, ssr] = await Promise.all([ + miniflareModulesFromDirectory(path.resolve(root, "dist/rsc"), "rsc"), + miniflareModulesFromDirectory(path.resolve(root, "dist/ssr"), "ssr"), + ]); + const modules = [...rsc, ...ssr]; + const entryIndex = modules.findIndex((module) => module.path === WORKER_ENTRY); + if (entryIndex === -1) { + throw new Error(`Worker entry "${WORKER_ENTRY}" not found in build output`); + } + // Miniflare treats the first module as the Worker entrypoint. + modules.unshift(modules.splice(entryIndex, 1)[0]); + + miniflare = await createMiniflare({ + modules, + compatibilityDate: "2026-03-10", + compatibilityFlags: ["nodejs_compat"], + assets: { + directory: client, + routerConfig: { + has_user_worker: true, + invoke_user_worker_ahead_of_assets: false, + debug: true, + }, + assetConfig: { + html_handling: "auto-trailing-slash", + not_found_handling: "none", + debug: true, + has_static_routing: false, + }, + }, + }); +}); + +test.afterAll(async () => { + await miniflare?.[Symbol.asyncDispose](); +}); + +test("renders the homepage and hydrates client routes", async ({ page }) => { + const response = await page.goto(miniflare.url.toString()); + expect(response?.status()).toBe(200); + await page.waitForLoadState("networkidle"); + await page.evaluate(() => document.fonts.ready); + + await expect(page).toHaveScreenshot("index.png", { + animations: "disabled", + maxDiffPixelRatio: 0.03, + }); + + await page.click("a[href='/about']"); + await page.waitForURL("**/about"); + await page.evaluate(() => document.fonts.ready); + + await expect(page).toHaveScreenshot("about.png", { + animations: "disabled", + maxDiffPixelRatio: 0.03, + }); + + expect(await page.textContent("button.counter")).toBe("Count is 0"); + await page.click("button.counter"); + expect(await page.textContent("button.counter")).toBe("Count is 1"); +}); diff --git a/fixtures/react-router-rsc/tsconfig.json b/fixtures/react-router-rsc/tsconfig.json new file mode 100644 index 00000000..1950c5a2 --- /dev/null +++ b/fixtures/react-router-rsc/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "allowImportingTsExtensions": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "skipLibCheck": true, + "verbatimModuleSyntax": true, + "noEmit": true, + "moduleResolution": "Bundler", + "module": "ESNext", + "target": "ESNext", + "lib": ["ESNext", "DOM"], + "jsx": "react-jsx" + } +} diff --git a/fixtures/react-router-rsc/vite.config.ts b/fixtures/react-router-rsc/vite.config.ts new file mode 100644 index 00000000..5ae53e4f --- /dev/null +++ b/fixtures/react-router-rsc/vite.config.ts @@ -0,0 +1,33 @@ +import cloudflare from "@distilled.cloud/cloudflare-vite-plugin"; +import react from "@vitejs/plugin-react"; +import rsc from "@vitejs/plugin-rsc"; +import { defineConfig } from "vite"; + +// Minimal React Router app (hand-rolled on @vitejs/plugin-rsc) wired to the +// distilled Cloudflare vite plugin. The Worker IS the `rsc` environment; its +// fetch handler loads the `ssr` environment at runtime via +// `import.meta.viteRsc.loadModule("ssr", ...)`. The new `viteEnvironments` +// option declares this entry/child topology to the plugin. +export default defineConfig({ + plugins: [ + react(), + rsc({ + serverHandler: false, + entries: { + client: "./react-router-vite/entry.browser.tsx", + ssr: "./react-router-vite/entry.ssr.tsx", + rsc: "./react-router-vite/entry.worker.tsx", + }, + }), + cloudflare({ + main: "./react-router-vite/entry.worker.tsx", + compatibilityDate: "2026-03-10", + compatibilityFlags: ["nodejs_compat"], + viteEnvironments: { entry: "rsc", children: ["ssr"] }, + worker: { name: "fixtures-react-router-rsc", bindings: [] }, + }), + ], + optimizeDeps: { + include: ["react-router", "react-router/internal/react-server-client"], + }, +}); diff --git a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts index c13375cf..13b7baf8 100644 --- a/packages/cloudflare-rolldown-plugin/src/plugins/options.ts +++ b/packages/cloudflare-rolldown-plugin/src/plugins/options.ts @@ -78,6 +78,13 @@ export const optionsPlugin = createPlugin<"options", OptionsApi>("options", (plu ? (pluginOptions.main ?? defaultEnvironmentEntries(name, userConfig)) : (defaultEnvironmentEntries(name, userConfig) ?? pluginOptions.main); return { + // Bake `process.env.NODE_ENV` (and friends) into the worker build. + // workerd has no value for it at runtime, so without this libraries + // like React fall back to their development builds. Setting `define` + // at the environment level applies it even though `keepProcessEnv` + // stays `true` (so other `process.env` access is preserved at + // runtime for `nodejs_compat`). + define, resolve: { noExternal: true, conditions: [...DEFAULT_RESOLVE_CONDITION_NAMES, "development|production"],