From 6a246b903549fa1f0118e38b1a97407f285c5a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Fri, 13 Nov 2026 16:56:42 +0100 Subject: [PATCH 1/3] Add Finance-SvelteKit experimental workload An original SvelteKit (Svelte 5) finance portal, modelled on Firefox profiles of a popular finance site's home and screener pages: a market strip with canvas sparklines, an intraday area chart, a news stream that loads behind a skeleton, and a screener page with a quote-card strip, sector chips, a 25-row table with SVG sparklines, sorting, pagination, a treemap heatmap and related news. Quotes stream in over a fixed burst of messages and flash the cells they touch. The profiles also set its size and styling. Their largest restyles touched about 2,400 elements on the home page and 4,000 on the screener page, and their class names show Svelte component-scoped CSS next to tw-prefixed Tailwind utilities, so the workload uses the same mix. Charts render without draw-in animations and measure their canvas once, following resizes through a ResizeObserver. All data is generated deterministically at startup (seeded PRNG, fixed clock) and served as JSON strings over MessageChannel tasks, so loads pay for parsing without timers or network. The app uses the hash router and a single bundle so it runs from any subdirectory and never fetches code during a step. The adapter wrapper rewrites the SPA fallback's root-relative paths and emits resources.txt. --- .eslintignore | 1 + .prettierignore | 1 + suites-experimental/finance/.gitignore | 1 + suites-experimental/finance/README.md | 72 + suites-experimental/finance/package-lock.json | 2163 +++++++++++++++++ suites-experimental/finance/package.json | 23 + suites-experimental/finance/postcss.config.js | 5 + suites-experimental/finance/src/app.css | 120 + suites-experimental/finance/src/app.html | 12 + .../finance/src/lib/analytics.js | 77 + suites-experimental/finance/src/lib/charts.js | 219 ++ .../src/lib/components/CanvasSparkline.svelte | 24 + .../finance/src/lib/components/Footer.svelte | 32 + .../finance/src/lib/components/Header.svelte | 153 ++ .../finance/src/lib/components/HeatMap.svelte | 116 + .../finance/src/lib/components/Icon.svelte | 35 + .../src/lib/components/IconButton.svelte | 31 + .../src/lib/components/MarketSummary.svelte | 78 + .../src/lib/components/MarketsTable.svelte | 130 + .../src/lib/components/MoversCard.svelte | 22 + .../src/lib/components/Pagination.svelte | 53 + .../src/lib/components/PriceChange.svelte | 10 + .../src/lib/components/QuoteList.svelte | 59 + .../src/lib/components/RangeCell.svelte | 48 + .../src/lib/components/RatingCell.svelte | 38 + .../src/lib/components/ScreenerTabs.svelte | 42 + .../src/lib/components/SectorChips.svelte | 60 + .../src/lib/components/StoryItem.svelte | 150 ++ .../src/lib/components/StoryStream.svelte | 23 + .../lib/components/StoryStreamSkeleton.svelte | 35 + .../src/lib/components/SvgSparkline.svelte | 47 + .../src/lib/components/TableRow.svelte | 98 + .../src/lib/components/TickerCard.svelte | 55 + .../src/lib/components/TickerCarousel.svelte | 92 + .../src/lib/components/TickerChip.svelte | 33 + .../src/lib/components/TrendingCard.svelte | 10 + .../finance/src/lib/data/api.js | 70 + .../finance/src/lib/data/market.js | 223 ++ .../finance/src/lib/data/random.js | 15 + suites-experimental/finance/src/lib/format.js | 53 + .../finance/src/lib/quotes.svelte.js | 97 + .../finance/src/routes/+layout.js | 7 + .../finance/src/routes/+layout.svelte | 36 + .../finance/src/routes/+page.js | 20 + .../finance/src/routes/+page.svelte | 72 + .../routes/markets/stocks/[screener]/+page.js | 40 + .../markets/stocks/[screener]/+page.svelte | 50 + .../finance/tailwind.config.js | 18 + suites-experimental/finance/vite.config.js | 58 + 49 files changed, 4927 insertions(+) create mode 100644 suites-experimental/finance/.gitignore create mode 100644 suites-experimental/finance/README.md create mode 100644 suites-experimental/finance/package-lock.json create mode 100644 suites-experimental/finance/package.json create mode 100644 suites-experimental/finance/postcss.config.js create mode 100644 suites-experimental/finance/src/app.css create mode 100644 suites-experimental/finance/src/app.html create mode 100644 suites-experimental/finance/src/lib/analytics.js create mode 100644 suites-experimental/finance/src/lib/charts.js create mode 100644 suites-experimental/finance/src/lib/components/CanvasSparkline.svelte create mode 100644 suites-experimental/finance/src/lib/components/Footer.svelte create mode 100644 suites-experimental/finance/src/lib/components/Header.svelte create mode 100644 suites-experimental/finance/src/lib/components/HeatMap.svelte create mode 100644 suites-experimental/finance/src/lib/components/Icon.svelte create mode 100644 suites-experimental/finance/src/lib/components/IconButton.svelte create mode 100644 suites-experimental/finance/src/lib/components/MarketSummary.svelte create mode 100644 suites-experimental/finance/src/lib/components/MarketsTable.svelte create mode 100644 suites-experimental/finance/src/lib/components/MoversCard.svelte create mode 100644 suites-experimental/finance/src/lib/components/Pagination.svelte create mode 100644 suites-experimental/finance/src/lib/components/PriceChange.svelte create mode 100644 suites-experimental/finance/src/lib/components/QuoteList.svelte create mode 100644 suites-experimental/finance/src/lib/components/RangeCell.svelte create mode 100644 suites-experimental/finance/src/lib/components/RatingCell.svelte create mode 100644 suites-experimental/finance/src/lib/components/ScreenerTabs.svelte create mode 100644 suites-experimental/finance/src/lib/components/SectorChips.svelte create mode 100644 suites-experimental/finance/src/lib/components/StoryItem.svelte create mode 100644 suites-experimental/finance/src/lib/components/StoryStream.svelte create mode 100644 suites-experimental/finance/src/lib/components/StoryStreamSkeleton.svelte create mode 100644 suites-experimental/finance/src/lib/components/SvgSparkline.svelte create mode 100644 suites-experimental/finance/src/lib/components/TableRow.svelte create mode 100644 suites-experimental/finance/src/lib/components/TickerCard.svelte create mode 100644 suites-experimental/finance/src/lib/components/TickerCarousel.svelte create mode 100644 suites-experimental/finance/src/lib/components/TickerChip.svelte create mode 100644 suites-experimental/finance/src/lib/components/TrendingCard.svelte create mode 100644 suites-experimental/finance/src/lib/data/api.js create mode 100644 suites-experimental/finance/src/lib/data/market.js create mode 100644 suites-experimental/finance/src/lib/data/random.js create mode 100644 suites-experimental/finance/src/lib/format.js create mode 100644 suites-experimental/finance/src/lib/quotes.svelte.js create mode 100644 suites-experimental/finance/src/routes/+layout.js create mode 100644 suites-experimental/finance/src/routes/+layout.svelte create mode 100644 suites-experimental/finance/src/routes/+page.js create mode 100644 suites-experimental/finance/src/routes/+page.svelte create mode 100644 suites-experimental/finance/src/routes/markets/stocks/[screener]/+page.js create mode 100644 suites-experimental/finance/src/routes/markets/stocks/[screener]/+page.svelte create mode 100644 suites-experimental/finance/tailwind.config.js create mode 100644 suites-experimental/finance/vite.config.js diff --git a/.eslintignore b/.eslintignore index 8f9434ed3..444da4dcd 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,6 +2,7 @@ **/dist package-lock.json **/.angular +**/.svelte-kit /suites/todomvc/architecture-examples/* !/suites/todomvc/architecture-examples/angular diff --git a/.prettierignore b/.prettierignore index 40d154c13..4a66ccedd 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,6 +2,7 @@ **/dist package-lock.json **/.angular +**/.svelte-kit /suites/todomvc/architecture-examples/* !/suites/todomvc/architecture-examples/angular diff --git a/suites-experimental/finance/.gitignore b/suites-experimental/finance/.gitignore new file mode 100644 index 000000000..8ee847558 --- /dev/null +++ b/suites-experimental/finance/.gitignore @@ -0,0 +1 @@ +.svelte-kit diff --git a/suites-experimental/finance/README.md b/suites-experimental/finance/README.md new file mode 100644 index 000000000..13ef65562 --- /dev/null +++ b/suites-experimental/finance/README.md @@ -0,0 +1,72 @@ +## Description + +Finance portals combine three things on one page: a news front page, data-dense +market tables, and quotes that keep moving while the page is open. Small charts +are everywhere: canvas sparklines in the market strip, an intraday area chart, and +an SVG sparkline per table row. + +This workload is an original, self-contained SvelteKit (Svelte 5) app, +"Quoteboard", that reproduces those interactions. Its features and their weight +were picked from Firefox profiles of a popular finance portal's home page and +screener pages: which modules each page loads, element counts (the largest +restyles touched about 2,400 elements on the home page and 4,000 on the screener +page), and how styles are organized. It is **not** derived from that site's source code or +assets. All companies, prices and stories are fictional and generated +deterministically at startup (seeded PRNG, fixed clock, no network), so every run +renders identical data. + +## What are we testing + +- SvelteKit client-side navigation (hash router): `load` functions, JSON parsing + of API-shaped responses, and mounting a route's component tree +- Svelte 5 fine-grained reactivity: streamed quote updates touch only the cells + that show them, and `{#key}` re-creates price spans to restart their flash + animation +- Canvas 2D drawing (paths, gradients, dashed lines, text), measured once on + mount and resized through a `ResizeObserver` +- SVG sparklines built as path strings. Charts render in their final state, + with no draw-in animations +- A 13-column auto-layout table that is sorted, paginated and resized +- A squarified treemap heatmap of absolutely positioned tiles +- The profiled site's styling architecture: Svelte component-scoped CSS (hashed + `qb-` classes) next to `tw-`-prefixed Tailwind utilities, which makes style + computation cost per element comparable to the profiled site's +- A container query, `content-visibility: auto`, sticky headers, skeleton loaders +- Page-view instrumentation like real portals ship: a `MutationObserver`, + `checkVisibility()`, `getComputedStyle()` and `getBoundingClientRect()` per + module, and a link audit, serialized into a beacon payload that isn't sent + +## How are we testing + +The suite starts on the home page, which is rendered while loading and is not +measured. + +- `NavigateToTrending` clicks "Trending tickers" and waits until the screener + page has rendered a strip of quote cards, sector chips, its first 25 rows and + 25 related news stories. +- `SortTable` sorts by five columns in turn. +- `PaginateTable` steps through three pages, then shows 50 rows per page. +- `ToggleHeatmap` switches to the heatmap view and back to the table. +- `StreamQuotes` presses "Go live", which replays 20 quote messages, one per + task. Each updates up to 24 subscribed symbols, which flash and redraw their + charts. +- `NavigateToHome` clicks the logo. The home page renders a skeleton, then the + news stream that arrives a few tasks later. + +Simulated responses arrive on `MessageChannel` tasks rather than timers, so the +measured time doesn't include timer clamping. Navigations are the only history +entries, two per iteration, which keeps the workload well clear of browsers' +`pushState` rate limits. + +## Developer Documentation + +The app was created with SvelteKit and `@sveltejs/adapter-static`, using the hash +router with a single bundle (`output.bundleStrategy: "single"`), so it can be +served from any subdirectory and never fetches code during a step. It can be +previewed during development with `npm run dev`. To update the files run in the +harness, run `npm run build`, which regenerates the committed `dist/` directory, +including `resources.txt`. + +The built workload can be loaded within the harness at e.g. +`http://localhost:8080/?developerMode&suites=Finance-SvelteKit`, or directly at +`http://localhost:8080/suites-experimental/finance/dist/index.html`. diff --git a/suites-experimental/finance/package-lock.json b/suites-experimental/finance/package-lock.json new file mode 100644 index 000000000..0d0aaec1b --- /dev/null +++ b/suites-experimental/finance/package-lock.json @@ -0,0 +1,2163 @@ +{ + "name": "finance-sveltekit", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "finance-sveltekit", + "version": "0.0.0", + "devDependencies": { + "@sveltejs/adapter-static": "^4.0.1", + "@sveltejs/kit": "^3.2.0", + "@sveltejs/vite-plugin-svelte": "^7.3.1", + "postcss": "^8.5.32", + "svelte": "^5.57.3", + "tailwindcss": "^3.4.19", + "vite": "^8.3.8" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.3.0.tgz", + "integrity": "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emnapi/core": { + "version": "2.0.0-beta.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-2.0.0-beta.2.tgz", + "integrity": "sha512-MzCVoWtcg4Pm2B3je5mMvYMvj3MmBKw+fU7vbsJc9/iT8ip6/eYIHo/Dsm/mr+wsHB6SEyJDuxB09Yy6r/8+4A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "2.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "2.0.0-beta.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-2.0.0-beta.2.tgz", + "integrity": "sha512-JcrSPzPvZuNZvJqPTF9Fw25LbOJ8avzFVlaf2grmH4Cku0oGP47tRgqPZadcfj3YoZMHkFVnCYFbQM8I+i5QEw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.2.0.tgz", + "integrity": "sha512-JP7OYH38Q7+pyTAe14raZHLQIK7BDKbdwji+Xi3pcd/6QkJYQH3SX4wu6EplGrGJWRepoNzedfm0G3B9Z4e9Rw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.3.tgz", + "integrity": "sha512-lhTtjtoEArWBjismtfS761S9Hlyn+nIZqOZVYoZ1Xn82PMNMVnk+jj/NhC4fAXGTs7ydONcJiR5vyQBTMDu27g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.6.tgz", + "integrity": "sha512-mRyDmEw9/fCDXCGfL1sbgIw/GUK0PVxRW4YwgPN61F3LQ+fSyoBFarJVzgGyhF6kRzEQZ0FejDqm4WLXSeqQmw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || 2.0.0-beta.2", + "@emnapi/runtime": "^1.7.1 || 2.0.0-beta.2" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.156.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.156.0.tgz", + "integrity": "sha512-1Duyg48TdJNdyhEB8Xn1L36bT9Pv6GUtlKjHxOys8xkaF/rQq3XJE+bAurSK/AgIz5mGFsb77cn9Pb20+WCScw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.19.tgz", + "integrity": "sha512-e+Ebe0WT+9tbXmNKPIiC8YrWAHYwug3m6i4p3rZGGulfZri1jtI4HslT+OZnFf/OIHowWF71DTlHwXBP2SiA7A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.19.tgz", + "integrity": "sha512-hOombrTJ+szt2ciEzS7oCA78lMPBgF5UMQWCJIyNfnrI7GLl8HYoFqCnpL2gcPOaXMqryGCU5ExdQKyDDcC+yA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.19.tgz", + "integrity": "sha512-XoMA2pwCKGTQzUjPgtRwkedH4WmXhdIPKT6l6oOxd1PUbNodGI6DcBekgN7EC//+x3pfBYX/qL1gNmZAqivw5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.19.tgz", + "integrity": "sha512-zo8EsSSwMZCY/e+fRnGaX02YGgvJD0yvhNi/9hpGPA2MJ9ZF8dPCw75UGfpI6OpQ/myR/dIYmogRvqiZD0TrXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.19.tgz", + "integrity": "sha512-O8ISAOfI1nj7AuBPd30FSF47z7f7lzlk5poQVGgiikvQuGEBFYvW8ki+UEnT+YJPnNGjQzn304klt4O9FUE7mA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.19.tgz", + "integrity": "sha512-/lYCba5NO0yq4S0jGMH8QTBtkvQxoXjoQWzF5BcWp9MjB9jxVRD1GQDNofkVjcOj30DWyhLIN/4Oe60JKzaONw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.19.tgz", + "integrity": "sha512-pm/apFMBnSbFb3O6nG5pHO1iqHhZrjGDFEjSm+ot9J17XXPvH9L90y5h+jgfHCE+Qdlsvy8qKEcU4VKxgjD+vA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.19.tgz", + "integrity": "sha512-K1SmQp3FFichn8oqrNIwvFoZ5wcvjC7goBzpaMNvLf9FZc+QB/QvLmnpJZgAAGHiD980tP8FZZnXk2K94rvlxg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.19.tgz", + "integrity": "sha512-I0xwD57jP/3A7+5UPZin2w8Y7XHEmEM5zqpYZOcQ45MlNW6tUJUziymV3yRr7rL4vwyfEcRsRksC4a7Kc08p5Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.19.tgz", + "integrity": "sha512-BrtEHT24SNXj9HiXqy+s/kXiSiQGt6EKCPUlRirAoIioVxKGnbXBUEkbZASAei7iZJB0/Ynm+0JP00Ipx7AnSw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.19.tgz", + "integrity": "sha512-rm75/9DkHKeU2WnA8w4X94WGYgLlDCFCfkQC+4bguVJmC/jvDRv2B+2wLOYhyE0fVuv7//Ckk39OoOufyCu76Q==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.19.tgz", + "integrity": "sha512-irh/4+iYFM4GAzpluDErgjz89bj6NDtqqMAG/6O1LG38Ed3NpyebzCpcVNkFW8uhy+/3/BocUvnXDR33CXWe9A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.19.tgz", + "integrity": "sha512-+KWTWNoPt+CfSq94CiceWu3yPE+QzkaG+phPyz9G60tmZLwfQkcA4AeksafzvJAw4pvg1mcd5a1TkUqVv0s4JA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.19.tgz", + "integrity": "sha512-7EH+skOsl5qpWnK0vzamgJFhOv1cwxQpsxm8tI883K9fur5c27p8q87fzEO6BbBPabA2/+s/zR63/pLc137iOg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "2.0.0-beta.2", + "@emnapi/runtime": "2.0.0-beta.2", + "@napi-rs/wasm-runtime": "1.2.6" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.19.tgz", + "integrity": "sha512-3Q1Yg1Q7tEvBWoZf5rFvG/GKZQPgwfru+p4wBajDfJFBs0SHCpL++dfTjW9gyAKd1T7ZpIYvdaUax7Pa/IhFVQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.19.tgz", + "integrity": "sha512-y/sqRRc6d7hECzOFc5n3LSeqctmIp9mMjY1Kq4s14uLPd/LZ4z7i1zrW6fMtp2mPeuQIC4e6ULMcwQzMomqQag==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.1.1.tgz", + "integrity": "sha512-glFIJDvLNDtJhT9qrMFWLPbvS+skxgt4L0K8Ei2YtnhPysAJnsI7nyxSJxKj4ADeKX0OLjC4ntRkwpLOoS3rwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.13.tgz", + "integrity": "sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-static": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-4.0.1.tgz", + "integrity": "sha512-svOU1OcBiJ9Kn1cdxHDmJIsBy1/iQe9STPvUFkO2/iinxPFS7r8fc9TiCaWWUgbrE+7ox6Bt4MURv3RPi7sunQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@sveltejs/kit": "^3.0.0-next.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-3.2.0.tgz", + "integrity": "sha512-zhb8GTeFd/6BwfqaY4wKIF7eFACYOgn/KNppV6UHFBHk174B4d7xTtN7x0PxaPPlwnCTgbnpaYF6r9lirz0Biw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "@rolldown/pluginutils": "^1.0.1", + "@standard-schema/spec": "^1.1.0", + "cookie": "^2.0.1", + "devalue": "^5.9.4", + "esm-env": "^1.2.2", + "magic-string": "^1.2.3", + "mrmime": "^2.0.1", + "sirv": "^3.0.2" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=22.17" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^7.0.0", + "svelte": "^5.57.1", + "typescript": "^6.0.0", + "vite": "^8.0.12" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.3.1.tgz", + "integrity": "sha512-ZPsLN8B1e/En+Ak5s4V7srFDT532oS0qieLsQwu63NGKsS+iAjoO2Js1BochlHlglcU+Pt7WAO3C5Ee+4f6gVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "deepmerge": "^4.3.1", + "magic-string": "^1.0.0", + "obug": "^2.1.0", + "vitefu": "^1.1.2" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "svelte": "^5.46.4", + "vite": "^8.0.0-beta.7 || ^8.0.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz", + "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.10.tgz", + "integrity": "sha512-lTyOMnOcmRBzdCv6/4zUZCrEhV0xrqQiUZBZtVcbmLHBXKLnN8q89v/wKRF/Q0HbwVsqbIG3o5HDoFl9sduM+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/aria-query": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cookie": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.2.tgz", + "integrity": "sha512-SL0cVuEdETEp7pN0FuZb01toc3vnODEfVUCooxG1SJN0yIirh7JMb9X9bvfCFf0vzzbT68RdbKDHYTiWy7a7iw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.4.tgz", + "integrity": "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==", + "dev": true, + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esrap": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.4.0.tgz", + "integrity": "sha512-CQPcneEHw/czj32uwQC52nYnax8H4SXt25FCM+LIwGZ49cMIxpOj3bP8wmzcI+Z0HIy5KkClaPhRLfCySzwHPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "peerDependencies": { + "@typescript-eslint/types": "^8.2.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/types": { + "optional": true + } + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.18.0.tgz", + "integrity": "sha512-qyax9fgaKOEC4qK8WZn7Q4IcoZC9RC1O1fFxRcRTq/oMMkWT/uXpqpXa12kzWR409zAt1drYN9N9BO26UmBCmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.5.tgz", + "integrity": "sha512-aRAOI9Ia43ygL4o1EOmC/sc/0OFxrm7qcQngiIyWaqVnSnz932ybJ07zyCcgMNGSDP8Yxaf88Xj1zHEXmvQ4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.5.tgz", + "integrity": "sha512-oLLC2+YvQUfAa+vxoufE/yc1icb87M9iXIRqAoR/2EhE/8cb3SF/+Gi0c2By1B+5Vz2e2vQtUvHiu1+63Znh+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/obug": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz", + "integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.6.tgz", + "integrity": "sha512-BK87/Qgti3DIcgS2PaCVoteFIkjIFvCJBizZDIVSjdlFNg4Jv+SRqbN2JONb/qQUH4ZFqfXYF+izg15W2HVRvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.2.tgz", + "integrity": "sha512-/peqiBB/n07gQGLsWaHho3WfvUyRscw0gYTsEFMhrIe/nWLkYaf5SbKYjGYqtRV3aPwykJgF2VEMo1ac4bnsGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/resolve": { + "version": "1.22.14", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.14.tgz", + "integrity": "sha512-1ZjYvR9sbkiwC5+9QHetqW5HmA2pHRMaoupD01t2HlYnV/vydC2zPwLixhsMieSWVZt0JJRg5LVIk0Ow1D/TrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.18.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.19.tgz", + "integrity": "sha512-QIiV1J9d0pzN4jeeTH4sjPy0AZ6cAy2cG0ExeoE1+U8Rvj+hR3gYkokx0YbeUNGlZqiQJJ2utcETe0oKXwzsuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.156.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.19", + "@rolldown/binding-android-arm64": "1.2.19", + "@rolldown/binding-darwin-arm64": "1.2.19", + "@rolldown/binding-darwin-x64": "1.2.19", + "@rolldown/binding-freebsd-x64": "1.2.19", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.19", + "@rolldown/binding-linux-arm64-gnu": "1.2.19", + "@rolldown/binding-linux-arm64-musl": "1.2.19", + "@rolldown/binding-linux-ppc64-gnu": "1.2.19", + "@rolldown/binding-linux-s390x-gnu": "1.2.19", + "@rolldown/binding-linux-x64-gnu": "1.2.19", + "@rolldown/binding-linux-x64-musl": "1.2.19", + "@rolldown/binding-openharmony-arm64": "1.2.19", + "@rolldown/binding-wasm32-wasi": "1.2.19", + "@rolldown/binding-win32-arm64-msvc": "1.2.19", + "@rolldown/binding-win32-x64-msvc": "1.2.19" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svelte": { + "version": "5.57.3", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.57.3.tgz", + "integrity": "sha512-5mAftM3SoDV9X0zdF386F9p2pv8iKOwi/KfBS1XnBAkVCKFzVOLHUoa8iGWAt4buoicLmyWVRpuuD5wplXOQsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "@jridgewell/sourcemap-codec": "^1.6.0", + "@sveltejs/acorn-typescript": "^1.0.13", + "@types/estree": "^1.0.9", + "acorn": "^8.18.0", + "aria-query": "5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.9.2", + "esm-env": "^1.2.1", + "esrap": "^2.3.6", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte/node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.9.1.tgz", + "integrity": "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.3.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.8.tgz", + "integrity": "sha512-LzOn3h9rG/0vhLdXrV4yJ4QFA9eEoqCl+Yx+kGJd7aB9w6nfNgVHKSYwLPZUHrz4ZGa+t+kXjV9itvcHDLQouA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.32", + "rolldown": "~1.2.18", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1 || ^0.8.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/zimmerframe": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.5.tgz", + "integrity": "sha512-msJxIvYDYcoNL+PJsu+7qmpDWsYmAxTY+2TNYXXF0hzBzBk0BMecOqDOG/EckUoKCuKwObfbugIl8QpqHDXeFA==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/suites-experimental/finance/package.json b/suites-experimental/finance/package.json new file mode 100644 index 000000000..9201b2e55 --- /dev/null +++ b/suites-experimental/finance/package.json @@ -0,0 +1,23 @@ +{ + "name": "finance-sveltekit", + "private": true, + "version": "0.0.0", + "type": "module", + "imports": { + "#lib/*": "./src/lib/*" + }, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@sveltejs/adapter-static": "^4.0.1", + "@sveltejs/kit": "^3.2.0", + "@sveltejs/vite-plugin-svelte": "^7.3.1", + "postcss": "^8.5.32", + "svelte": "^5.57.3", + "tailwindcss": "^3.4.19", + "vite": "^8.3.8" + } +} diff --git a/suites-experimental/finance/postcss.config.js b/suites-experimental/finance/postcss.config.js new file mode 100644 index 000000000..58d7a289b --- /dev/null +++ b/suites-experimental/finance/postcss.config.js @@ -0,0 +1,5 @@ +import tailwindcss from "tailwindcss"; + +export default { + plugins: [tailwindcss()], +}; diff --git a/suites-experimental/finance/src/app.css b/suites-experimental/finance/src/app.css new file mode 100644 index 000000000..4fa5d2c51 --- /dev/null +++ b/suites-experimental/finance/src/app.css @@ -0,0 +1,120 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --ink: #16191f; + --muted: #5a6270; + --line: #e2e5ea; + --surface: #ffffff; + --canvas: #f5f6f8; + --accent: #0b7a75; + --accent-soft: #e3f2f1; + --up: #0a8a5f; + --down: #d1344b; + --up-flash: rgba(10, 138, 95, 0.22); + --down-flash: rgba(209, 52, 75, 0.22); + --radius: 10px; + font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + font-size: 14px; + line-height: 1.4; + color: var(--ink); + background: var(--canvas); +} + +body { + margin: 0; +} + +@layer components { + .up { + color: var(--up); + } + + .down { + color: var(--down); + } + + .card { + background: var(--surface); + border: 1px solid var(--line); + border-radius: var(--radius); + padding: 16px; + } + + .section-title { + font-size: 18px; + font-weight: 700; + margin-bottom: 12px; + } + + .switch-group { + display: inline-flex; + padding: 3px; + border-radius: 999px; + background: var(--canvas); + border: 1px solid var(--line); + } + + .switch { + padding: 4px 12px; + border-radius: 999px; + font-size: 12px; + font-weight: 600; + color: var(--muted); + } + + .switch.active { + background: var(--surface); + color: var(--ink); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12); + } + + .live-value { + border-radius: 3px; + padding: 0 2px; + font-variant-numeric: tabular-nums; + } + + .flash-up { + animation: flash-up 0.9s ease-out; + } + + .flash-down { + animation: flash-down 0.9s ease-out; + } + + .loader { + border-radius: 6px; + background: linear-gradient(90deg, #eceef1 25%, #f6f7f9 37%, #eceef1 63%); + background-size: 400% 100%; + animation: shimmer 1.4s ease infinite; + } +} + +@keyframes flash-up { + from { + background-color: var(--up-flash); + } + to { + background-color: transparent; + } +} + +@keyframes flash-down { + from { + background-color: var(--down-flash); + } + to { + background-color: transparent; + } +} + +@keyframes shimmer { + from { + background-position: 100% 50%; + } + to { + background-position: 0 50%; + } +} diff --git a/suites-experimental/finance/src/app.html b/suites-experimental/finance/src/app.html new file mode 100644 index 000000000..e168362c5 --- /dev/null +++ b/suites-experimental/finance/src/app.html @@ -0,0 +1,12 @@ + + + + + + Finance + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/suites-experimental/finance/src/lib/analytics.js b/suites-experimental/finance/src/lib/analytics.js new file mode 100644 index 000000000..b67aee475 --- /dev/null +++ b/suites-experimental/finance/src/lib/analytics.js @@ -0,0 +1,77 @@ +// Page-view instrumentation in the style finance portals ship: on every view it +// collects module impressions (visibility, computed style, geometry), audits +// outgoing links, and serializes a beacon payload. Nothing is sent anywhere. + +const AUDITED_PATTERNS = ["utm_", "affiliate", "/sponsored/", "doubleclick", "partner=", "/promo/", "tracking", "redirect?", "ref=", "campaign"]; +const beacons = []; +let pendingImpressions = 0; +let observer = null; + +export function observeModules(root) { + observer?.disconnect(); + observer = new MutationObserver((records) => { + for (const record of records) { + for (const node of record.addedNodes) { + if (node.nodeType === Node.ELEMENT_NODE && node.hasAttribute("data-module")) + pendingImpressions++; + } + + } + }); + observer.observe(root, { childList: true, subtree: true }); +} + +function auditLinks(root) { + const flagged = []; + const anchors = root.querySelectorAll("a[href]"); + for (const anchor of anchors) { + const href = anchor.href; + for (const pattern of AUDITED_PATTERNS) { + if (href.indexOf(pattern) !== -1) { + flagged.push(pattern); + break; + } + } + } + return { count: anchors.length, flagged }; +} + +function collectImpressions(root) { + const viewportHeight = root.ownerDocument.documentElement.clientHeight; + const impressions = []; + for (const module of root.querySelectorAll("[data-module]")) { + if (!module.checkVisibility({ opacityProperty: true, visibilityProperty: true })) + continue; + const style = getComputedStyle(module); + const rect = module.getBoundingClientRect(); + impressions.push({ + module: module.dataset.module, + sticky: style.position === "sticky" || style.position === "fixed", + display: style.display, + top: Math.round(rect.top), + height: Math.round(rect.height), + inView: rect.top < viewportHeight && rect.bottom > 0, + }); + } + return impressions; +} + +export function trackPageView(root, page) { + const payload = { + page, + url: root.ownerDocument.location.hash, + mutations: pendingImpressions, + impressions: collectImpressions(root), + links: auditLinks(root), + }; + pendingImpressions = 0; + beacons.push(JSON.stringify(payload)); + if (beacons.length > 20) + beacons.shift(); +} + +export function pageReady(page) { + trackPageView(document.body, page); + document.documentElement.dataset.readyPage = page; + window.dispatchEvent(new CustomEvent("finance-page-ready", { detail: page })); +} diff --git a/suites-experimental/finance/src/lib/charts.js b/suites-experimental/finance/src/lib/charts.js new file mode 100644 index 000000000..0702d7be2 --- /dev/null +++ b/suites-experimental/finance/src/lib/charts.js @@ -0,0 +1,219 @@ +const UP = [10, 138, 95]; +const DOWN = [209, 52, 75]; + +function rgba([r, g, b], alpha) { + return `rgba(${r}, ${g}, ${b}, ${alpha})`; +} + +// Sizes the backing store to the element's laid-out size. Callers pass a size +// they have measured once rather than reading layout on every draw. +function prepareCanvas(canvas, width, height) { + const ratio = window.devicePixelRatio || 1; + const backingWidth = Math.round(width * ratio); + const backingHeight = Math.round(height * ratio); + if (canvas.width !== backingWidth || canvas.height !== backingHeight) { + canvas.width = backingWidth; + canvas.height = backingHeight; + } + const context = canvas.getContext("2d"); + context.setTransform(ratio, 0, 0, ratio, 0, 0); + context.clearRect(0, 0, width, height); + return { context, width, height }; +} + +export function extent(series, baseline) { + let min = baseline; + let max = baseline; + for (const value of series) { + if (value < min) + min = value; + if (value > max) + max = value; + } + if (min === max) + return [min - 1, max + 1]; + return [min, max]; +} + +function tracePath(context, series, scaleX, scaleY) { + context.beginPath(); + for (let i = 0; i < series.length; i++) { + if (i === 0) + context.moveTo(scaleX(i), scaleY(series[i])); + else + context.lineTo(scaleX(i), scaleY(series[i])); + } +} + +function drawSeries(context, series, baseline, width, top, bottom, left, right, lineWidth) { + const [min, max] = extent(series, baseline); + const color = series[series.length - 1] >= baseline ? UP : DOWN; + const scaleX = (i) => left + (i / (series.length - 1)) * (right - left); + const scaleY = (value) => top + (1 - (value - min) / (max - min)) * (bottom - top); + + tracePath(context, series, scaleX, scaleY); + context.lineTo(scaleX(series.length - 1), bottom); + context.lineTo(scaleX(0), bottom); + context.closePath(); + const gradient = context.createLinearGradient(0, top, 0, bottom); + gradient.addColorStop(0, rgba(color, 0.28)); + gradient.addColorStop(1, rgba(color, 0)); + context.fillStyle = gradient; + context.fill(); + + tracePath(context, series, scaleX, scaleY); + context.strokeStyle = rgba(color, 1); + context.lineWidth = lineWidth; + context.lineJoin = "round"; + context.stroke(); + + context.beginPath(); + context.setLineDash([2, 3]); + context.moveTo(left, scaleY(baseline)); + context.lineTo(right, scaleY(baseline)); + context.strokeStyle = "rgba(90, 98, 112, 0.7)"; + context.lineWidth = 1; + context.stroke(); + context.setLineDash([]); + + return { min, max, scaleY }; +} + +export function drawSparkline(canvas, size, series, baseline) { + const { context, width, height } = prepareCanvas(canvas, size.width, size.height); + drawSeries(context, series, baseline, width, 2, height - 2, 0, width, 1.5); +} + +export function drawAreaChart(canvas, size, series, baseline, timeLabels, formatValue) { + const { context, width, height } = prepareCanvas(canvas, size.width, size.height); + const axisWidth = 64; + const axisHeight = 22; + const right = width - axisWidth; + const bottom = height - axisHeight; + const { min, max, scaleY } = drawSeries(context, series, baseline, width, 8, bottom, 0, right, 2); + + context.font = "11px system-ui, sans-serif"; + context.fillStyle = "#5a6270"; + context.strokeStyle = "rgba(90, 98, 112, 0.15)"; + context.lineWidth = 1; + context.textBaseline = "middle"; + const steps = 5; + for (let i = 0; i <= steps; i++) { + const value = min + ((max - min) * i) / steps; + const y = Math.round(scaleY(value)) + 0.5; + context.beginPath(); + context.moveTo(0, y); + context.lineTo(right, y); + context.stroke(); + context.fillText(formatValue(value), right + 8, y); + } + + context.textBaseline = "alphabetic"; + context.textAlign = "center"; + for (let i = 0; i < timeLabels.length; i++) { + const x = (i / (timeLabels.length - 1)) * right; + const label = timeLabels[i]; + const labelWidth = context.measureText(label).width; + context.fillText(label, Math.min(Math.max(x, labelWidth / 2), right - labelWidth / 2), height - 6); + } + context.textAlign = "start"; +} + +export function svgSparklinePath(series, baseline, width, height) { + const [min, max] = extent(series, baseline); + const scaleY = (value) => 1 + (1 - (value - min) / (max - min)) * (height - 2); + let path = ""; + for (let i = 0; i < series.length; i++) { + const x = (i / (series.length - 1)) * width; + path += `${i ? "L" : "M"}${x.toFixed(1)} ${scaleY(series[i]).toFixed(1)}`; + } + return { path, baselineY: scaleY(baseline).toFixed(1) }; +} + +const HEAT_STOPS = [ + [-3, [198, 47, 69]], + [0, [93, 100, 112]], + [3, [19, 145, 95]], +]; + +export function heatColor(changePercent) { + const value = Math.max(-3, Math.min(3, changePercent)); + for (let i = 1; i < HEAT_STOPS.length; i++) { + const [toValue, to] = HEAT_STOPS[i]; + if (value <= toValue) { + const [fromValue, from] = HEAT_STOPS[i - 1]; + const t = (value - fromValue) / (toValue - fromValue); + const channel = (c) => Math.round(from[c] + (to[c] - from[c]) * t); + return `rgb(${channel(0)}, ${channel(1)}, ${channel(2)})`; + } + } + return `rgb(${HEAT_STOPS[2][1].join(", ")})`; +} + +function worstRatio(row, rowArea, side) { + let min = Infinity; + let max = 0; + for (const item of row) { + min = Math.min(min, item.area); + max = Math.max(max, item.area); + } + const sideSquared = side * side; + const areaSquared = rowArea * rowArea; + return Math.max((sideSquared * max) / areaSquared, areaSquared / (sideSquared * min)); +} + +// Squarified treemap (Bruls, Huizing, van Wijk). Items need a positive value. +export function squarify(items, x, y, width, height) { + const total = items.reduce((sum, item) => sum + item.value, 0); + const scale = (width * height) / total; + const queue = items.map((item) => ({ item, area: item.value * scale })).sort((a, b) => b.area - a.area); + const rects = []; + + let row = []; + let rowArea = 0; + const layoutRow = () => { + const horizontal = width >= height; + const side = horizontal ? height : width; + const thickness = rowArea / side; + let offset = 0; + for (const entry of row) { + const length = entry.area / thickness; + if (horizontal) + rects.push({ item: entry.item, x, y: y + offset, width: thickness, height: length }); + else + rects.push({ item: entry.item, x: x + offset, y, width: length, height: thickness }); + offset += length; + } + if (horizontal) { + x += thickness; + width -= thickness; + } else { + y += thickness; + height -= thickness; + } + row = []; + rowArea = 0; + }; + + for (const entry of queue) { + const side = Math.min(width, height); + if (row.length && worstRatio([...row, entry], rowArea + entry.area, side) > worstRatio(row, rowArea, side)) + layoutRow(); + row.push(entry); + rowArea += entry.area; + } + if (row.length) + layoutRow(); + return rects; +} + +// Measures a canvas once on mount, then follows its resizes. +export function observeSize(canvas, onResize) { + onResize({ width: canvas.clientWidth, height: canvas.clientHeight }); + const observer = new ResizeObserver(([entry]) => { + const { inlineSize, blockSize } = entry.contentBoxSize[0]; + onResize({ width: inlineSize, height: blockSize }); + }); + observer.observe(canvas); + return () => observer.disconnect(); +} diff --git a/suites-experimental/finance/src/lib/components/CanvasSparkline.svelte b/suites-experimental/finance/src/lib/components/CanvasSparkline.svelte new file mode 100644 index 000000000..19dc8d2e8 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/CanvasSparkline.svelte @@ -0,0 +1,24 @@ + + + + + diff --git a/suites-experimental/finance/src/lib/components/Footer.svelte b/suites-experimental/finance/src/lib/components/Footer.svelte new file mode 100644 index 000000000..574368c4f --- /dev/null +++ b/suites-experimental/finance/src/lib/components/Footer.svelte @@ -0,0 +1,32 @@ + + + + + diff --git a/suites-experimental/finance/src/lib/components/Header.svelte b/suites-experimental/finance/src/lib/components/Header.svelte new file mode 100644 index 000000000..236ba6dfe --- /dev/null +++ b/suites-experimental/finance/src/lib/components/Header.svelte @@ -0,0 +1,153 @@ + + + + + diff --git a/suites-experimental/finance/src/lib/components/HeatMap.svelte b/suites-experimental/finance/src/lib/components/HeatMap.svelte new file mode 100644 index 000000000..218341f77 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/HeatMap.svelte @@ -0,0 +1,116 @@ + + + + + diff --git a/suites-experimental/finance/src/lib/components/Icon.svelte b/suites-experimental/finance/src/lib/components/Icon.svelte new file mode 100644 index 000000000..b1fe0e1e1 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/Icon.svelte @@ -0,0 +1,35 @@ + + + + + diff --git a/suites-experimental/finance/src/lib/components/IconButton.svelte b/suites-experimental/finance/src/lib/components/IconButton.svelte new file mode 100644 index 000000000..deb75bea2 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/IconButton.svelte @@ -0,0 +1,31 @@ + + + + + diff --git a/suites-experimental/finance/src/lib/components/MarketSummary.svelte b/suites-experimental/finance/src/lib/components/MarketSummary.svelte new file mode 100644 index 000000000..94d6547e6 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/MarketSummary.svelte @@ -0,0 +1,78 @@ + + +
+
+ {#each quotes as tab, index (tab.symbol)} + + {/each} +
+
+
+

{quote.name}

+ {quote.symbol} · At close: {formatTime(NOW)} EST +
+
+ + + `(${formatPercent(value)})`} colored /> +
+
+ +
+ + diff --git a/suites-experimental/finance/src/lib/components/MarketsTable.svelte b/suites-experimental/finance/src/lib/components/MarketsTable.svelte new file mode 100644 index 000000000..6284d0bf9 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/MarketsTable.svelte @@ -0,0 +1,130 @@ + + +
+ + + + {#each columns as column (column.id)} + + {/each} + + + + + {#each visible as row (row.quote.symbol)} + + {/each} + +
0 ? "ascending" : "descending") : undefined}> + {#if column.value || column.text} + + {:else} + {column.label} + {/if} + Actions
+
+ + diff --git a/suites-experimental/finance/src/lib/components/MoversCard.svelte b/suites-experimental/finance/src/lib/components/MoversCard.svelte new file mode 100644 index 000000000..c5fa7178b --- /dev/null +++ b/suites-experimental/finance/src/lib/components/MoversCard.svelte @@ -0,0 +1,22 @@ + + +
+

Top movers

+
+ {#each tabs as tab (tab.id)} + + {/each} +
+ + See more +
diff --git a/suites-experimental/finance/src/lib/components/Pagination.svelte b/suites-experimental/finance/src/lib/components/Pagination.svelte new file mode 100644 index 000000000..f54aaa8fc --- /dev/null +++ b/suites-experimental/finance/src/lib/components/Pagination.svelte @@ -0,0 +1,53 @@ + + +
+ + {first}-{last} of {total} results +
+ + {#each { length: pageCount }, index (index)} + + {/each} + +
+
+ + diff --git a/suites-experimental/finance/src/lib/components/PriceChange.svelte b/suites-experimental/finance/src/lib/components/PriceChange.svelte new file mode 100644 index 000000000..052197dbf --- /dev/null +++ b/suites-experimental/finance/src/lib/components/PriceChange.svelte @@ -0,0 +1,10 @@ + + +{#key quote.revision} + 0 && "flash-up", quote.revision && quote.direction < 0 && "flash-down"]}>{format(quote[field])} +{/key} diff --git a/suites-experimental/finance/src/lib/components/QuoteList.svelte b/suites-experimental/finance/src/lib/components/QuoteList.svelte new file mode 100644 index 000000000..5f97be953 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/QuoteList.svelte @@ -0,0 +1,59 @@ + + + + + diff --git a/suites-experimental/finance/src/lib/components/RangeCell.svelte b/suites-experimental/finance/src/lib/components/RangeCell.svelte new file mode 100644 index 000000000..0f1c490d7 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/RangeCell.svelte @@ -0,0 +1,48 @@ + + +
+
+ {formatPrice(low)} + {formatPrice(high)} +
+
+ + +
+
+ + diff --git a/suites-experimental/finance/src/lib/components/RatingCell.svelte b/suites-experimental/finance/src/lib/components/RatingCell.svelte new file mode 100644 index 000000000..86757ce77 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/RatingCell.svelte @@ -0,0 +1,38 @@ + + +
+ {rating.toFixed(1)} + {label} +
+ + diff --git a/suites-experimental/finance/src/lib/components/ScreenerTabs.svelte b/suites-experimental/finance/src/lib/components/ScreenerTabs.svelte new file mode 100644 index 000000000..9f78901f4 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/ScreenerTabs.svelte @@ -0,0 +1,42 @@ + + + + + diff --git a/suites-experimental/finance/src/lib/components/SectorChips.svelte b/suites-experimental/finance/src/lib/components/SectorChips.svelte new file mode 100644 index 000000000..f9aa347aa --- /dev/null +++ b/suites-experimental/finance/src/lib/components/SectorChips.svelte @@ -0,0 +1,60 @@ + + +
+ + {#each sectors as sector (sector.name)} + + {/each} +
+ + diff --git a/suites-experimental/finance/src/lib/components/StoryItem.svelte b/suites-experimental/finance/src/lib/components/StoryItem.svelte new file mode 100644 index 000000000..96713c1a4 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/StoryItem.svelte @@ -0,0 +1,150 @@ + + +
+ +
+
+ {#each story.tickers as quote (quote.symbol)} + + {/each} +
+

{story.title}

+

{story.summary}

+
+
+ + {story.source} + + + {#if story.premium}Plus{/if} +
+
+ + + +
+
+
+
+ + diff --git a/suites-experimental/finance/src/lib/components/StoryStream.svelte b/suites-experimental/finance/src/lib/components/StoryStream.svelte new file mode 100644 index 000000000..b4b38f9c8 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/StoryStream.svelte @@ -0,0 +1,23 @@ + + +
+

{title}

+ {#each stories as story (story.id)} + + {/each} +
+ + diff --git a/suites-experimental/finance/src/lib/components/StoryStreamSkeleton.svelte b/suites-experimental/finance/src/lib/components/StoryStreamSkeleton.svelte new file mode 100644 index 000000000..01109d3f8 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/StoryStreamSkeleton.svelte @@ -0,0 +1,35 @@ + + +
+ {#each { length: count }, index (index)} +
+
+
+
+
+
+
+
+
+ {/each} +
+ + diff --git a/suites-experimental/finance/src/lib/components/SvgSparkline.svelte b/suites-experimental/finance/src/lib/components/SvgSparkline.svelte new file mode 100644 index 000000000..96a99b14f --- /dev/null +++ b/suites-experimental/finance/src/lib/components/SvgSparkline.svelte @@ -0,0 +1,47 @@ + + +
+ +
+ + diff --git a/suites-experimental/finance/src/lib/components/TableRow.svelte b/suites-experimental/finance/src/lib/components/TableRow.svelte new file mode 100644 index 000000000..eec33512c --- /dev/null +++ b/suites-experimental/finance/src/lib/components/TableRow.svelte @@ -0,0 +1,98 @@ + + + + +
+ + + {quote.symbol[0]} + + {quote.symbol} + {row.exchange} + + +
+ + +
+ {quote.name} + {row.sector} +
+ + + + + + + + + + + {formatCompact(quote.volume)} + {formatCompact(row.avgVolume)} + {formatCompact(row.marketCap)} + {formatRatio(row.peRatio)} + {formatPercent(row.fiftyTwoWeekChangePercent)} + + + + + + + diff --git a/suites-experimental/finance/src/lib/components/TickerCard.svelte b/suites-experimental/finance/src/lib/components/TickerCard.svelte new file mode 100644 index 000000000..ec73f3095 --- /dev/null +++ b/suites-experimental/finance/src/lib/components/TickerCard.svelte @@ -0,0 +1,55 @@ + + +
+ +
+ + + + `(${formatPercent(value)})`} colored /> + +
+ +
+ + diff --git a/suites-experimental/finance/src/lib/components/TickerCarousel.svelte b/suites-experimental/finance/src/lib/components/TickerCarousel.svelte new file mode 100644 index 000000000..413a441de --- /dev/null +++ b/suites-experimental/finance/src/lib/components/TickerCarousel.svelte @@ -0,0 +1,92 @@ + + +
+ +
+ {#each quotes as quote (quote.symbol)} + + {/each} +
+ + {#if live} + + {/if} +
+ + diff --git a/suites-experimental/finance/src/lib/components/TickerChip.svelte b/suites-experimental/finance/src/lib/components/TickerChip.svelte new file mode 100644 index 000000000..8a7d5a84d --- /dev/null +++ b/suites-experimental/finance/src/lib/components/TickerChip.svelte @@ -0,0 +1,33 @@ + + + + {quote.symbol} + + + + + + + diff --git a/suites-experimental/finance/src/lib/components/TrendingCard.svelte b/suites-experimental/finance/src/lib/components/TrendingCard.svelte new file mode 100644 index 000000000..5a5bdb96a --- /dev/null +++ b/suites-experimental/finance/src/lib/components/TrendingCard.svelte @@ -0,0 +1,10 @@ + + +
+

Trending tickers

+ +
diff --git a/suites-experimental/finance/src/lib/data/api.js b/suites-experimental/finance/src/lib/data/api.js new file mode 100644 index 000000000..5cd206701 --- /dev/null +++ b/suites-experimental/finance/src/lib/data/api.js @@ -0,0 +1,70 @@ +import { SCREENERS, companies, indices, screenerResults, stories } from "./market.js"; + +// The app talks to its "backend" through serialized responses, so every page +// load pays for JSON.parse and for turning the payload into view models, the +// way it would with a real fetch. The strings are built once at startup, +// outside the measured steps. +const responses = new Map(); + +function serve(path, body) { + responses.set(path, JSON.stringify(body)); +} + +for (const screener of SCREENERS) { + serve(`/v1/screener/${screener.id}`, { + id: screener.id, + title: screener.label, + total: companies.length, + quotes: screenerResults(screener.id).map(({ interest, ...quote }) => quote), + }); +} + +serve("/v1/market-summary", { quotes: indices }); + +function snapshot({ symbol, name, price, change, changePercent, previousClose, volume, series }) { + return { symbol, name, price, change, changePercent, previousClose, volume, series }; +} + +const bySymbol = new Map(companies.map((company) => [company.symbol, company])); + +serve("/v1/news", { + stories: stories.map((story) => ({ ...story, tickers: story.tickers.map((symbol) => snapshot(bySymbol.get(symbol))) })), +}); + +function topBy(rank, count) { + return [...companies] + .sort((a, b) => rank(a) - rank(b)) + .slice(0, count) + .map(snapshot); +} + +serve("/v1/movers", { + trending: topBy((q) => -q.interest, 8), + gainers: topBy((q) => -q.changePercent, 6), + losers: topBy((q) => q.changePercent, 6), + active: topBy((q) => -q.volume, 6), +}); + +// Responses arrive on a later task, like a network response would. A +// MessageChannel rather than setTimeout(0), so a measured step doesn't pay for +// the timer clamp. +const channel = new MessageChannel(); +const pending = []; +channel.port1.onmessage = () => pending.shift()(); + +export function nextTask() { + return new Promise((resolve) => { + pending.push(resolve); + channel.port2.postMessage(0); + }); +} + +// `latency` is in tasks, so slower endpoints resolve after faster ones. +export async function fetchJSON(path, latency = 1) { + const body = responses.get(path); + if (body === undefined) + throw new Error(`No response for ${path}`); + for (let i = 0; i < latency; i++) + await nextTask(); + return JSON.parse(body); +} diff --git a/suites-experimental/finance/src/lib/data/market.js b/suites-experimental/finance/src/lib/data/market.js new file mode 100644 index 000000000..53d0b052b --- /dev/null +++ b/suites-experimental/finance/src/lib/data/market.js @@ -0,0 +1,223 @@ +import { createRandom, pick } from "./random.js"; + +// Fixed "now" (a Thursday, 21:00 UTC, the US close) instead of Date.now(). +export const NOW = Date.UTC(2026, 10, 12, 21, 0); +export const SESSION_OPEN = Date.UTC(2026, 10, 12, 14, 30); +export const POINTS_PER_SESSION = 78; +export const POINT_INTERVAL_MS = 5 * 60 * 1000; + +const SECTORS = [ + { name: "Technology", suffixes: ["Systems", "Semiconductor", "Software", "Networks", "Devices"] }, + { name: "Healthcare", suffixes: ["Therapeutics", "Biosciences", "Medical", "Health"] }, + { name: "Financial Services", suffixes: ["Bancorp", "Capital", "Financial", "Trust"] }, + { name: "Consumer Cyclical", suffixes: ["Motors", "Retail", "Brands", "Apparel"] }, + { name: "Industrials", suffixes: ["Industries", "Aerospace", "Logistics", "Machinery"] }, + { name: "Communication Services", suffixes: ["Media", "Telecom", "Interactive", "Studios"] }, + { name: "Energy", suffixes: ["Energy", "Petroleum", "Resources", "Power"] }, + { name: "Consumer Defensive", suffixes: ["Foods", "Beverages", "Household", "Grocers"] }, + { name: "Utilities", suffixes: ["Utilities", "Electric", "Water", "Gas"] }, + { name: "Real Estate", suffixes: ["Realty", "Properties", "REIT", "Land"] }, + { name: "Basic Materials", suffixes: ["Materials", "Chemicals", "Mining", "Steel"] }, +]; + +const NAME_HEADS = ["Ar", "Bel", "Cor", "Dyn", "El", "Fen", "Gal", "Hel", "Ion", "Jun", "Kel", "Lum", "Mer", "Nov", "Or", "Pax", "Quin", "Ros", "Sol", "Tor", "Ul", "Ver", "Wex", "Xan", "Yor", "Zen"]; +const NAME_TAILS = ["ada", "ex", "ion", "ora", "ics", "tek", "via", "lon", "ara", "is", "ent", "ix", "um", "eon"]; +const LEGAL = ["Inc.", "Corp.", "Holdings", "Group", "Ltd.", "Co."]; +const EXCHANGES = ["NMS", "NYQ", "NGM", "ASE"]; + +const COMPANY_COUNT = 240; + +function randomWalk(random, start, count, volatility, drift) { + const series = new Array(count); + let value = start; + for (let i = 0; i < count; i++) { + value *= 1 + drift + (random() - 0.5) * volatility; + series[i] = Math.round(value * 100) / 100; + } + return series; +} + +function makeSymbol(name, used, random) { + const letters = name.replace(/[^A-Z]/gi, "").toUpperCase(); + for (let length = 3; length <= 4; length++) { + for (let attempt = 0; attempt < 8; attempt++) { + let symbol = letters[0]; + while (symbol.length < length) + symbol += letters[1 + Math.floor(random() * (letters.length - 1))]; + if (!used.has(symbol)) { + used.add(symbol); + return symbol; + } + } + } + const fallback = `${letters.slice(0, 2)}${used.size}`; + used.add(fallback); + return fallback; +} + +function buildCompanies() { + const random = createRandom(0x5eed); + const usedNames = new Set(); + const usedSymbols = new Set(); + const companies = []; + while (companies.length < COMPANY_COUNT) { + const sector = SECTORS[companies.length % SECTORS.length]; + const base = pick(random, NAME_HEADS) + pick(random, NAME_TAILS); + const name = `${base} ${pick(random, sector.suffixes)} ${pick(random, LEGAL)}`; + if (usedNames.has(base)) + continue; + usedNames.add(base); + + const symbol = makeSymbol(base + sector.suffixes[0], usedSymbols, random); + const previousClose = Math.round((8 + random() ** 2 * 700) * 100) / 100; + const volatility = 0.004 + random() * 0.012; + const drift = (random() - 0.48) * 0.0012; + const series = randomWalk(random, previousClose, POINTS_PER_SESSION, volatility, drift); + const price = series[series.length - 1]; + const yearSeries = randomWalk(random, price * (0.6 + random() * 0.6), 52, 0.08, 0.002); + yearSeries[yearSeries.length - 1] = price; + const fiftyTwoWeekLow = Math.min(...yearSeries, ...series); + const fiftyTwoWeekHigh = Math.max(...yearSeries, ...series); + const sharesOutstanding = Math.round(2e7 + random() ** 3 * 8e9); + const avgVolume = Math.round(sharesOutstanding * (0.002 + random() * 0.02)); + + companies.push({ + symbol, + name, + sector: sector.name, + exchange: pick(random, EXCHANGES), + previousClose, + price, + change: Math.round((price - previousClose) * 100) / 100, + changePercent: ((price - previousClose) / previousClose) * 100, + volume: Math.round(avgVolume * (0.3 + random() * 2.5)), + avgVolume, + marketCap: Math.round(price * sharesOutstanding), + peRatio: random() < 0.15 ? null : Math.round((4 + random() * 70) * 100) / 100, + fiftyTwoWeekLow, + fiftyTwoWeekHigh, + fiftyTwoWeekChangePercent: ((price - yearSeries[0]) / yearSeries[0]) * 100, + earningsDate: NOW + Math.floor(random() * 90) * 86400000, + interest: random() * (1 + Math.abs(price - previousClose) / previousClose) * 100, + series, + }); + } + return companies; +} + +const INDEX_NAMES = [ + ["^LC500", "Large Cap 500", 6120], + ["^IND30", "Industrials 30", 44210], + ["^TECH", "Tech Composite", 19870], + ["^SC2K", "Small Cap 2000", 2410], + ["^VOLX", "Volatility Index", 16.4], + ["CL=F", "Crude Oil", 71.2], + ["GC=F", "Gold", 2650], + ["^TNY", "10-Yr Bond", 4.31], + ["EURUSD=X", "EUR/USD", 1.083], + ["BTC-USD", "Bitcoin USD", 91250], + ["^EU600", "Europe 600", 512.4], + ["^AS225", "Asia 225", 38950], +]; + +function buildIndices() { + const random = createRandom(0x1dec5); + return INDEX_NAMES.map(([symbol, name, level]) => { + const series = randomWalk(random, level, POINTS_PER_SESSION, 0.004, (random() - 0.5) * 0.0006); + const price = series[series.length - 1]; + return { + symbol, + name, + previousClose: level, + price, + change: price - level, + changePercent: ((price - level) / level) * 100, + series, + }; + }); +} + +const SOURCES = ["Market Desk", "Wire Service", "Daily Ledger", "Capital Brief", "Street Notes", "The Closing Bell", "Quarterly Review"]; +const HEADLINE_TEMPLATES = [ + "{company} shares jump after {event}", + "{company} slides as {event} weighs on outlook", + "Why {company} could be the next big {theme} play", + "{company} beats estimates, raises full-year guidance", + "Analysts split on {company} after {event}", + "{sector} stocks rally as investors bet on {theme}", + "{company} to cut 4% of workforce amid {theme} push", + "Is {company} stock a buy after {event}?", + "{company} and {other} announce {theme} partnership", + "Stocks drift as traders weigh {event}", +]; +const EVENTS = ["a strong earnings report", "a surprise guidance cut", "a new buyback program", "an analyst upgrade", "rate-cut hopes", "a product recall", "record quarterly revenue", "a CEO shake-up", "an antitrust probe", "the latest jobs data"]; +const THEMES = ["AI", "clean energy", "cloud", "streaming", "biotech", "dividend", "infrastructure", "chip", "consumer"]; +const SUMMARY_SENTENCES = [ + "The move extends a run that has seen the stock outperform the broader market this quarter.", + "Management said demand remained resilient despite a softer macro backdrop.", + "Investors are watching margins closely after two quarters of rising costs.", + "Several brokerages adjusted their price targets following the announcement.", + "Trading volume was well above the three-month average.", + "The company reiterated its long-term targets during the call with analysts.", + "Options activity pointed to elevated expectations for further volatility.", + "Peers in the same industry moved in sympathy during the session.", +]; + +function buildStories(companies) { + const random = createRandom(0x2ea75); + const stories = []; + for (let i = 0; i < 48; i++) { + const company = pick(random, companies); + const other = pick(random, companies); + const title = pick(random, HEADLINE_TEMPLATES) + .replace("{company}", company.name.split(" ").slice(0, 2).join(" ")) + .replace("{other}", other.name.split(" ")[0]) + .replace("{event}", pick(random, EVENTS)) + .replace("{theme}", pick(random, THEMES)) + .replace("{sector}", company.sector); + const tickerCount = 1 + Math.floor(random() * 3); + const tickers = [company.symbol]; + while (tickers.length < tickerCount) + tickers.push(pick(random, companies).symbol); + const sentenceCount = 2 + Math.floor(random() * 2); + const summary = []; + while (summary.length < sentenceCount) { + const sentence = pick(random, SUMMARY_SENTENCES); + if (!summary.includes(sentence)) + summary.push(sentence); + } + stories.push({ + id: `story-${i}`, + title, + summary: summary.join(" "), + source: pick(random, SOURCES), + publishedAt: NOW - Math.floor(5 + random() * 60 * 36) * 60000, + tickers: [...new Set(tickers)], + hue: Math.floor(random() * 360), + premium: random() < 0.12, + video: random() < 0.15, + }); + } + stories.sort((a, b) => b.publishedAt - a.publishedAt); + return stories; +} + +export const companies = buildCompanies(); +export const indices = buildIndices(); +export const stories = buildStories(companies); + +const SCREENER_SIZE = 150; + +export const SCREENERS = [ + { id: "trending", label: "Trending", rank: (q) => -q.interest }, + { id: "most-active", label: "Most Active", rank: (q) => -q.volume }, + { id: "gainers", label: "Top Gainers", rank: (q) => -q.changePercent }, + { id: "losers", label: "Top Losers", rank: (q) => q.changePercent }, + { id: "52-week-gainers", label: "52 Week Gainers", rank: (q) => -q.fiftyTwoWeekChangePercent }, + { id: "large-cap", label: "Large Cap", rank: (q) => -q.marketCap }, +]; + +export function screenerResults(id) { + const screener = SCREENERS.find((s) => s.id === id); + return [...companies].sort((a, b) => screener.rank(a) - screener.rank(b)).slice(0, SCREENER_SIZE); +} diff --git a/suites-experimental/finance/src/lib/data/random.js b/suites-experimental/finance/src/lib/data/random.js new file mode 100644 index 000000000..cd92cc846 --- /dev/null +++ b/suites-experimental/finance/src/lib/data/random.js @@ -0,0 +1,15 @@ +// Seeded mulberry32, so every run renders identical data. +export function createRandom(seed) { + let state = seed >>> 0; + return () => { + state = (state + 0x6d2b79f5) >>> 0; + let t = state; + t = Math.imul(t ^ (t >>> 15), t | 1); + t ^= t + Math.imul(t ^ (t >>> 7), t | 61); + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + }; +} + +export function pick(random, list) { + return list[Math.floor(random() * list.length)]; +} diff --git a/suites-experimental/finance/src/lib/format.js b/suites-experimental/finance/src/lib/format.js new file mode 100644 index 000000000..5c2912d2e --- /dev/null +++ b/suites-experimental/finance/src/lib/format.js @@ -0,0 +1,53 @@ +const price = new Intl.NumberFormat("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); +const signed = new Intl.NumberFormat("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2, signDisplay: "exceptZero" }); +const percent = new Intl.NumberFormat("en-US", { style: "percent", minimumFractionDigits: 2, maximumFractionDigits: 2, signDisplay: "exceptZero" }); +const compact = new Intl.NumberFormat("en-US", { notation: "compact", minimumFractionDigits: 2, maximumFractionDigits: 2 }); +const date = new Intl.DateTimeFormat("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" }); +const time = new Intl.DateTimeFormat("en-US", { hour: "numeric", minute: "2-digit", timeZone: "America/New_York" }); +const relative = new Intl.RelativeTimeFormat("en-US", { numeric: "auto", style: "narrow" }); + +export function formatPrice(value) { + return price.format(value); +} + +export function formatChange(value) { + return signed.format(value); +} + +export function formatPercent(value) { + return percent.format(value / 100); +} + +export function formatCompact(value) { + return value === null ? "--" : compact.format(value); +} + +export function formatRatio(value) { + return value === null ? "--" : price.format(value); +} + +export function formatDate(value) { + return date.format(value); +} + +export function formatTime(value) { + return time.format(value); +} + +export function formatRelative(timestamp, now) { + const minutes = Math.round((timestamp - now) / 60000); + if (minutes > -60) + return relative.format(minutes, "minute"); + const hours = Math.round(minutes / 60); + if (hours > -24) + return relative.format(hours, "hour"); + return relative.format(Math.round(hours / 24), "day"); +} + +export function trend(value) { + if (value > 0) + return "up"; + if (value < 0) + return "down"; + return "flat"; +} diff --git a/suites-experimental/finance/src/lib/quotes.svelte.js b/suites-experimental/finance/src/lib/quotes.svelte.js new file mode 100644 index 000000000..090e74cfc --- /dev/null +++ b/suites-experimental/finance/src/lib/quotes.svelte.js @@ -0,0 +1,97 @@ +/* global $state */ +import { tick } from "svelte"; +import { nextTask } from "./data/api.js"; +import { createRandom } from "./data/random.js"; + +export class LiveQuote { + price = $state(0); + change = $state(0); + changePercent = $state(0); + volume = $state(0); + series = $state.raw([]); + // Bumped on every streamed update, so price cells can flash. + revision = $state(0); + direction = $state(0); + + constructor(quote) { + this.symbol = quote.symbol; + this.name = quote.name; + this.previousClose = quote.previousClose; + this.price = quote.price; + this.change = quote.change; + this.changePercent = quote.changePercent; + this.volume = quote.volume ?? 0; + this.series = quote.series; + } + + apply(price, volume) { + this.direction = Math.sign(price - this.price); + this.price = price; + this.change = price - this.previousClose; + this.changePercent = (this.change / this.previousClose) * 100; + this.volume += volume; + const series = this.series.slice(); + series[series.length - 1] = price; + this.series = series; + this.revision++; + } +} + +const quotes = new Map(); + +// A response only seeds a symbol's live quote: once the stream has updated a +// symbol, its streamed values win over the (older) response. +export function liveQuote(quote) { + let live = quotes.get(quote.symbol); + if (!live) { + live = new LiveQuote(quote); + quotes.set(quote.symbol, live); + } + return live; +} + +// Components subscribe to the symbols they show, the way a streaming client +// tells its socket which tickers to send. +const subscriptions = new Map(); + +export function subscribe(symbol) { + subscriptions.set(symbol, (subscriptions.get(symbol) ?? 0) + 1); + return () => { + const count = subscriptions.get(symbol) - 1; + if (count) + subscriptions.set(symbol, count); + else + subscriptions.delete(symbol); + }; +} + +export const stream = $state({ live: false, messages: 0 }); + +const STREAM_MESSAGES = 20; +const UPDATES_PER_MESSAGE = 24; +const random = createRandom(0x57ea); + +function applyMessage() { + const symbols = [...subscriptions.keys()]; + const updates = Math.min(symbols.length, UPDATES_PER_MESSAGE); + for (let i = 0; i < updates; i++) { + const quote = quotes.get(symbols[Math.floor(random() * symbols.length)]); + const price = Math.round(quote.price * (1 + (random() - 0.5) * 0.006) * 100) / 100; + quote.apply(price, Math.floor(random() * 5000)); + } + stream.messages++; +} + +// Replays a fixed burst of quote messages, one per task, then reports back. +export async function startStream() { + if (stream.live) + return; + stream.live = true; + for (let i = 0; i < STREAM_MESSAGES; i++) { + await nextTask(); + applyMessage(); + } + stream.live = false; + await tick(); + window.dispatchEvent(new Event("finance-stream-complete")); +} diff --git a/suites-experimental/finance/src/routes/+layout.js b/suites-experimental/finance/src/routes/+layout.js new file mode 100644 index 000000000..09d6351c5 --- /dev/null +++ b/suites-experimental/finance/src/routes/+layout.js @@ -0,0 +1,7 @@ +import { fetchJSON } from "#lib/data/api.js"; +import { liveQuote } from "#lib/quotes.svelte.js"; + +export async function load() { + const summary = await fetchJSON("/v1/market-summary"); + return { indices: summary.quotes.map(liveQuote) }; +} diff --git a/suites-experimental/finance/src/routes/+layout.svelte b/suites-experimental/finance/src/routes/+layout.svelte new file mode 100644 index 000000000..010caca4b --- /dev/null +++ b/suites-experimental/finance/src/routes/+layout.svelte @@ -0,0 +1,36 @@ + + +
+
+ +
+
+ {@render children()} +
+