diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/.gitignore b/inlang/packages/paraglide/paraglide-js/benchmark/.gitignore new file mode 100644 index 0000000000..2a40958039 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# dynamically generated +src/pages +src/i18n/generated.ts +messages +project.inlang \ No newline at end of file diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/README.md b/inlang/packages/paraglide/paraglide-js/benchmark/README.md new file mode 100644 index 0000000000..02529f5478 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/README.md @@ -0,0 +1,109 @@ +# Benchmark + +## Introduction + +The internationalization (i18n) library you choose can significantly impact your application's bundle size and overall performance. + +This benchmark provides data-driven insights comparing Paraglide-JS with i18next, one of the most popular i18n solutions. + +### What is Being Tested + +This benchmark evaluates the bundle size implications of both libraries across various scenarios: + +- **Number of Locales**: How does an i18n library scale with the number of locales? +- **Number of Message per page**: How does an i18n library scale with the number of messages that are used on a given page? +- **Library Implementation Variants**: Testing different implementation approaches: + - **Paraglide**: + - `default`: Standard implementation + - `experimental-`: Experimental implementation with per-locale splitting + - **i18next**: + - `http-backend`: Using HTTP backend for loading translations +- **Namespace Size**: Testing how the total available messages in a namespace affects bundle size + +### Key Considerations + +#### The Paraglide Approach + +Paraglide takes a different approach to i18n by compiling messages into tree-shakable functions. Bundlers are able to tree-shake and include only the used messages for any given page. This has important implications. + +#### Work in Progress + +We are actively working on per-locale splitting to further optimize bundle size for applications with many languages and messages. Find more information in issue [#88](https://github.com/opral/inlang-paraglide-js/issues/88). + +## Setup + +The benchmark creates a static website for each configuration (library variant, number of locales, messages per page, and namespace size). Each website is loaded in a headless browser, and the total transfer size is measured. + +### Library modes + +Each library is tested in different modes: + +- **Paraglide**: + - **default**: Out of the box Paraglide JS with no additional compiler options. + - ****: Mode with a [compiler option](https://inlang.com/m/gerre34r/library-inlang-paraglideJs/compiler-options) that is being tested. +- **i18next**: + - **default**: The default i18next implementation [source](https://www.i18next.com/overview/getting-started#basic-sample). + - **http-backend**: i18next implementation using HTTP backend for loading translations on dmeand [source](https://github.com/i18next/i18next-http-backend). + +### Metrics + +- **Total Transfer Size**: The total transfer size of the website. +- **Messages**: The number of messages per page. +- **Namespace Size**: The total number of messages in the namespace. +- **Locales**: The number of locales. + +### Limitations + +**Choosing the number of messages and namespace varies between projects** + +Some teams use per component namespacing while other teams have one namespace for their entire project. In cal.com's case, every component that uses i18n [loads at least 3000 messages per locale](https://github.com/calcom/cal.com/blob/b5e08ea80ffecff04363a18789491065dd6ccc0b/apps/web/public/static/locales/en/common.json). + +To the point of the problem: Avoiding manual chunking of messages into namespaces is the benefit of Paraglide JS. The bundler tree-shakes all unused messages, making namespaces redundant. + + +## Results + + +`Locales: 5` +`Messages: 200` +`Namespace Size: 500` + +| Library | Total Transfer Size | +|---------------------------------------------------|---------------------| +| paraglide (experimental-middleware-optimizations) | 31.5 KB | +| paraglide (default) | 90.1 KB | +| i18next (default) | 694.3 KB | +| i18next (http-backend) | 191.0 KB | + + +`Locales: 10` +`Messages: 200` +`Namespace Size: 500` + +| Library | Total Transfer Size | +|---------------------------------------------------|---------------------| +| paraglide (experimental-middleware-optimizations) | 31.6 KB | +| paraglide (default) | 148.3 KB | +| i18next (default) | 694.3 KB | +| i18next (http-backend) | 191.0 KB | + + +`Locales: 20` +`Messages: 200` +`Namespace Size: 500` + +| Library | Total Transfer Size | +|---------------------------------------------------|---------------------| +| paraglide (experimental-middleware-optimizations) | 31.7 KB | +| paraglide (default) | 266.1 KB | +| i18next (default) | 694.3 KB | +| i18next (http-backend) | 191.1 KB | + + +## Contributing + +Contributions to improve the benchmark are welcome. + +1. adjust the build matrix in `build.config.ts` +2. run `pnpm run bench` to build the benchmark +3. run `pnpm run preview` to preview the results diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/bench.ts b/inlang/packages/paraglide/paraglide-js/benchmark/bench.ts new file mode 100644 index 0000000000..2335f5bb4f --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/bench.ts @@ -0,0 +1,247 @@ +import { chromium } from "playwright"; +import { builds, buildConfigToString } from "./build.config.ts"; +import { startServer } from "./server.ts"; +import fs from "node:fs"; +import { runBuilds } from "./build.ts"; +import csvToMarkdown from "csv-to-markdown-table"; + +// Function to benchmark network transfer +async function benchmarkBuild(url: string): Promise { + const browser = await chromium.launch(); + const page = await browser.newPage(); + + // Track responses and their sizes + const responsePromises: Promise[] = []; + page.on("response", (response) => { + // Only add successful responses + if (response.status() >= 200 && response.status() < 400) { + const promise = response + .body() + .then((body) => body.length) + .catch(() => 0); + responsePromises.push(promise); + } + }); + + console.log(`Benchmarking ${url}`); + + // Format URL properly with server address + await page.goto(url, { waitUntil: "networkidle" }); + + // Wait for all response promises to resolve before closing the browser + const sizes = await Promise.all(responsePromises); + const totalBytes = sizes.reduce((sum, size) => sum + size, 0); + + await browser.close(); + + return totalBytes; // Return bytes +} + +// Format bytes to human-readable format (B, KB) +function formatBytes(bytes: number): string { + if (bytes < 1024) { + return `${bytes} B`; + } else { + return `${(bytes / 1024).toFixed(1)} KB`; + } +} + +async function runBenchmarks() { + await runBuilds(); + + // Get unique libraries, library modes, locales, messages + const libraries = [...new Set(builds.map((build) => build.library))].sort( + (a, b) => + a === "paraglide" ? -1 : b === "paraglide" ? 1 : a.localeCompare(b) + ); + + // Get library modes grouped by library + const libraryModes: Record = {}; + for (const build of builds) { + if (!libraryModes[build.library]) { + libraryModes[build.library] = []; + } + if (!libraryModes[build.library].includes(build.libraryMode)) { + libraryModes[build.library].push(build.libraryMode); + } + } + + const locales = [...new Set(builds.map((build) => build.locales))].sort( + (a, b) => a - b + ); + const messages = [...new Set(builds.map((build) => build.messages))].sort( + (a, b) => a - b + ); + const namespaceSizes = [ + ...new Set(builds.map((build) => build.namespaceSize)), + ] + .filter((size): size is number => size !== undefined && size !== null) + .sort((a, b) => a - b); + + const port = 8110; + + const server = startServer(port); // Start server + + // Create a map of unique library+mode combinations + const libraryModeMap = new Map(); + for (const build of builds) { + const key = `${build.library}-${build.libraryMode}`; + const displayName = `${build.library} (${build.libraryMode})`; + libraryModeMap.set(key, displayName); + } + + // Create results object to store benchmark data + // Structure: locale -> message -> namespaceSize (as string) -> libraryModeKey -> size + const results: Record< + number, + Record + > + > + > = {}; + + // Initialize results structure + for (const locale of locales) { + results[locale] = {}; + for (const message of messages) { + results[locale][message] = {}; + // Use 'default' as the key when namespaceSize is undefined + for (const namespaceSize of [...namespaceSizes, undefined]) { + const nsKey = namespaceSize?.toString() || "default"; + results[locale][message][nsKey] = {}; + for (const [libraryModeKey] of libraryModeMap) { + results[locale][message][nsKey][libraryModeKey] = 0; + } + } + } + } + + // Run benchmarks and collect results + const benchmarkPromises: Array> = []; + for (const build of builds) { + const name = buildConfigToString(build); + const promise = benchmarkBuild(`http://localhost:${port}/${name}`).then( + (size) => { + const nsKey = build.namespaceSize?.toString() || "default"; + results[build.locales][build.messages][nsKey][ + `${build.library}-${build.libraryMode}` + ] = size; + } + ); + benchmarkPromises.push(promise); + } + + // Wait for all benchmarks to complete + await Promise.all(benchmarkPromises); + + server.close(); + + // Generate markdown with tables + let markdownOutput = "# Benchmark Results\n\n"; + + // Create a unique set of configurations + type ConfigKey = string; + type LibraryResults = Record; // library-mode key -> size + + // Group results by configuration + const configResults = new Map(); + + // Collect all configurations and their results + for (const locale of locales) { + for (const message of messages) { + for (const namespaceSize of [...namespaceSizes, undefined]) { + const nsKey = namespaceSize?.toString() || "default"; + const nsValue = namespaceSize !== undefined ? namespaceSize : message; + + // Create a unique key for this configuration + const configKey = `l${locale}-m${message}-ns${nsValue}`; + + if (!configResults.has(configKey)) { + configResults.set(configKey, { + locale, + message, + namespaceSize: nsValue, + results: {} + }); + } + + // Add library results for this configuration + const libraryResults = configResults.get(configKey)!.results; + for (const [key, _] of libraryModeMap) { + libraryResults[key] = results[locale][message][nsKey][key]; + } + } + } + } + + // Sort configurations + const sortedConfigs = Array.from(configResults.entries()) + .sort((a, b) => { + // First sort by locale + if (a[1].locale !== b[1].locale) { + return a[1].locale - b[1].locale; + } + // Then by message count + if (a[1].message !== b[1].message) { + return a[1].message - b[1].message; + } + // Finally by namespace size + return (a[1].namespaceSize || 0) - (b[1].namespaceSize || 0); + }); + + // Generate a section for each configuration + let runNumber = 1; + for (const [configKey, config] of sortedConfigs) { + // Skip configurations with no results + const hasResults = Object.values(config.results).some(size => size > 0); + if (!hasResults) continue; + + // Add configuration details as code blocks + markdownOutput += `\`Locales: ${config.locale}\` \n`; + markdownOutput += `\`Messages: ${config.message}\` \n`; + markdownOutput += `\`Namespace Size: ${config.namespaceSize}\` \n\n`; + + // Create table for this configuration + let tableData: string[][] = []; + tableData.push(["Library", "Total Transfer Size"]); + + // Sort libraries (paraglide first, then i18next) + const sortedLibraryEntries = Object.entries(config.results) + .sort((a, b) => { + if (a[0].startsWith("paraglide")) return -1; // paraglide comes first + if (b[0].startsWith("paraglide")) return 1; + return a[0].localeCompare(b[0]); + }); + + // Add library results + for (const [key, size] of sortedLibraryEntries) { + if (size === 0) continue; // Skip libraries with no results + + const displayName = libraryModeMap.get(key) || key; + tableData.push([displayName, formatBytes(size)]); + } + + // Convert to CSV format for csvToMarkdown + const csvData = tableData.map(row => row.join(',')).join('\n'); + + // Convert to markdown table using csvToMarkdown + const markdownTable = csvToMarkdown(csvData, ',', true); + + markdownOutput += markdownTable + '\n\n'; + runNumber++; + } + + // Write the markdown tables to a file for easy copying + fs.writeFileSync("benchmark-results.md", markdownOutput); + console.log("\nResults saved to benchmark-results.md"); + + return markdownOutput; +} + +runBenchmarks(); diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/benchmark-results.md b/inlang/packages/paraglide/paraglide-js/benchmark/benchmark-results.md new file mode 100644 index 0000000000..8586347197 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/benchmark-results.md @@ -0,0 +1,38 @@ +# Benchmark Results + +`Locales: 5` +`Messages: 200` +`Namespace Size: 500` + +| Library | Total Transfer Size | +|---------------------------------------------------|---------------------| +| paraglide (experimental-middleware-optimizations) | 31.5 KB | +| paraglide (default) | 90.1 KB | +| i18next (default) | 694.3 KB | +| i18next (http-backend) | 191.0 KB | + + +`Locales: 10` +`Messages: 200` +`Namespace Size: 500` + +| Library | Total Transfer Size | +|---------------------------------------------------|---------------------| +| paraglide (experimental-middleware-optimizations) | 31.6 KB | +| paraglide (default) | 148.3 KB | +| i18next (default) | 694.3 KB | +| i18next (http-backend) | 191.0 KB | + + +`Locales: 20` +`Messages: 200` +`Namespace Size: 500` + +| Library | Total Transfer Size | +|---------------------------------------------------|---------------------| +| paraglide (experimental-middleware-optimizations) | 31.7 KB | +| paraglide (default) | 266.1 KB | +| i18next (default) | 694.3 KB | +| i18next (http-backend) | 191.1 KB | + + diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/build.config.ts b/inlang/packages/paraglide/paraglide-js/benchmark/build.config.ts new file mode 100644 index 0000000000..caa4a487f0 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/build.config.ts @@ -0,0 +1,156 @@ +import { type UserConfig } from "vite"; + +/** + * The total number of messages in the namespace. + * This can be larger than the number of messages rendered on the page. + * If not specified, it defaults to the same value as messages. + */ +export const builds: BuildConfig[] = [ + ...createBuildMatrix({ + libraries: { + paraglide: ["default", "experimental-middleware-optimizations"], + i18next: ["default", "http-backend"], + }, + locales: [5, 10, 20], + messages: [200], + percentDynamic: 20, + namespaceSizes: [500], + }), +]; + +export function createViteConfig(args: { + outdir: string; + mode: string; + library: string; + libraryMode: string; + base: string; + buildName: string; + generateAboutPage: boolean; +}): UserConfig { + return { + logLevel: "error", + base: args.base, + build: { + outDir: args.outdir, + minify: true, + target: "es2024", + // don't load the module preload to keep the bundle free + // from side effects that could affect the benchmark + // + // doesn't work because of https://github.com/vitejs/vite/issues/18551 + modulePreload: false, + }, + define: { + // using process.env to make ssg build work + "process.env.BASE": JSON.stringify(args.base), + "process.env.MODE": JSON.stringify(args.mode), + "process.env.BUILD_NAME": JSON.stringify(args.buildName), + "process.env.LIBRARY": JSON.stringify(args.library), + "process.env.LIBRARY_MODE": JSON.stringify(args.libraryMode), + "process.env.GENERATE_ABOUT_PAGE": JSON.stringify(args.generateAboutPage), + "process.env.IS_CLIENT": JSON.stringify("true"), + }, + }; +} + +export function createBuildMatrix(config: { + libraries: Record; + locales: Array; + messages: Array; + percentDynamic: number; + generateAboutPage?: boolean; + namespaceSizes?: Array; +}): BuildConfig[] { + const builds = []; + + for (const [library, modes] of Object.entries(config.libraries)) { + for (const mode of modes) { + for (const locale of config.locales) { + for (const message of config.messages) { + if (config.namespaceSizes && config.namespaceSizes.length > 0) { + // Create builds with different namespace sizes + for (const namespaceSize of config.namespaceSizes) { + // Throw error if namespace size is lower than message count + if (namespaceSize < message) { + throw new Error( + `Namespace size (${namespaceSize}) cannot be lower than message count (${message})` + ); + } + + builds.push({ + library: library as BuildConfig["library"], + libraryMode: mode, + locales: locale, + messages: message, + namespaceSize, + percentDynamic: config.percentDynamic, + generateAboutPage: config.generateAboutPage ?? true, + }); + } + } else { + // Default behavior - namespace size equals message count + builds.push({ + library: library as BuildConfig["library"], + libraryMode: mode, + locales: locale, + messages: message, + percentDynamic: config.percentDynamic, + generateAboutPage: config.generateAboutPage ?? true, + }); + } + } + } + } + } + return builds; +} + +export type BuildConfig = { + locales: number; + messages: number; + percentDynamic: number; + library: "paraglide" | "i18next"; + /** + * The mode for the specific library (e.g., "default", "experimental-", "http-backend") + */ + libraryMode: string; + /** + * Mainly useful for testing routing. + */ + generateAboutPage: boolean; + /** + * The total number of messages in the namespace. + * This can be larger than the number of messages rendered on the page. + * If not specified, it defaults to the same value as messages. + */ + namespaceSize?: number; +}; + +export function buildConfigToString(config: BuildConfig): string { + return `l${config.locales}-m${config.messages}-ns${config.namespaceSize}-d${config.percentDynamic}-${config.library}-${config.libraryMode}`; +} + +export function buildConfigFromString(str: string): BuildConfig { + const parts = str.split("-"); + let locales, messages, namespaceSize, percentDynamic, library, libraryMode; + + // Extract parts based on prefix + for (const part of parts) { + if (part.startsWith("l")) locales = Number(part.substring(1)); + else if (part.startsWith("m")) messages = Number(part.substring(1)); + else if (part.startsWith("ns")) namespaceSize = Number(part.substring(2)); + else if (part.startsWith("d")) percentDynamic = Number(part.substring(1)); + else if (part === "paraglide" || part === "i18next") library = part; + else libraryMode = part; + } + + return { + locales: locales!, + messages: messages!, + namespaceSize, + percentDynamic: percentDynamic!, + library: library as BuildConfig["library"], + libraryMode: libraryMode!, + generateAboutPage: true, + }; +} diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/build.samples.ts b/inlang/packages/paraglide/paraglide-js/benchmark/build.samples.ts new file mode 100644 index 0000000000..4c8ac893f8 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/build.samples.ts @@ -0,0 +1,142 @@ +export const sampleMessages = [ + "The power of automation at your fingertips", + "Empowering teams, driving results", + "The power of automation at your fingertips.", + "The missing piece in your workflow.", + "Designed for teams, built for impact.", + "Seamless, intuitive, and powerful.", + "Innovation that drives results.", + "A simple yet powerful solution.", + "Your vision, our expertise.", + "A productivity suite that delivers.", + "Work smarter, not harder.", + "Empowering teams, driving results.", + "Helping businesses grow and thrive.", + "An effortless way to manage work.", + "Turning ideas into reality.", + "A new standard in productivity.", + "Maximize your potential with us.", + "Collaborate like never before.", + "Your success starts here.", + "Your success starts here.", + "Simplicity that scales with you.", + "We make productivity simple.", + "From concept to execution, we've got you covered.", + "We make productivity simple.", + "Your digital workspace, perfected.", + "Where efficiency meets innovation.", + "Seamless, intuitive, and powerful.", + "Join thousands of satisfied users today.", + "Seamless collaboration at your fingertips.", + "Work better, faster, and smarter.", + "Your goals, our mission.", + "Seamless collaboration at your fingertips.", + "Elevate your experience with our technology.", + "Results-driven technology for modern teams.", + "Transform the way you collaborate.", + "Designed for doers, by doers.", + "Smart solutions for modern challenges.", + "Future-proof your workflow today.", + "Make the most of your time.", + "Your journey to success starts here.", + "Step up your game with our tools.", + "Optimize your day with us.", + "Helping businesses grow and thrive.", + "The missing piece in your workflow.", + "A productivity suite that delivers.", + "Solutions tailored for your needs.", + "Powerful features, easy to use.", + "Smarter tools for a smarter you.", + "Collaborate like never before.", + "Streamline your processes with ease.", + "Future-proof your workflow today.", + "Efficiency meets simplicity.", + "Designed for doers, by doers.", + "Transform the way you work with our tools.", + "Your vision, our expertise.", + "Your digital workspace, perfected.", + "Take your productivity to new heights.", + "From concept to execution, we've got you covered.", + "The future of productivity is now.", + "Your goals, our mission.", + "Transform the way you collaborate.", + "Maximize your potential with us.", + "Optimize your day with us.", + "Innovation that drives results.", + "Your journey to success starts here.", + "Efficiency meets simplicity.", + "Workflows that work for you.", + "Smart solutions for modern challenges.", + "Say goodbye to inefficiencies.", + "Streamline your processes with ease.", + "Smart solutions for modern challenges.", + "Turning ideas into reality.", + "Get started with our intuitive tools.", + "Effortless organization for every team.", + "Say hello to effortless productivity.", + "Where innovation meets execution.", + "Designed for efficiency, built for success.", + "Effortless organization for every team.", + "Boost efficiency with our solutions.", + "An effortless way to manage work.", + "Workflows that work for you.", + "Achieve more with less effort.", + "Smart features for smarter work.", + "Empowering teams, driving results.", + "Streamline your processes with ease.", + "Built for today, ready for tomorrow.", + "Because time is money.", + "Built for today, ready for tomorrow.", + "A new standard in productivity.", + "Seamless collaboration at your fingertips.", + "Your partner in growth and innovation.", + "Smarter tools for a smarter you.", + "Transform the way you work with our tools.", + "Innovation meets simplicity.", + "Welcome to smarter working.", + "Your workflow, supercharged.", + "Optimize your workflow like never before.", + "Maximize your potential with us.", + "Designed for teams, built for impact.", + "Empowering teams, driving results.", + "Your success starts here.", +]; + +export const sampleLocales = [ + "en", + "de", + "es", + "fr", + "it", + "nl", + "pt", + "ru", + "zh", + "ja", + "ko", + "ar", + "tr", + "pl", + "sv", + "da", + "fi", + "no", + "cs", + "hi", + "id", + "th", + "el", + "he", + "hu", +]; + +export const sampleInlangSettings = { + baseLocale: "en", + locales: ["en", "de"], + modules: [ + "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@6/dist/index.js", + ], + "plugin.inlang.i18next": { + pathPattern: "./messages/{locale}.json", + }, +}; diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/build.ts b/inlang/packages/paraglide/paraglide-js/benchmark/build.ts new file mode 100644 index 0000000000..1c1aecbc27 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/build.ts @@ -0,0 +1,242 @@ +import { build } from "vite"; +import fs from "node:fs/promises"; +import { normalize } from "node:path"; +import { + builds, + buildConfigToString, + createViteConfig, +} from "./build.config.ts"; +import { compile } from "@inlang/paraglide-js"; +import { + sampleMessages, + sampleLocales, + sampleInlangSettings, +} from "./build.samples.ts"; + +export const runBuilds = async () => { + // Clean the dist directory + await fs.rm("./dist", { recursive: true, force: true }); + + // copy the message translation files in case a libary needs them + await fs.mkdir("./dist"); + + for (const [i, b] of builds.entries()) { + // Format library name with mode for display + const libraryDisplay = `${b.library} (${b.libraryMode})`; + + console.log(`Build ${i + 1} of ${builds.length}:`); + console.table([ + { + Locales: b.locales, + Messages: b.messages, + "Namespace Size": b.namespaceSize || b.messages, + "% Dynamic": b.percentDynamic, + Library: libraryDisplay, + }, + ]); + const locales = sampleLocales.slice(0, b.locales); + + const base = buildConfigToString(b); + const outdir = `./dist/${base}`; + + const numDynamic = Math.floor((b.percentDynamic / 100) * b.messages); + + // created generated i18n file + + const libFile = await fs.readFile(`./src/i18n/${b.library}.ts`, "utf-8"); + await fs.writeFile( + `./src/i18n/generated.ts`, + libFile + "\nexport const locales = " + JSON.stringify(locales) + ";" + ); + + // generate messages + const keys = await generateMessages({ + locales, + numMessages: b.messages, + numDynamic, + namespaceSize: b.namespaceSize, + }); + + if (b.library === "paraglide") { + await compileParaglide({ locales, mode: b.libraryMode }); + } + + // generate pages + + const staticPaths = ["/"]; + + if (b.generateAboutPage) { + staticPaths.push("/about"); + } + + await generatePage({ + path: "/index.ts", + keys, + library: b.library, + }); + + if (b.generateAboutPage) { + await generatePage({ + path: "/about/index.ts", + keys, + library: b.library, + }); + } + + // client side build + await build( + createViteConfig({ + buildName: base, + outdir, + base, + mode: "ssg", // Always use SSG mode + library: b.library, + libraryMode: b.libraryMode, // Pass libraryMode to Vite config + generateAboutPage: b.generateAboutPage, + }) + ); + + // server side build + process.env.BASE = base; + process.env.LIBRARY = b.library; + process.env.LIBRARY_MODE = b.libraryMode; // Set libraryMode in environment + process.env.IS_CLIENT = "false"; + // const rootHtml = await fs.readFile(`./${outdir}/index.html`, "utf-8"); + const { handle } = await import(`./src/entry-server.ts`); + + // render each route + for (const path of staticPaths) { + const response = await handle( + new Request(new URL(path, "http://example.com")) + ); + const html = await response.text(); + const outputPath = normalize(`./${outdir}/${path}/index.html`); + await fs.mkdir(normalize(`./${outdir}/${path}`), { recursive: true }); + await fs.writeFile(outputPath, html, "utf-8"); + } + await fs.cp("./messages", `./dist/${base}/messages`, { recursive: true }); + } +}; + +async function generatePage(args: { + path: string; + keys: string[]; + library: string; +}) { + // import library specific expressions + const { refMessage, importExpression } = await import( + `./src/i18n/${args.library}.ts` + ); + + let paragraphs: string[] = []; + + for (const key of args.keys) { + if (key.endsWith("dynamic")) { + paragraphs.push(`\`

\${${refMessage(key, { name: "Peter" })}}

\``); + } else { + paragraphs.push(`\`

\${${refMessage(key)}}

\``); + } + } + + const basePath = args.path === "/index.ts" ? ".." : "../.."; + + const page = `${importExpression().replace("", basePath)} + +export function Page(): string { + return shuffleArray([ + ${paragraphs.join(",\n")} + ]).join("\\n"); +}; + +// shuffle the paragraphs +// to have a visible difference when switching locales +function shuffleArray (array: any[]) { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; +}; +`; + await fs.mkdir(`src/pages/about`, { recursive: true }); + await fs.writeFile(`./src/pages${args.path}`, page); +} + +/** + * Generates messages for the given locales. + * + * @example + * ./ + */ +async function generateMessages(args: { + locales: string[]; + numMessages: number; + numDynamic: number; + namespaceSize?: number; +}) { + // Use namespaceSize if provided, otherwise default to numMessages + const totalMessages = args.namespaceSize || args.numMessages; + + // Validate that namespace size is not lower than the number of messages + if (args.namespaceSize && args.namespaceSize < args.numMessages) { + throw new Error( + `Namespace size (${args.namespaceSize}) cannot be lower than message count (${args.numMessages})` + ); + } + + let messages: Record = {}; + let msgI = 0; + + // Generate all messages for the namespace + for (let i = 0; i < totalMessages; i++) { + if (i < args.numDynamic) { + messages[`message${i}dynamic`] = sampleMessages[msgI] + " {{name}}"; + } else { + messages[`message${i}`] = sampleMessages[msgI]; + } + msgI++; + // reset the message index to 0 to + // loop over the samples again + if (msgI === sampleMessages.length) { + msgI = 0; + } + } + + for (const locale of args.locales) { + await fs.mkdir(`./messages`, { recursive: true }); + await fs.writeFile( + `./messages/${locale}.json`, + JSON.stringify(messages, null, 2) + ); + } + + // Return only the keys that should be rendered on the page + // This is limited to numMessages, even if namespaceSize is larger + return Object.keys(messages).slice(0, args.numMessages); +} + +async function compileParaglide(args: { locales: string[]; mode: string }) { + await fs.mkdir(`./project.inlang`, { recursive: true }); + await fs.writeFile( + `./project.inlang/settings.json`, + JSON.stringify( + { + ...sampleInlangSettings, + locales: args.locales, + }, + null, + 2 + ) + ); + await compile({ + project: "./project.inlang", + outdir: "./src/paraglide", + isServer: "!process.env.IS_CLIENT", + enableMiddlewareOptimizations: + args.mode === "experimental-middleware-optimizations", + }); +} + +if (process.env.RUN_BUILD) { + await runBuilds(); +} diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/index.html b/inlang/packages/paraglide/paraglide-js/benchmark/index.html new file mode 100644 index 0000000000..70c4431bc4 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/index.html @@ -0,0 +1,12 @@ + + + + + + <!--app-title--> + + +
+ + + diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/package.json b/inlang/packages/paraglide/paraglide-js/benchmark/package.json new file mode 100644 index 0000000000..048572ded3 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/package.json @@ -0,0 +1,29 @@ +{ + "name": "@inlang/paraglide-js-benchmark", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite dev", + "prebench": "pnpm exec playwright install --only-shell chromium", + "bench": "node --experimental-strip-types ./bench.ts", + "build": "RUN_BUILD=true node --experimental-strip-types ./build.ts", + "preview": "PREVIEW=true node --experimental-strip-types ./server.ts", + "clean": "rm -rf ./dist ./node_modules ./src/paraglide" + }, + "devDependencies": { + "@hono/node-server": "^1.13.7", + "@inlang/paraglide-js": "workspace:*", + "csv-to-markdown-table": "^1.4.1", + "hono": "^4.6.11", + "playwright": "^1.50.1", + "typescript": "^5.8.0", + "vite": "^6.0.7" + }, + "dependencies": { + "i18next": "^24.2.2", + "i18next-browser-languagedetector": "^8.0.4", + "i18next-fs-backend": "^2.6.0", + "i18next-http-backend": "^3.0.2" + } +} diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/server.ts b/inlang/packages/paraglide/paraglide-js/benchmark/server.ts new file mode 100644 index 0000000000..2891ff1c12 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/server.ts @@ -0,0 +1,43 @@ +import { Hono } from "hono"; +import { serveStatic } from "@hono/node-server/serve-static"; +import { serve } from "@hono/node-server"; +import path from "node:path"; +import fs from "node:fs"; + +export function startServer(port: number) { + const app = new Hono(); + + // Serve multiple builds under different paths + const distPath = path.resolve("dist"); + const directories = fs.readdirSync(distPath); + + for (const dir of directories) { + app.use(`/${dir}*`, serveStatic({ root: "./dist" })); + } + + app.get("/", (c) => + c.html(` +

Paraglide JS Benchmark

+

Available Builds:

+ ${directories.map((dir) => `${dir}`).join("
")} +`) + ); + + app.use("*", async (c) => { + return c.html( + `

404 Not Found. You likely requested an spa build from an ssr route.

Go back to the homepage

`, + 404 + ); + }); + + const server = serve({ + fetch: app.fetch, + port, + }); + console.log(`Server is running at http://localhost:${port}`); + return server; +} + +if (process.env.PREVIEW) { + startServer(3005); +} diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/src/app.ts b/inlang/packages/paraglide/paraglide-js/benchmark/src/app.ts new file mode 100644 index 0000000000..4daf52a281 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/src/app.ts @@ -0,0 +1,34 @@ +import { init, getLocale, locales, setLocale } from "./i18n/generated.ts"; + +// some libraries require async initialization (e.g. i18next) +if (init) { + // @ts-ignore can be undefined + await init(); +} + +export function App(args: { children: string }): string { + // @ts-expect-error - glboal variable + globalThis.setLocale = setLocale; + + return ` + + +
+ +
+

Current locale: ${getLocale()}

+

Set locale:

+ ${locales.map((locale) => ``).join("")} +
+ +
+ +
+ ${args.children} +
+ `; +} diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/src/entry-client.ts b/inlang/packages/paraglide/paraglide-js/benchmark/src/entry-client.ts new file mode 100644 index 0000000000..ac49e8392b --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/src/entry-client.ts @@ -0,0 +1,41 @@ +import { App } from "./app.ts"; + +// render the initial page +render(window.location.pathname); + +// simulating client-side routing +// when a link is clicked, prevent the default behavior +// and re-render the app instead +document.addEventListener("click", (event) => { + const target = event.target as HTMLElement; + // intercept tag clicks + if (target.tagName === "A") { + event.preventDefault(); + // update the URL and re-render the app + const pathname = target.getAttribute("href")!; + history.pushState(null, "", pathname); + render(pathname); + } +}); + +async function render(pathname: string) { + let children: string; + + const path = pathname.replace(process.env.BASE!, "").replaceAll("//", "/"); + + // rootpath + if (path === "/") { + const { Page } = await import("./pages/index.js"); + children = Page(); + } else if (process.env.GENERATE_ABOUT_PAGE && path === "/about") { + // @ts-ignore - might not be generated + const { Page } = await import("./pages/about/index.js"); + children = Page(); + } else { + throw new Error("Unknown page"); + } + + const html = App({ children }); + + document.getElementById("root")!.innerHTML = html; +} diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/src/entry-server.ts b/inlang/packages/paraglide/paraglide-js/benchmark/src/entry-server.ts new file mode 100644 index 0000000000..b6225439c9 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/src/entry-server.ts @@ -0,0 +1,44 @@ +import { Page as Home } from "./pages/index.ts"; +import { Page as About } from "./pages/about/index.ts"; +import { App } from "./app.ts"; +import { middleware } from "./i18n/generated.ts"; +import fs from "node:fs/promises"; + +export async function handle(request: Request): Promise { + // @ts-ignore - middleware might not be defined + const _middleware: typeof mockMiddleware = middleware ?? mockMiddleware; + + return _middleware(request, async ({ request }) => { + let children: string; + + // assumes that a client side build has been done + const rootHtml = await fs.readFile( + new URL(`../dist/${process.env.BASE}/index.html`, import.meta.url) + .pathname, + "utf-8" + ); + + const path = new URL(request.url).pathname; + + if (path === "/") { + children = Home(); + } else if (path === "/about") { + children = About(); + } else { + throw new Error("Unknown page"); + } + + const html = App({ children }); + + return new Response(rootHtml.replace("", html), { + headers: { "Content-Type": "text/html" }, + }); + }); +} + +function mockMiddleware( + request: Request, + resolve: (args: { request: Request }) => Promise +): Promise { + return resolve({ request }); +} \ No newline at end of file diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/src/i18n/generated.d.ts b/inlang/packages/paraglide/paraglide-js/benchmark/src/i18n/generated.d.ts new file mode 100644 index 0000000000..ab4b0cc999 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/src/i18n/generated.d.ts @@ -0,0 +1,9 @@ +export declare const setLocale: (locale: string) => void; + +export declare const getLocale: () => string; + +export declare const locales: string[]; + +export declare const init: () => Promise | undefined; + +export declare const middleware: (...args: any[]) => any; diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/src/i18n/i18next.ts b/inlang/packages/paraglide/paraglide-js/benchmark/src/i18n/i18next.ts new file mode 100644 index 0000000000..44f07da4c3 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/src/i18n/i18next.ts @@ -0,0 +1,91 @@ +import * as i18next from "i18next"; +import FsBackend from "i18next-fs-backend"; +import LanguageDetector from "i18next-browser-languagedetector"; +import HttpApi from "i18next-http-backend"; + +/** + * Creates a reference to the message using i18next. + * + * @example + * refMessage("message", { id: "123" }) + * -> t("message", { id: "123" }) + */ +export const refMessage = (key: string, params: Record) => { + return `i18next.t("${key}", ${params ? JSON.stringify(params) : ""})`; +}; + +export const importExpression = () => `import i18next from "i18next";`; + +export const getLocale = () => { + return new URL(window.location.href).searchParams.get("locale") || "en"; +}; + +export const setLocale = (locale: string) => { + i18next.changeLanguage(locale); + const url = new URL(window.location.href); + url.searchParams.set("locale", locale); + window.location.href = url.toString(); +}; + +export const init = async () => { + if (process.env.IS_CLIENT) { + // don't try to load the messages during ssg + if (typeof window !== "undefined") { + // Check for specific library modes + if (process.env.LIBRARY_MODE === "http-backend") { + // HTTP Backend mode + await i18next + .use(LanguageDetector) + .use(HttpApi) + .init({ + backend: { + loadPath: `/${process.env.BUILD_NAME}/messages/{{lng}}.json`, + load: "languageOnly", + }, + lng: "en", + }); + } else if (process.env.LIBRARY_MODE === "default") { + // default to bundled mode as described in + // https://www.i18next.com/overview/getting-started#basic-sample + const jsonFiles: Record = import.meta.glob( + "../../messages/*.json", + { + eager: true, + query: "?raw", + } + ); + + const locales = Object.keys(jsonFiles).map((key) => + key.replace("../../messages/", "").replace(".json", "") + ); + + const resources = Object.fromEntries( + locales.map((locale) => [ + locale, + { + translation: JSON.parse( + jsonFiles[`../../messages/${locale}.json`].default + ), + }, + ]) + ); + // @ts-ignore - i18next type errors + await i18next.init({ + lng: "en", + resources, + }); + } else { + throw new Error(`Unknown library mode: ${process.env.LIBRARY_MODE}`); + } + } + } else { + await i18next.use(FsBackend).init({ + lng: "en", + backend: { + loadPath: "../../messages/{{lng}}.json", + }, + }); + } +}; + +export const middleware = undefined; \ No newline at end of file diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/src/i18n/paraglide.ts b/inlang/packages/paraglide/paraglide-js/benchmark/src/i18n/paraglide.ts new file mode 100644 index 0000000000..336075deff --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/src/i18n/paraglide.ts @@ -0,0 +1,24 @@ +import * as runtime from "../paraglide/runtime.js"; +import * as server from "../paraglide/server.js"; + +/** + * Creates a reference to the message. + * + * @example + * createReference("message", { id: "123" }) + * -> m.message({ id: "123" }) + */ +export const refMessage = (key: string, params?: Record) => { + return `m.${key}(${params ? JSON.stringify(params) : ""})`; +}; + +export const importExpression = () => + `import { m } from "/paraglide/messages.js";`; + +export const setLocale = runtime.setLocale; + +export const getLocale = runtime.getLocale; + +export const init = undefined; + +export const middleware = server.paraglideMiddleware; \ No newline at end of file diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/src/vite-env.d.ts b/inlang/packages/paraglide/paraglide-js/benchmark/src/vite-env.d.ts new file mode 100644 index 0000000000..11f02fe2a0 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/inlang/packages/paraglide/paraglide-js/benchmark/tsconfig.json b/inlang/packages/paraglide/paraglide-js/benchmark/tsconfig.json new file mode 100644 index 0000000000..59dc7965d5 --- /dev/null +++ b/inlang/packages/paraglide/paraglide-js/benchmark/tsconfig.json @@ -0,0 +1,22 @@ +{ + "include": ["./src", "./build.ts", "server.ts"], + "exclude": ["./src/pages/**/*"], + "compilerOptions": { + "resolveJsonModule": true, + "target": "ESNext", + "useDefineForClassFields": true, + "module": "preserve", + "allowImportingTsExtensions": true, + "lib": ["ESNext", "DOM"], + "strict": true, + "isolatedModules": true, + "esModuleInterop": true, + "noEmit": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "skipLibCheck": true, + "allowJs": true, + "checkJs": true + } +} diff --git a/inlang/packages/paraglide/paraglide-js/benchmarks/README.md b/inlang/packages/paraglide/paraglide-js/benchmarks/README.md deleted file mode 100644 index 7fcc3fb91e..0000000000 --- a/inlang/packages/paraglide/paraglide-js/benchmarks/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Paraglide Benchmarks - -This directory contains benchmark results comparing the bundle size of Paraglide to other i18n libraries. - -## Methodology -The benchmarks are using the messages from [obsidian-translations](https://github.com/obsidianmd/obsidian-translations) to attempt to be as realistic as possible. - -Each datapoint is produced by setting up a vite-project with the respective library and adding N messages in M languages to the project. The project contains i18n library + messages, as best as possible. The project is then built, minified and gzipped. We measure the size of JS Bundle only. - -## Terminology - -- **Messages**: A message is a string that is translated into multiple languages. For example, the message `Hello World` is translated into `Hallo Welt` in German. -- **Message Variants**: Is _one_ translation of a message. For example, the message `Hello World` is translated into `Hallo Welt` in German, and `Bonjour le monde` in French. Each of these translations is a message variant. In general, the number of message variants is equal to the number of languages times the number of messages. diff --git a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/growth-when-adding-languages.png b/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/growth-when-adding-languages.png deleted file mode 100644 index f17a15d843..0000000000 Binary files a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/growth-when-adding-languages.png and /dev/null differ diff --git a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/i18next-bytes-per-variant-logarithmic.png b/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/i18next-bytes-per-variant-logarithmic.png deleted file mode 100644 index 21dfc3d82c..0000000000 Binary files a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/i18next-bytes-per-variant-logarithmic.png and /dev/null differ diff --git a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/i18next-bytes-per-variant.png b/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/i18next-bytes-per-variant.png deleted file mode 100644 index 71d2d5db44..0000000000 Binary files a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/i18next-bytes-per-variant.png and /dev/null differ diff --git a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/i18next-cost-per-message-logarithmic.png b/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/i18next-cost-per-message-logarithmic.png deleted file mode 100644 index c6951db571..0000000000 Binary files a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/i18next-cost-per-message-logarithmic.png and /dev/null differ diff --git a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/paraglide-bytes-per-variant.png b/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/paraglide-bytes-per-variant.png deleted file mode 100644 index 2c20423104..0000000000 Binary files a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/paraglide-bytes-per-variant.png and /dev/null differ diff --git a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/paraglide-cost-per-message.png b/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/paraglide-cost-per-message.png deleted file mode 100644 index c7f1ced213..0000000000 Binary files a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/paraglide-cost-per-message.png and /dev/null differ diff --git a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/vs-i18next-multiple-languages.png b/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/vs-i18next-multiple-languages.png deleted file mode 100644 index d31e0edd18..0000000000 Binary files a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/vs-i18next-multiple-languages.png and /dev/null differ diff --git a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/vs-i18next-one-language.png b/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/vs-i18next-one-language.png deleted file mode 100644 index b615dfb67f..0000000000 Binary files a/inlang/packages/paraglide/paraglide-js/benchmarks/graphs/vs-i18next-one-language.png and /dev/null differ diff --git a/inlang/packages/paraglide/paraglide-js/benchmarks/raw-data/i18next.csv b/inlang/packages/paraglide/paraglide-js/benchmarks/raw-data/i18next.csv deleted file mode 100644 index a054e61256..0000000000 --- a/inlang/packages/paraglide/paraglide-js/benchmarks/raw-data/i18next.csv +++ /dev/null @@ -1,109 +0,0 @@ -languageSize,messageSize,uncompressed,gzipped -1,1,47986,14162 -1,5,48555,14403 -1,10,49026,14571 -1,20,50037,14913 -1,50,53259,15719 -1,100,60021,17886 -1,300,83532,23229 -1,500,119652,31756 -1,800,140903,38503 -1,1000,166968,43221 -1,1400,212029,51871 -1,1852,265021,57063 -2,1,48138,14229 -2,5,48583,14310 -2,10,49634,14563 -2,20,51275,14932 -2,50,58745,17473 -2,100,67568,19622 -2,300,103175,23073 -2,500,139471,35964 -2,800,209670,54750 -2,1000,237227,62071 -2,1400,315948,79791 -2,1852,428996,93220 -5,1,48338,14334 -5,5,49880,14952 -5,10,52090,15428 -5,20,55436,15123 -5,50,67676,18925 -5,100,90062,24253 -5,300,173245,36612 -5,500,273456,75257 -5,800,361447,95607 -5,1000,442776,117703 -5,1400,601588,157268 -5,1852,874787,196480 -10,1,48747,14416 -10,5,52939,15348 -10,10,57239,15946 -10,20,63820,17799 -10,50,94780,23396 -10,100,124555,28066 -10,300,296764,68715 -10,500,456408,120163 -10,800,652501,173694 -10,1000,881388,225088 -10,1400,1142658,296710 -10,1852,1462681,352558 -20,1,50324,15054 -20,5,55629,15458 -20,10,61828,17718 -20,20,83706,19625 -20,50,126629,28228 -20,100,198378,41272 -20,300,533874,99341 -20,500,796801,215034 -20,800,1243163,336704 -20,1000,1639967,432336 -20,1400,2262169,584463 -20,1852,2933787,684467 -30,1,50822,15215 -30,5,62923,17554 -30,10,67323,18467 -30,20,87005,19689 -30,50,161025,32933 -30,100,284538,55488 -30,300,724391,116619 -30,500,1178317,311609 -30,800,1885380,500756 -30,1000,2258532,608885 -30,1400,3155404,844869 -30,1852,4126722,971989 -40,1,50996,14806 -40,5,62419,17617 -40,10,73246,18390 -40,20,97986,23808 -40,50,180183,34507 -40,100,337338,52220 -40,300,934233,173018 -40,500,1534053,420732 -40,800,2435255,658103 -40,1000,2991230,796434 -40,1400,4248660,1125991 -40,1852,5613198,1314785 -50,1,52151,15279 -50,5,64443,17410 -50,10,81499,19377 -50,20,112591,23758 -50,50,213142,39870 -50,100,408007,65476 -50,300,1156269,187849 -50,500,1910982,523630 -50,800,3101356,827814 -50,1000,3780405,1013977 -50,1400,5314673,1407392 -50,1852,6939627,1633925 -61,1,50473,14684 -61,5,71111,17983 -61,10,98207,22035 -61,20,135964,28360 -61,50,278813,47733 -61,100,505903,82988 -61,300,1397225,240962 -61,500,2371463,656953 -61,800,3656834,989894 -61,1000,4636516,1238824 -61,1400,6383482,1692994 -61,1852,8466953,1979278 \ No newline at end of file diff --git a/inlang/packages/paraglide/paraglide-js/benchmarks/raw-data/paraglide.csv b/inlang/packages/paraglide/paraglide-js/benchmarks/raw-data/paraglide.csv deleted file mode 100644 index 1e4a22e82d..0000000000 --- a/inlang/packages/paraglide/paraglide-js/benchmarks/raw-data/paraglide.csv +++ /dev/null @@ -1,109 +0,0 @@ -languageSize,messageSize,uncompressed,gzipped -1,1,154,165 -1,5,606,277 -1,10,1158,409 -1,20,2552,856 -1,50,6327,1623 -1,100,11951,2678 -1,300,35973,7537 -1,500,65664,13439 -1,800,96189,17997 -1,1000,151361,26897 -1,1400,168416,29157 -1,1852,209118,34100 -2,1,186,172 -2,5,1160,509 -2,10,2013,915 -2,20,3144,833 -2,50,8069,2114 -2,100,16347,3550 -2,300,53837,13893 -2,500,95983,22684 -2,800,151517,34421 -2,1000,168314,37810 -2,1400,254064,54527 -2,1852,397948,74230 -5,1,502,374 -5,5,1349,509 -5,10,2732,1129 -5,20,7238,2891 -5,50,15029,4242 -5,100,30593,8178 -5,300,89154,22133 -5,500,158709,43934 -5,800,245720,64914 -5,1000,300151,82438 -5,1400,473856,125695 -5,1852,731598,161295 -10,1,371,296 -10,5,2042,837 -10,10,4759,2001 -10,20,9689,3387 -10,50,27921,10033 -10,100,60545,19723 -10,300,171185,53989 -10,500,271071,77620 -10,800,432910,115360 -10,1000,548478,167435 -10,1400,793214,234107 -10,1852,1038105,289182 -20,1,1968,839 -20,5,4229,1658 -20,10,12843,4601 -20,20,18295,5954 -20,50,47105,15187 -20,100,102767,30362 -20,300,328592,98128 -20,500,518595,140418 -20,800,809305,235407 -20,1000,1109916,348175 -20,1400,1559651,474657 -20,1852,1969218,564865 -30,1,829,526 -30,5,6020,2664 -30,10,17482,6689 -30,20,29183,9446 -30,50,77206,23283 -30,100,146085,42666 -30,300,495754,159916 -30,500,813990,246539 -30,800,1279067,402492 -30,1000,1616821,512280 -30,1400,2184641,689592 -30,1852,2861156,853862 -40,1,1453,748 -40,5,8652,3743 -40,10,19880,7280 -40,20,35450,12840 -40,50,107612,36812 -40,100,179110,57657 -40,300,635955,183162 -40,500,1034288,302089 -40,800,1715746,510556 -40,1000,2115945,674569 -40,1400,2956598,931154 -40,1852,3923232,1147078 -50,1,2771,1155 -50,5,17155,6209 -50,10,25888,10058 -50,20,62435,20055 -50,50,117401,38274 -50,100,281765,83821 -50,300,756234,224656 -50,500,1277416,382234 -50,800,2103067,618842 -50,1000,2704804,867284 -50,1400,3663754,1155619 -50,1852,4762951,1412487 -61,1,2005,835 -61,5,18573,6455 -61,10,32351,10662 -61,20,66253,22090 -61,50,134028,44092 -61,100,315273,93525 -61,300,1011506,290419 -61,500,1569821,455633 -61,800,2509117,728243 -61,1000,3158751,1006218 -61,1400,4437586,1403614 -61,1852,5875735,1733239 \ No newline at end of file diff --git a/inlang/packages/paraglide/paraglide-js/marketplace-manifest.json b/inlang/packages/paraglide/paraglide-js/marketplace-manifest.json index e5a12a121e..db837d6f2a 100644 --- a/inlang/packages/paraglide/paraglide-js/marketplace-manifest.json +++ b/inlang/packages/paraglide/paraglide-js/marketplace-manifest.json @@ -19,7 +19,7 @@ "": { "/": "./docs/introduction.md", "/limitations": "./docs/limitations.md", - "/scaling": "./docs/scaling.md", + "/benchmark": "./benchmark/README.md", "/migrating-to-v2": "./docs/migrating-to-v2.md", "/changelog": "./CHANGELOG.md" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 79fde55156..90ccea111e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 2.27.10 '@vitest/coverage-v8': specifier: ^2.0.5 - version: 2.1.3(vitest@2.1.8(@types/node@22.10.6)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.3))(terser@5.36.0)) + version: 2.1.3(vitest@2.1.8(@types/node@22.10.6)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.8.2))(terser@5.36.0)) nx: specifier: 18.3.3 version: 18.3.3 @@ -22,7 +22,7 @@ importers: version: 16.5.2 vitest: specifier: ^2.1.8 - version: 2.1.8(@types/node@22.10.6)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.3))(terser@5.36.0) + version: 2.1.8(@types/node@22.10.6)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.8.2))(terser@5.36.0) inlang/packages/cli: dependencies: @@ -210,7 +210,7 @@ importers: version: 5.7.2 vitest: specifier: 0.34.3 - version: 0.34.3(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(playwright@1.48.1)(safaridriver@0.1.2)(terser@5.36.0)(webdriverio@9.2.1) + version: 0.34.3(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(playwright@1.50.1)(safaridriver@0.1.2)(terser@5.36.0)(webdriverio@9.2.1) inlang/packages/marketplace-registry: dependencies: @@ -316,6 +316,43 @@ importers: specifier: 2.1.8 version: 2.1.8(@types/node@22.10.6)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.3))(terser@5.36.0) + inlang/packages/paraglide/paraglide-js/benchmark: + dependencies: + i18next: + specifier: ^24.2.2 + version: 24.2.2(typescript@5.8.2) + i18next-browser-languagedetector: + specifier: ^8.0.4 + version: 8.0.4 + i18next-fs-backend: + specifier: ^2.6.0 + version: 2.6.0 + i18next-http-backend: + specifier: ^3.0.2 + version: 3.0.2 + devDependencies: + '@hono/node-server': + specifier: ^1.13.7 + version: 1.13.7(hono@4.6.11) + '@inlang/paraglide-js': + specifier: workspace:* + version: link:.. + csv-to-markdown-table: + specifier: ^1.4.1 + version: 1.4.1 + hono: + specifier: ^4.6.11 + version: 4.6.11 + playwright: + specifier: ^1.50.1 + version: 1.50.1 + typescript: + specifier: ^5.8.0 + version: 5.8.2 + vite: + specifier: ^6.0.7 + version: 6.0.11(@types/node@22.10.6)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) + inlang/packages/paraglide/paraglide-js/examples/astro: dependencies: '@astrojs/check': @@ -1851,7 +1888,7 @@ importers: version: 5.2.2 vitest: specifier: 0.34.3 - version: 0.34.3(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(playwright@1.48.1)(safaridriver@0.1.2)(terser@5.36.0)(webdriverio@9.2.1) + version: 0.34.3(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(playwright@1.50.1)(safaridriver@0.1.2)(terser@5.36.0)(webdriverio@9.2.1) packages/csv-app: dependencies: @@ -5001,6 +5038,9 @@ packages: '@lit-labs/ssr-dom-shim@1.2.1': resolution: {integrity: sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ==} + '@lit-labs/ssr-dom-shim@1.3.0': + resolution: {integrity: sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==} + '@lit/react@1.0.6': resolution: {integrity: sha512-QIss8MPh6qUoFJmuaF4dSHts3qCsA36S3HcOLiNPShxhgYPr4XJRnCBKPipk85sR9xr6TQrOcDMfexwbNdJHYA==} peerDependencies: @@ -9876,6 +9916,10 @@ packages: resolution: {integrity: sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==} engines: {node: '>= 0.8.0'} + compression@1.8.0: + resolution: {integrity: sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==} + engines: {node: '>= 0.8.0'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -10098,6 +10142,10 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csv-to-markdown-table@1.4.1: + resolution: {integrity: sha512-jhLkfM7LXGQCuhxCwIw0QmpHCbMXy8ouC+T8KKoKaZ43DQAezpHCxNl74j2S9Sb4SEnVgMK8/RqJfNUk6xMHRQ==} + hasBin: true + cytoscape-cose-bilkent@4.1.0: resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} peerDependencies: @@ -12157,6 +12205,9 @@ packages: resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} engines: {node: '>=10.18'} + i18next-browser-languagedetector@8.0.4: + resolution: {integrity: sha512-f3frU3pIxD50/Tz20zx9TD9HobKYg47fmAETb117GKGPrhwcSSPJDoCposXlVycVebQ9GQohC3Efbpq7/nnJ5w==} + i18next-fs-backend@2.6.0: resolution: {integrity: sha512-3ZlhNoF9yxnM8pa8bWp5120/Ob6t4lVl1l/tbLmkml/ei3ud8IWySCHt2lrY5xWRlSU5D9IV2sm5bEbGuTqwTw==} @@ -14668,13 +14719,13 @@ packages: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} - playwright-core@1.48.1: - resolution: {integrity: sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==} + playwright-core@1.50.1: + resolution: {integrity: sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==} engines: {node: '>=18'} hasBin: true - playwright@1.48.1: - resolution: {integrity: sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==} + playwright@1.50.1: + resolution: {integrity: sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==} engines: {node: '>=18'} hasBin: true @@ -15914,8 +15965,8 @@ packages: resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} engines: {node: '>= 10'} - sirv@3.0.0: - resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} + sirv@3.0.1: + resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} engines: {node: '>=18'} sisteransi@1.0.5: @@ -16835,6 +16886,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} + engines: {node: '>=14.17'} + hasBin: true + uc.micro@1.0.6: resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} @@ -21118,6 +21174,8 @@ snapshots: '@lit-labs/ssr-dom-shim@1.2.1': {} + '@lit-labs/ssr-dom-shim@1.3.0': {} + '@lit/react@1.0.6(@types/react@18.3.11)': dependencies: '@types/react': 18.3.11 @@ -21132,7 +21190,7 @@ snapshots: '@lit/reactive-element@2.0.4': dependencies: - '@lit-labs/ssr-dom-shim': 1.2.1 + '@lit-labs/ssr-dom-shim': 1.3.0 '@lit/task@1.0.1': dependencies: @@ -23073,7 +23131,7 @@ snapshots: dependencies: '@react-router/express': 7.2.0(express@4.21.1)(react-router@7.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(typescript@5.7.3) '@react-router/node': 7.2.0(react-router@7.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(typescript@5.7.3) - compression: 1.7.5 + compression: 1.8.0 express: 4.21.1 get-port: 5.1.1 morgan: 1.10.0 @@ -23258,7 +23316,7 @@ snapshots: '@remix-run/express': 2.12.1(express@4.21.1)(typescript@5.7.2) '@remix-run/node': 2.12.1(typescript@5.7.2) chokidar: 3.6.0 - compression: 1.7.5 + compression: 1.8.0 express: 4.21.1 get-port: 5.1.1 morgan: 1.10.0 @@ -24186,7 +24244,7 @@ snapshots: better-opn: 3.0.2 chalk: 4.1.2 cli-table3: 0.6.5 - compression: 1.7.5 + compression: 1.8.0 detect-port: 1.6.1 express: 4.21.1 fs-extra: 11.2.0 @@ -24424,7 +24482,7 @@ snapshots: mrmime: 2.0.0 sade: 1.8.1 set-cookie-parser: 2.7.1 - sirv: 3.0.0 + sirv: 3.0.1 svelte: 5.19.5 vite: 5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0) @@ -26021,7 +26079,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/browser@2.1.3(@types/node@22.10.6)(@vitest/spy@2.1.3)(playwright@1.48.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.3)(webdriverio@9.2.1)': + '@vitest/browser@2.1.3(@types/node@22.10.6)(@vitest/spy@2.1.3)(playwright@1.50.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.3)(webdriverio@9.2.1)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) @@ -26034,7 +26092,7 @@ snapshots: vitest: 2.1.3(@types/node@22.10.6)(@vitest/browser@2.1.3)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.2))(terser@5.36.0) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 webdriverio: 9.2.1 transitivePeerDependencies: - '@types/node' @@ -26045,7 +26103,7 @@ snapshots: - vite optional: true - '@vitest/browser@2.1.3(@types/node@22.10.6)(@vitest/spy@2.1.8)(playwright@1.48.1)(typescript@5.6.3)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.3)(webdriverio@9.2.1)': + '@vitest/browser@2.1.3(@types/node@22.10.6)(@vitest/spy@2.1.8)(playwright@1.50.1)(typescript@5.6.3)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.3)(webdriverio@9.2.1)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) @@ -26058,7 +26116,7 @@ snapshots: vitest: 2.1.3(@types/node@22.10.6)(@vitest/browser@2.1.3)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.6.3))(terser@5.36.0) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 webdriverio: 9.2.1 transitivePeerDependencies: - '@types/node' @@ -26069,7 +26127,7 @@ snapshots: - vite optional: true - '@vitest/browser@2.1.8(@types/node@20.16.13)(playwright@1.48.1)(typescript@5.3.2)(vite@5.4.11(@types/node@20.16.13)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@8.41.0)': + '@vitest/browser@2.1.8(@types/node@20.16.13)(playwright@1.50.1)(typescript@5.3.2)(vite@5.4.11(@types/node@20.16.13)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@8.41.0)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) @@ -26077,12 +26135,12 @@ snapshots: '@vitest/utils': 2.1.8 magic-string: 0.30.17 msw: 2.7.0(@types/node@20.16.13)(typescript@5.3.2) - sirv: 3.0.0 + sirv: 3.0.1 tinyrainbow: 1.2.0 vitest: 2.1.8(@types/node@20.16.13)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@20.16.13)(typescript@5.3.2))(terser@5.36.0) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 webdriverio: 8.41.0 transitivePeerDependencies: - '@types/node' @@ -26092,7 +26150,7 @@ snapshots: - vite optional: true - '@vitest/browser@2.1.8(@types/node@20.5.9)(playwright@1.48.1)(typescript@5.2.2)(vite@5.4.11(@types/node@20.5.9)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': + '@vitest/browser@2.1.8(@types/node@20.5.9)(playwright@1.50.1)(typescript@5.2.2)(vite@5.4.11(@types/node@20.5.9)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) @@ -26100,12 +26158,12 @@ snapshots: '@vitest/utils': 2.1.8 magic-string: 0.30.17 msw: 2.7.0(@types/node@20.5.9)(typescript@5.2.2) - sirv: 3.0.0 + sirv: 3.0.1 tinyrainbow: 1.2.0 vitest: 2.1.8(@types/node@20.5.9)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@20.5.9)(typescript@5.2.2))(terser@5.36.0) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 webdriverio: 9.2.1 transitivePeerDependencies: - '@types/node' @@ -26115,7 +26173,7 @@ snapshots: - vite optional: true - '@vitest/browser@2.1.8(@types/node@20.5.9)(playwright@1.48.1)(typescript@5.7.2)(vite@4.5.5(@types/node@20.5.9)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': + '@vitest/browser@2.1.8(@types/node@20.5.9)(playwright@1.50.1)(typescript@5.7.2)(vite@4.5.5(@types/node@20.5.9)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) @@ -26123,12 +26181,12 @@ snapshots: '@vitest/utils': 2.1.8 magic-string: 0.30.17 msw: 2.7.0(@types/node@20.5.9)(typescript@5.7.2) - sirv: 3.0.0 + sirv: 3.0.1 tinyrainbow: 1.2.0 vitest: 2.1.8(@types/node@20.5.9)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@20.5.9)(typescript@5.7.2))(terser@5.36.0) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 webdriverio: 9.2.1 transitivePeerDependencies: - '@types/node' @@ -26138,20 +26196,20 @@ snapshots: - vite optional: true - '@vitest/browser@2.1.8(@types/node@22.10.1)(playwright@1.48.1)(safaridriver@0.1.2)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0))(vitest@0.34.3)(webdriverio@9.2.1)': + '@vitest/browser@2.1.8(@types/node@22.10.1)(playwright@1.50.1)(safaridriver@0.1.2)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0))(vitest@0.34.3)(webdriverio@9.2.1)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) - '@vitest/mocker': 2.1.8(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.3))(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0)) + '@vitest/mocker': 2.1.8(msw@2.7.0(@types/node@22.10.6)(typescript@5.8.2))(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0)) '@vitest/utils': 2.1.8 magic-string: 0.30.17 msw: 2.7.0(@types/node@22.10.1)(typescript@5.7.2) - sirv: 3.0.0 + sirv: 3.0.1 tinyrainbow: 1.2.0 - vitest: 0.34.3(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(playwright@1.48.1)(safaridriver@0.1.2)(terser@5.36.0)(webdriverio@9.2.1) + vitest: 0.34.3(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(playwright@1.50.1)(safaridriver@0.1.2)(terser@5.36.0)(webdriverio@9.2.1) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 safaridriver: 0.1.2 webdriverio: 9.2.1 transitivePeerDependencies: @@ -26162,7 +26220,7 @@ snapshots: - vite optional: true - '@vitest/browser@2.1.8(@types/node@22.10.1)(playwright@1.48.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': + '@vitest/browser@2.1.8(@types/node@22.10.1)(playwright@1.50.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) @@ -26170,12 +26228,12 @@ snapshots: '@vitest/utils': 2.1.8 magic-string: 0.30.17 msw: 2.7.0(@types/node@22.10.1)(typescript@5.7.2) - sirv: 3.0.0 + sirv: 3.0.1 tinyrainbow: 1.2.0 vitest: 2.1.8(@types/node@22.10.1)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.1)(typescript@5.7.2))(terser@5.36.0) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 webdriverio: 9.2.1 transitivePeerDependencies: - '@types/node' @@ -26185,7 +26243,7 @@ snapshots: - vite optional: true - '@vitest/browser@2.1.8(@types/node@22.10.6)(playwright@1.48.1)(typescript@5.3.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': + '@vitest/browser@2.1.8(@types/node@22.10.6)(playwright@1.50.1)(typescript@5.3.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) @@ -26193,12 +26251,12 @@ snapshots: '@vitest/utils': 2.1.8 magic-string: 0.30.17 msw: 2.7.0(@types/node@22.10.6)(typescript@5.3.2) - sirv: 3.0.0 + sirv: 3.0.1 tinyrainbow: 1.2.0 vitest: 2.1.8(@types/node@22.10.6)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.3.2))(terser@5.36.0) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 webdriverio: 9.2.1 transitivePeerDependencies: - '@types/node' @@ -26208,7 +26266,7 @@ snapshots: - vite optional: true - '@vitest/browser@2.1.8(@types/node@22.10.6)(playwright@1.48.1)(typescript@5.4.5)(vite@6.0.11(@types/node@22.10.6)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0))(vitest@2.1.8)(webdriverio@9.2.1)': + '@vitest/browser@2.1.8(@types/node@22.10.6)(playwright@1.50.1)(typescript@5.4.5)(vite@6.0.11(@types/node@22.10.6)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0))(vitest@2.1.8)(webdriverio@9.2.1)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) @@ -26216,12 +26274,12 @@ snapshots: '@vitest/utils': 2.1.8 magic-string: 0.30.17 msw: 2.7.0(@types/node@22.10.6)(typescript@5.4.5) - sirv: 3.0.0 + sirv: 3.0.1 tinyrainbow: 1.2.0 vitest: 2.1.8(@types/node@22.10.6)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.4.5))(terser@5.36.0) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 webdriverio: 9.2.1 transitivePeerDependencies: - '@types/node' @@ -26231,7 +26289,7 @@ snapshots: - vite optional: true - '@vitest/browser@2.1.8(@types/node@22.10.6)(playwright@1.48.1)(typescript@5.6.3)(vite@5.4.9(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': + '@vitest/browser@2.1.8(@types/node@22.10.6)(playwright@1.50.1)(typescript@5.6.3)(vite@5.4.9(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) @@ -26239,12 +26297,12 @@ snapshots: '@vitest/utils': 2.1.8 magic-string: 0.30.17 msw: 2.7.0(@types/node@22.10.6)(typescript@5.6.3) - sirv: 3.0.0 + sirv: 3.0.1 tinyrainbow: 1.2.0 vitest: 2.1.8(@types/node@22.10.6)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.6.3))(terser@5.36.0) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 webdriverio: 9.2.1 transitivePeerDependencies: - '@types/node' @@ -26254,7 +26312,7 @@ snapshots: - vite optional: true - '@vitest/browser@2.1.8(@types/node@22.10.6)(playwright@1.48.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': + '@vitest/browser@2.1.8(@types/node@22.10.6)(playwright@1.50.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) @@ -26262,12 +26320,12 @@ snapshots: '@vitest/utils': 2.1.8 magic-string: 0.30.17 msw: 2.7.0(@types/node@22.10.6)(typescript@5.7.2) - sirv: 3.0.0 + sirv: 3.0.1 tinyrainbow: 1.2.0 vitest: 2.1.8(@types/node@22.10.6)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.2))(terser@5.36.0) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 webdriverio: 9.2.1 transitivePeerDependencies: - '@types/node' @@ -26277,7 +26335,7 @@ snapshots: - vite optional: true - '@vitest/browser@2.1.8(@types/node@22.10.6)(playwright@1.48.1)(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': + '@vitest/browser@2.1.8(@types/node@22.10.6)(playwright@1.50.1)(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1)': dependencies: '@testing-library/dom': 10.4.0 '@testing-library/user-event': 14.5.2(@testing-library/dom@10.4.0) @@ -26285,12 +26343,12 @@ snapshots: '@vitest/utils': 2.1.8 magic-string: 0.30.17 msw: 2.7.0(@types/node@22.10.6)(typescript@5.7.3) - sirv: 3.0.0 + sirv: 3.0.1 tinyrainbow: 1.2.0 vitest: 2.1.8(@types/node@22.10.6)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.3))(terser@5.36.0) ws: 8.18.0 optionalDependencies: - playwright: 1.48.1 + playwright: 1.50.1 webdriverio: 9.2.1 transitivePeerDependencies: - '@types/node' @@ -26313,7 +26371,7 @@ snapshots: std-env: 3.8.0 test-exclude: 6.0.0 v8-to-istanbul: 9.3.0 - vitest: 0.34.3(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(playwright@1.48.1)(safaridriver@0.1.2)(terser@5.36.0)(webdriverio@9.2.1) + vitest: 0.34.3(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(playwright@1.50.1)(safaridriver@0.1.2)(terser@5.36.0)(webdriverio@9.2.1) transitivePeerDependencies: - supports-color @@ -26353,7 +26411,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@2.1.3(vitest@2.1.8(@types/node@22.10.6)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.3))(terser@5.36.0))': + '@vitest/coverage-v8@2.1.3(vitest@2.1.8(@types/node@22.10.6)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.8.2))(terser@5.36.0))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -26367,7 +26425,7 @@ snapshots: std-env: 3.8.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: 2.1.8(@types/node@22.10.6)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.3))(terser@5.36.0) + vitest: 2.1.8(@types/node@22.10.6)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.8.2))(terser@5.36.0) transitivePeerDependencies: - supports-color @@ -26387,7 +26445,7 @@ snapshots: tinyrainbow: 1.2.0 vitest: 2.1.8(@types/node@22.10.6)(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.2))(terser@5.36.0) optionalDependencies: - '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.48.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) + '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.50.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) transitivePeerDependencies: - supports-color @@ -26548,23 +26606,32 @@ snapshots: msw: 2.7.0(@types/node@22.10.6)(typescript@5.7.2) vite: 5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0) - '@vitest/mocker@2.1.8(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.3))(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0))': + '@vitest/mocker@2.1.8(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.3))(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))': dependencies: '@vitest/spy': 2.1.8 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: msw: 2.7.0(@types/node@22.10.6)(typescript@5.7.3) + vite: 5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0) + + '@vitest/mocker@2.1.8(msw@2.7.0(@types/node@22.10.6)(typescript@5.8.2))(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0))': + dependencies: + '@vitest/spy': 2.1.8 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + msw: 2.7.0(@types/node@22.10.6)(typescript@5.8.2) vite: 5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0) optional: true - '@vitest/mocker@2.1.8(msw@2.7.0(@types/node@22.10.6)(typescript@5.7.3))(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))': + '@vitest/mocker@2.1.8(msw@2.7.0(@types/node@22.10.6)(typescript@5.8.2))(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))': dependencies: '@vitest/spy': 2.1.8 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - msw: 2.7.0(@types/node@22.10.6)(typescript@5.7.3) + msw: 2.7.0(@types/node@22.10.6)(typescript@5.8.2) vite: 5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0) '@vitest/pretty-format@2.0.5': @@ -28544,6 +28611,18 @@ snapshots: transitivePeerDependencies: - supports-color + compression@1.8.0: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9 + negotiator: 0.6.4 + on-headers: 1.0.2 + safe-buffer: 5.2.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + concat-map@0.0.1: {} concat-stream@1.6.2: @@ -28795,6 +28874,8 @@ snapshots: csstype@3.1.3: {} + csv-to-markdown-table@1.4.1: {} + cytoscape-cose-bilkent@4.1.0(cytoscape@3.30.4): dependencies: cose-base: 1.0.3 @@ -31648,6 +31729,10 @@ snapshots: hyperdyperid@1.2.0: {} + i18next-browser-languagedetector@8.0.4: + dependencies: + '@babel/runtime': 7.25.7 + i18next-fs-backend@2.6.0: {} i18next-http-backend@3.0.2: @@ -31666,6 +31751,12 @@ snapshots: optionalDependencies: typescript: 5.7.3 + i18next@24.2.2(typescript@5.8.2): + dependencies: + '@babel/runtime': 7.25.7 + optionalDependencies: + typescript: 5.8.2 + iconify-icon@1.0.8: dependencies: '@iconify/types': 2.0.0 @@ -32568,7 +32659,7 @@ snapshots: lit-element@4.1.1: dependencies: - '@lit-labs/ssr-dom-shim': 1.2.1 + '@lit-labs/ssr-dom-shim': 1.3.0 '@lit/reactive-element': 2.0.4 lit-html: 3.2.1 @@ -34105,6 +34196,32 @@ snapshots: - '@types/node' optional: true + msw@2.7.0(@types/node@22.10.6)(typescript@5.8.2): + dependencies: + '@bundled-es-modules/cookie': 2.0.1 + '@bundled-es-modules/statuses': 1.0.1 + '@bundled-es-modules/tough-cookie': 0.1.6 + '@inquirer/confirm': 5.1.1(@types/node@22.10.6) + '@mswjs/interceptors': 0.37.4 + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.5 + graphql: 16.9.0 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + strict-event-emitter: 0.5.1 + type-fest: 4.26.1 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/node' + optional: true + muggle-string@0.4.1: {} murmurhash3js@3.0.1: {} @@ -34926,15 +35043,13 @@ snapshots: dependencies: find-up: 3.0.0 - playwright-core@1.48.1: - optional: true + playwright-core@1.50.1: {} - playwright@1.48.1: + playwright@1.50.1: dependencies: - playwright-core: 1.48.1 + playwright-core: 1.50.1 optionalDependencies: fsevents: 2.3.2 - optional: true pluralize@8.0.0: {} @@ -36630,7 +36745,7 @@ snapshots: mrmime: 2.0.0 totalist: 3.0.1 - sirv@3.0.0: + sirv@3.0.1: dependencies: '@polka/url': 1.0.0-next.28 mrmime: 2.0.0 @@ -37814,6 +37929,8 @@ snapshots: typescript@5.7.3: {} + typescript@5.8.2: {} + uc.micro@1.0.6: {} uc.micro@2.1.0: {} @@ -38683,7 +38800,7 @@ snapshots: optionalDependencies: vite: 6.0.11(@types/node@22.10.6)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0) - vitest@0.34.3(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(playwright@1.48.1)(safaridriver@0.1.2)(terser@5.36.0)(webdriverio@9.2.1): + vitest@0.34.3(@vitest/browser@2.1.8)(jsdom@25.0.1)(lightningcss@1.29.1)(playwright@1.50.1)(safaridriver@0.1.2)(terser@5.36.0)(webdriverio@9.2.1): dependencies: '@types/chai': 4.3.20 '@types/chai-subset': 1.3.5 @@ -38710,9 +38827,9 @@ snapshots: vite-node: 0.34.3(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0) why-is-node-running: 2.3.0 optionalDependencies: - '@vitest/browser': 2.1.8(@types/node@22.10.1)(playwright@1.48.1)(safaridriver@0.1.2)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0))(vitest@0.34.3)(webdriverio@9.2.1) + '@vitest/browser': 2.1.8(@types/node@22.10.1)(playwright@1.50.1)(safaridriver@0.1.2)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0))(vitest@0.34.3)(webdriverio@9.2.1) jsdom: 25.0.1 - playwright: 1.48.1 + playwright: 1.50.1 safaridriver: 0.1.2 webdriverio: 9.2.1 transitivePeerDependencies: @@ -38781,7 +38898,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.6 - '@vitest/browser': 2.1.3(@types/node@22.10.6)(@vitest/spy@2.1.8)(playwright@1.48.1)(typescript@5.6.3)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.3)(webdriverio@9.2.1) + '@vitest/browser': 2.1.3(@types/node@22.10.6)(@vitest/spy@2.1.8)(playwright@1.50.1)(typescript@5.6.3)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.3)(webdriverio@9.2.1) jsdom: 25.0.1 transitivePeerDependencies: - less @@ -38817,7 +38934,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.6 - '@vitest/browser': 2.1.3(@types/node@22.10.6)(@vitest/spy@2.1.3)(playwright@1.48.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.3)(webdriverio@9.2.1) + '@vitest/browser': 2.1.3(@types/node@22.10.6)(@vitest/spy@2.1.3)(playwright@1.50.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.3)(webdriverio@9.2.1) jsdom: 25.0.1 transitivePeerDependencies: - less @@ -38854,7 +38971,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.16.13 - '@vitest/browser': 2.1.8(@types/node@20.16.13)(playwright@1.48.1)(typescript@5.3.2)(vite@5.4.11(@types/node@20.16.13)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@8.41.0) + '@vitest/browser': 2.1.8(@types/node@20.16.13)(playwright@1.50.1)(typescript@5.3.2)(vite@5.4.11(@types/node@20.16.13)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@8.41.0) jsdom: 25.0.1 transitivePeerDependencies: - less @@ -38891,7 +39008,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.5.9 - '@vitest/browser': 2.1.8(@types/node@20.5.9)(playwright@1.48.1)(typescript@5.2.2)(vite@5.4.11(@types/node@20.5.9)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) + '@vitest/browser': 2.1.8(@types/node@20.5.9)(playwright@1.50.1)(typescript@5.2.2)(vite@5.4.11(@types/node@20.5.9)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) jsdom: 25.0.1 transitivePeerDependencies: - less @@ -38928,7 +39045,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.5.9 - '@vitest/browser': 2.1.8(@types/node@20.5.9)(playwright@1.48.1)(typescript@5.7.2)(vite@4.5.5(@types/node@20.5.9)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) + '@vitest/browser': 2.1.8(@types/node@20.5.9)(playwright@1.50.1)(typescript@5.7.2)(vite@4.5.5(@types/node@20.5.9)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) jsdom: 25.0.1 transitivePeerDependencies: - less @@ -38965,7 +39082,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.1 - '@vitest/browser': 2.1.8(@types/node@22.10.1)(playwright@1.48.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) + '@vitest/browser': 2.1.8(@types/node@22.10.1)(playwright@1.50.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) jsdom: 25.0.1 transitivePeerDependencies: - less @@ -39002,7 +39119,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.6 - '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.48.1)(typescript@5.3.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) + '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.50.1)(typescript@5.3.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) jsdom: 25.0.1 transitivePeerDependencies: - less @@ -39039,7 +39156,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.6 - '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.48.1)(typescript@5.4.5)(vite@6.0.11(@types/node@22.10.6)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0))(vitest@2.1.8)(webdriverio@9.2.1) + '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.50.1)(typescript@5.4.5)(vite@6.0.11(@types/node@22.10.6)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.36.0)(tsx@4.19.2)(yaml@2.7.0))(vitest@2.1.8)(webdriverio@9.2.1) jsdom: 25.0.1 transitivePeerDependencies: - less @@ -39076,7 +39193,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.6 - '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.48.1)(typescript@5.6.3)(vite@5.4.9(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) + '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.50.1)(typescript@5.6.3)(vite@5.4.9(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) jsdom: 25.0.1 transitivePeerDependencies: - less @@ -39113,7 +39230,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.6 - '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.48.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) + '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.50.1)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) jsdom: 25.0.1 transitivePeerDependencies: - less @@ -39150,7 +39267,43 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.6 - '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.48.1)(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) + '@vitest/browser': 2.1.8(@types/node@22.10.6)(playwright@1.50.1)(typescript@5.7.3)(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0))(vitest@2.1.8)(webdriverio@9.2.1) + jsdom: 25.0.1 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vitest@2.1.8(@types/node@22.10.6)(jsdom@25.0.1)(lightningcss@1.29.1)(msw@2.7.0(@types/node@22.10.6)(typescript@5.8.2))(terser@5.36.0): + dependencies: + '@vitest/expect': 2.1.8 + '@vitest/mocker': 2.1.8(msw@2.7.0(@types/node@22.10.6)(typescript@5.8.2))(vite@5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0)) + '@vitest/pretty-format': 2.1.8 + '@vitest/runner': 2.1.8 + '@vitest/snapshot': 2.1.8 + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 + chai: 5.1.2 + debug: 4.4.0(supports-color@8.1.1) + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 1.1.2 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.1 + tinypool: 1.0.1 + tinyrainbow: 1.2.0 + vite: 5.4.11(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0) + vite-node: 2.1.8(@types/node@22.10.6)(lightningcss@1.29.1)(terser@5.36.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.10.6 jsdom: 25.0.1 transitivePeerDependencies: - less