diff --git a/.env.d.ts b/.env.d.ts new file mode 100644 index 0000000..151dc49 --- /dev/null +++ b/.env.d.ts @@ -0,0 +1,7 @@ +/// + +declare namespace App { + interface Locals { + canAddInventory: boolean; + } +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..971ac8e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,58 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Pokemon TCG card database and inventory management app. Users search cards, track market prices, and manage their collections. Closed beta. + +## Commands + +```bash +npm run dev # Start dev server (binds 0.0.0.0:4321) +npm run build # Production build → ./dist/ +npm run preview # Preview production build +``` + +No test framework or linter is configured. + +Utility scripts in `scripts/` are run directly with `tsx` (e.g., `npx tsx scripts/reindex.ts`). + +## Tech Stack + +- **Astro 5** (SSR mode, Node.js standalone adapter) +- **PostgreSQL** via Drizzle ORM (schema in `pokemon` namespace, snake_case DB columns) +- **Typesense** for full-text card search +- **Clerk** for authentication +- **HTMX** for dynamic interactions (no SPA framework) +- **Bootstrap 5** with custom SCSS overrides, dark theme +- **Chart.js** for price history charts + +## Architecture + +### Data Flow + +TCGPlayer source data → `tcg_cards` → denormalized `cards` (per variant) → `skus` (per condition/language) → `price_history` / `sales_history`. User collections stored in `inventory` table linked to Clerk userId. + +PostgreSQL is source of truth. Typesense mirrors card/sku/inventory data for search. Both must be kept in sync — see `src/pages/api/inventory.ts` for the sync pattern (write to PG, then upsert/delete in Typesense). + +### Key Directories + +- `src/pages/` — Astro routes and API endpoints +- `src/pages/partials/` — HTMX partial responses (HTML fragments returned to `hx-post` targets) +- `src/pages/api/` — JSON/file API endpoints (`upload.ts` for CSV, `inventory.ts` for CRUD) +- `src/components/` — Reusable `.astro` components +- `src/db/` — Drizzle schema (`schema.ts`), relations (`relations.ts`), DB connection (`index.ts`), Typesense client (`typesense.ts`) +- `scripts/` — Data ingestion and indexing utilities (not part of the app runtime) + +### Authentication + +Clerk middleware in `src/middleware.ts` protects routes via `createRouteMatcher`. Auth context accessed via `Astro.locals.auth()` in pages/API routes. + +### Database Schema + +Drizzle config uses `casing: 'snake_case'` — define schema fields in camelCase, they map to snake_case columns automatically. Migrations live in `./drizzle/`. Schema is scoped to the `pokemon` PostgreSQL schema. + +### Frontend Patterns + +Pages use HTMX for interactivity — forms POST to `/partials/*` endpoints that return HTML fragments. No client-side routing. View Transitions API enabled for page navigation animations. Card modals and inventory forms are HTMX-driven with `hx-post`, `hx-target`, and `hx-swap` attributes. diff --git a/astro.config.mjs b/astro.config.mjs index 96573d2..0fed92a 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -18,5 +18,17 @@ export default defineConfig({ output: "server", security: { checkOrigin: false - } + }, + vite: { + css: { + preprocessorOptions: { + scss: { + // Silences deprecation warnings from dependencies + quietDeps: true, + // Specifically silence color function warnings + silenceDeprecations: ['color-functions', 'import','global-builtin'], + }, + }, + }, + }, }); diff --git a/package-lock.json b/package-lock.json index b893144..6659f94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@clerk/astro": "^3.0.1", "@clerk/shared": "^4.0.0", "@clerk/themes": "^2.4.55", + "@popperjs/core": "^2.11.8", "astro": "^5.17.1", "bootstrap": "^5.3.8", "chalk": "^5.6.2", @@ -117,9 +118,9 @@ } }, "node_modules/@azure-rest/core-client": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.5.1.tgz", - "integrity": "sha512-EHaOXW0RYDKS5CFffnixdyRPak5ytiCtU7uXDcP/uiY+A6jFRwNGzzJBiznkCzvi5EYpY+YWinieqHb0oY916A==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.6.0.tgz", + "integrity": "sha512-iuFKDm8XPzNxPfRjhyU5/xKZmcRDzSuEghXDHHk4MjBV/wFL34GmYVBZnn9wmuoLBeS1qAw9ceMdaeJBPcB1QQ==", "license": "MIT", "peer": true, "dependencies": { @@ -182,9 +183,9 @@ } }, "node_modules/@azure/core-http-compat": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.2.tgz", - "integrity": "sha512-Tf6ltdKzOJEgxZeWLCjMxrxbodB/ZeCbzzA1A2qHbhzAjzjHoBVSUeSl/baT/oHAxhc4qdqVaDKnc2+iE932gw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.4.0.tgz", + "integrity": "sha512-f1P96IB399YiN2ARYHP7EpZi3Bf3wH4SN2lGzrw7JVwm7bbsVYtf2iKSBwTywD2P62NOPZGHFSZi+6jjb75JuA==", "license": "MIT", "peer": true, "dependencies": { @@ -275,9 +276,9 @@ } }, "node_modules/@azure/identity": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.0.tgz", - "integrity": "sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.1.tgz", + "integrity": "sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==", "license": "MIT", "peer": true, "dependencies": { @@ -288,8 +289,8 @@ "@azure/core-tracing": "^1.0.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^4.2.0", - "@azure/msal-node": "^3.5.0", + "@azure/msal-browser": "^5.5.0", + "@azure/msal-node": "^5.1.0", "open": "^10.1.0", "tslib": "^2.2.0" }, @@ -356,22 +357,22 @@ } }, "node_modules/@azure/msal-browser": { - "version": "4.29.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.29.0.tgz", - "integrity": "sha512-/f3eHkSNUTl6DLQHm+bKecjBKcRQxbd/XLx8lvSYp8Nl/HRyPuIPOijt9Dt0sH50/SxOwQ62RnFCmFlGK+bR/w==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.6.3.tgz", + "integrity": "sha512-sTjMtUm+bJpENU/1WlRzHEsgEHppZDZ1EtNyaOODg/sQBtMxxJzGB+MOCM+T2Q5Qe1fKBrdxUmjyRxm0r7Ez9w==", "license": "MIT", "peer": true, "dependencies": { - "@azure/msal-common": "15.15.0" + "@azure/msal-common": "16.4.1" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.15.0.tgz", - "integrity": "sha512-/n+bN0AKlVa+AOcETkJSKj38+bvFs78BaP4rNtv3MJCmPH0YrHiskMRe74OhyZ5DZjGISlFyxqvf9/4QVEi2tw==", + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.4.1.tgz", + "integrity": "sha512-Bl8f+w37xkXsYh7QRkAKCFGYtWMYuOVO7Lv+BxILrvGz3HbIEF22Pt0ugyj0QPOl6NLrHcnNUQ9yeew98P/5iw==", "license": "MIT", "peer": true, "engines": { @@ -379,18 +380,18 @@ } }, "node_modules/@azure/msal-node": { - "version": "3.8.8", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.8.8.tgz", - "integrity": "sha512-+f1VrJH1iI517t4zgmuhqORja0bL6LDQXfBqkjuMmfTYXTQQnh1EvwwxO3UbKLT05N0obF72SRHFrC1RBDv5Gg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.1.2.tgz", + "integrity": "sha512-DoeSJ9U5KPAIZoHsPywvfEj2MhBniQe0+FSpjLUTdWoIkI999GB5USkW6nNEHnIaLVxROHXvprWA1KzdS1VQ4A==", "license": "MIT", "peer": true, "dependencies": { - "@azure/msal-common": "15.15.0", + "@azure/msal-common": "16.4.1", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, "engines": { - "node": ">=16" + "node": ">=20" } }, "node_modules/@babel/helper-string-parser": { @@ -412,9 +413,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" @@ -427,9 +428,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", - "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", "peer": true, "engines": { @@ -462,13 +463,13 @@ } }, "node_modules/@clerk/astro": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@clerk/astro/-/astro-3.0.4.tgz", - "integrity": "sha512-UVp/Xu607b2QD7AnG3aUZZIVrPFC5nUP9PHEmjle65PArPouB3BSjP4NE1Fo4CfzedKTJotNOq3Qwzc5qNVSxg==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@clerk/astro/-/astro-3.0.12.tgz", + "integrity": "sha512-Vwtek7WiGWuQmsqgPF9klePF9pZoQa7jBgI9+K1YTBcAecuLjXyeFHDqtQwfdHomX9NpJIB06dXyVBWX/q3dKw==", "license": "MIT", "dependencies": { - "@clerk/backend": "^3.2.0", - "@clerk/shared": "^4.3.0", + "@clerk/backend": "^3.2.8", + "@clerk/shared": "^4.6.0", "nanoid": "5.1.6", "nanostores": "1.0.1" }, @@ -480,12 +481,12 @@ } }, "node_modules/@clerk/backend": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-3.2.0.tgz", - "integrity": "sha512-3APNJ5jt5Db9f441FPVTjgzvPxjLhekygomkMOhsvPpItRNNEHgFZM/bGXYetgcOtUrO6vWcuhf1rlNSTRelhg==", + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-3.2.8.tgz", + "integrity": "sha512-N9yqCQCdkn/4XpfiVnaoS6wXDeC2yQf85ioHGolOIOCWXOPwMd63fONUfRhwOZSlXGTAsgyUNZu87Ps0tcVYsg==", "license": "MIT", "dependencies": { - "@clerk/shared": "^4.3.0", + "@clerk/shared": "^4.6.0", "standardwebhooks": "^1.0.0", "tslib": "2.8.1" }, @@ -494,9 +495,9 @@ } }, "node_modules/@clerk/shared": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-4.3.0.tgz", - "integrity": "sha512-Ydt8YGohNXEs6aBBLnti80OJT551yYWVTugFTO8Ee+uIZpStyqXF+ufBtJleuhfxs1D5KjtpIxc+hTqkZtqMyQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-4.6.0.tgz", + "integrity": "sha512-dtbsO/+xK5e+qWuOAY1ekZ8BJxsB0F0LYDpXWjRON7JSoFKSaqqaH5cwBvdjbbWaehb6jz1YUQmWVV8gBOx6Ag==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -536,9 +537,9 @@ } }, "node_modules/@clerk/themes/node_modules/@clerk/shared": { - "version": "3.47.2", - "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.47.2.tgz", - "integrity": "sha512-dwUT27DKq3Gr9vn9lAfc/LSe79P1rKIib8/mTWA7ZEzY7XX2Yq5UnDMCMznYrI8oVLdJrCT4ypFXRgnH306Oew==", + "version": "3.47.3", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.47.3.tgz", + "integrity": "sha512-jG0wMIZuuc8zaKieg9Os8ocTphG+llluRukUUdyVnu4+ZI1syVf+dkpDP3ZK69yLavTX3D0KAmkmQqTPzQV/Nw==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -573,9 +574,9 @@ "license": "Apache-2.0" }, "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", "license": "MIT", "optional": true, "dependencies": { @@ -583,9 +584,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", "cpu": [ "ppc64" ], @@ -599,9 +600,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", "cpu": [ "arm" ], @@ -615,9 +616,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", "cpu": [ "arm64" ], @@ -631,9 +632,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", "cpu": [ "x64" ], @@ -647,9 +648,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", "cpu": [ "arm64" ], @@ -663,9 +664,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", "cpu": [ "x64" ], @@ -679,9 +680,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", "cpu": [ "arm64" ], @@ -695,9 +696,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", "cpu": [ "x64" ], @@ -711,9 +712,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", "cpu": [ "arm" ], @@ -727,9 +728,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", "cpu": [ "arm64" ], @@ -743,9 +744,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", "cpu": [ "ia32" ], @@ -759,9 +760,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", "cpu": [ "loong64" ], @@ -775,9 +776,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", "cpu": [ "mips64el" ], @@ -791,9 +792,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", "cpu": [ "ppc64" ], @@ -807,9 +808,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", "cpu": [ "riscv64" ], @@ -823,9 +824,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", "cpu": [ "s390x" ], @@ -839,9 +840,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", "cpu": [ "x64" ], @@ -855,9 +856,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", "cpu": [ "arm64" ], @@ -871,9 +872,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", "cpu": [ "x64" ], @@ -887,9 +888,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", "cpu": [ "arm64" ], @@ -903,9 +904,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", "cpu": [ "x64" ], @@ -919,9 +920,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", "cpu": [ "arm64" ], @@ -935,9 +936,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", "cpu": [ "x64" ], @@ -951,9 +952,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", "cpu": [ "arm64" ], @@ -967,9 +968,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", "cpu": [ "ia32" ], @@ -983,9 +984,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", "cpu": [ "x64" ], @@ -1837,9 +1838,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", - "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", "cpu": [ "arm" ], @@ -1850,9 +1851,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", - "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", "cpu": [ "arm64" ], @@ -1863,9 +1864,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", - "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", "cpu": [ "arm64" ], @@ -1876,9 +1877,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", - "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", "cpu": [ "x64" ], @@ -1889,9 +1890,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", - "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", "cpu": [ "arm64" ], @@ -1902,9 +1903,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", - "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", "cpu": [ "x64" ], @@ -1915,9 +1916,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", - "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", "cpu": [ "arm" ], @@ -1928,9 +1929,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", - "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", "cpu": [ "arm" ], @@ -1941,9 +1942,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", - "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", "cpu": [ "arm64" ], @@ -1954,9 +1955,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", - "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", "cpu": [ "arm64" ], @@ -1967,9 +1968,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", - "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", "cpu": [ "loong64" ], @@ -1980,9 +1981,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", - "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", "cpu": [ "loong64" ], @@ -1993,9 +1994,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", - "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", "cpu": [ "ppc64" ], @@ -2006,9 +2007,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", - "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", "cpu": [ "ppc64" ], @@ -2019,9 +2020,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", - "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", "cpu": [ "riscv64" ], @@ -2032,9 +2033,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", - "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", "cpu": [ "riscv64" ], @@ -2045,9 +2046,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", - "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", "cpu": [ "s390x" ], @@ -2058,9 +2059,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", "cpu": [ "x64" ], @@ -2071,9 +2072,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", - "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", "cpu": [ "x64" ], @@ -2084,9 +2085,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", - "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", "cpu": [ "x64" ], @@ -2097,9 +2098,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", - "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", "cpu": [ "arm64" ], @@ -2110,9 +2111,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", - "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", "cpu": [ "arm64" ], @@ -2123,9 +2124,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", - "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", "cpu": [ "ia32" ], @@ -2136,9 +2137,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", - "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", "cpu": [ "x64" ], @@ -2149,9 +2150,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", - "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", "cpu": [ "x64" ], @@ -2262,9 +2263,9 @@ } }, "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", "license": "MIT", "dependencies": { "@types/ms": "*" @@ -2301,9 +2302,9 @@ "license": "MIT" }, "node_modules/@types/mssql": { - "version": "9.1.9", - "resolved": "https://registry.npmjs.org/@types/mssql/-/mssql-9.1.9.tgz", - "integrity": "sha512-P0nCgw6vzY23UxZMnbI4N7fnLGANt4LI4yvxze1paPj+LuN28cFv5EI+QidP8udnId/BKhkcRhm/BleNsjK65A==", + "version": "9.1.11", + "resolved": "https://registry.npmjs.org/@types/mssql/-/mssql-9.1.11.tgz", + "integrity": "sha512-vcujgrDbDezCxNDO4KY6gjwduLYOKfrexpRUwhoysRvcXZ3+IgZ/PMYFDgh8c3cQIxZ6skAwYo+H6ibMrBWPjQ==", "license": "MIT", "peer": true, "dependencies": { @@ -2322,18 +2323,18 @@ } }, "node_modules/@types/node": { - "version": "25.4.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.4.0.tgz", - "integrity": "sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw==", + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", "license": "MIT", "dependencies": { "undici-types": "~7.18.0" } }, "node_modules/@types/pg": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.18.0.tgz", - "integrity": "sha512-gT+oueVQkqnj6ajGJXblFR4iavIXWsGAFCk3dP4Kki5+a9R4NMt0JARdk6s8cUKcfUoqP5dAtDSLU8xYUTFV+Q==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2359,9 +2360,9 @@ "license": "MIT" }, "node_modules/@typespec/ts-http-runtime": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.4.tgz", - "integrity": "sha512-CI0NhTrz4EBaa0U+HaaUZrJhPoso8sG7ZFya8uQoBA57fjzrjRSv87ekCjLZOFExN+gXE/z0xuN2QfH4H2HrLQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.5.tgz", + "integrity": "sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw==", "license": "MIT", "peer": true, "dependencies": { @@ -2502,9 +2503,9 @@ } }, "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -2631,9 +2632,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", - "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", @@ -2986,9 +2987,9 @@ } }, "node_modules/cookie-es": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", - "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", "license": "MIT" }, "node_modules/crossws": { @@ -3093,36 +3094,36 @@ "license": "MIT" }, "node_modules/csv": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/csv/-/csv-6.4.1.tgz", - "integrity": "sha512-ajGosmTGnTwYyGl8STqZDu7R6LkDf3xL39XiOmliV/GufQeVUxHzTKIm4NOBCwmEuujK7B6isxs4Uqt9GcRCvA==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/csv/-/csv-6.5.1.tgz", + "integrity": "sha512-oZwQSayvtn3nGrLUl8epnbfSCsvhOmCKeMHEOVmZ/3YCTG+x26nhgrP1vB3Kjs4lUJF6jcsjjIEn2gYbbq5+Gw==", "license": "MIT", "dependencies": { - "csv-generate": "^4.5.0", - "csv-parse": "^6.1.0", - "csv-stringify": "^6.6.0", - "stream-transform": "^3.4.0" + "csv-generate": "^4.5.1", + "csv-parse": "^6.2.1", + "csv-stringify": "^6.7.0", + "stream-transform": "^3.4.1" }, "engines": { "node": ">= 0.1.90" } }, "node_modules/csv-generate": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-4.5.0.tgz", - "integrity": "sha512-aQr/vmOKyBSBHNwYhAoXw1+kUsPnMSwmYgpNoo36rIXoG1ecWILnvPGZeQ6oUjzrWknZAD3+jfpqYOBAl4x15A==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-4.5.1.tgz", + "integrity": "sha512-nWVtFBfyK+YX1fHtKkMqzULc6sH5ise6baD64SmYTq3GEDSErVZ5xzyMa0hNP/a9PGJTTTyQTU69xpjcuCx2IA==", "license": "MIT" }, "node_modules/csv-parse": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-6.1.0.tgz", - "integrity": "sha512-CEE+jwpgLn+MmtCpVcPtiCZpVtB6Z2OKPTr34pycYYoL7sxdOkXDdQ4lRiw6ioC0q6BLqhc6cKweCVvral8yhw==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-6.2.1.tgz", + "integrity": "sha512-LRLMV+UCyfMokp8Wb411duBf1gaBKJfOfBWU9eHMJ+b+cJYZsNu3AFmjJf3+yPGd59Exz1TsMjaSFyxnYB9+IQ==", "license": "MIT" }, "node_modules/csv-stringify": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.6.0.tgz", - "integrity": "sha512-YW32lKOmIBgbxtu3g5SaiqWNwa/9ISQt2EcgOq0+RAIFufFp9is6tqNnKahqE5kuKvrnYAzs28r+s6pXJR8Vcw==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.7.0.tgz", + "integrity": "sha512-UdtziYp5HuTz7e5j8Nvq+a/3HQo+2/aJZ9xntNTpmRRIg/3YYqDVgiS9fvAhtNbnyfbv2ZBe0bqCHqzhE7FqWQ==", "license": "MIT" }, "node_modules/debug": { @@ -3199,9 +3200,9 @@ } }, "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", "license": "MIT" }, "node_modules/delayed-stream": { @@ -3260,9 +3261,9 @@ } }, "node_modules/devalue": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.3.tgz", - "integrity": "sha512-nc7XjUU/2Lb+SvEFVGcWLiKkzfw8+qHI7zn8WYXKkLMgfGSHbgCEaR6bJpev8Cm6Rmrb19Gfd/tZvGqx9is3wg==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz", + "integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==", "license": "MIT" }, "node_modules/devlop": { @@ -3279,9 +3280,9 @@ } }, "node_modules/diff": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", - "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -3361,9 +3362,9 @@ } }, "node_modules/dotenv": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", - "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz", + "integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -4133,9 +4134,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -4145,32 +4146,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" } }, "node_modules/escape-html": { @@ -4433,9 +4434,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.6", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", - "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", "devOptional": true, "license": "MIT", "dependencies": { @@ -4470,14 +4471,14 @@ } }, "node_modules/h3": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.6.tgz", - "integrity": "sha512-oi15ESLW5LRthZ+qPCi5GNasY/gvynSKUQxgiovrY63bPAtG59wtM+LSrlcwvOHAXzGrXVLnI97brbkdPF9WoQ==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", "license": "MIT", "dependencies": { - "cookie-es": "^1.2.2", + "cookie-es": "^1.2.3", "crossws": "^0.3.5", - "defu": "^6.1.4", + "defu": "^6.1.6", "destr": "^2.0.5", "iron-webcrypto": "^1.2.1", "node-mock-http": "^1.0.4", @@ -5096,9 +5097,9 @@ } }, "node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.3.tgz", + "integrity": "sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -6171,13 +6172,13 @@ "license": "MIT" }, "node_modules/oniguruma-to-es": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz", - "integrity": "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.5.tgz", + "integrity": "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==", "license": "MIT", "dependencies": { "oniguruma-parser": "^0.12.1", - "regex": "^6.0.1", + "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, @@ -6381,9 +6382,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" @@ -6393,9 +6394,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", + "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", "funding": [ { "type": "opencollective", @@ -6541,9 +6542,9 @@ } }, "node_modules/react": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", - "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", + "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", "peer": true, "engines": { @@ -6825,9 +6826,9 @@ "peer": true }, "node_modules/rollup": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", - "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "version": "4.60.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -6840,31 +6841,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.59.0", - "@rollup/rollup-android-arm64": "4.59.0", - "@rollup/rollup-darwin-arm64": "4.59.0", - "@rollup/rollup-darwin-x64": "4.59.0", - "@rollup/rollup-freebsd-arm64": "4.59.0", - "@rollup/rollup-freebsd-x64": "4.59.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", - "@rollup/rollup-linux-arm-musleabihf": "4.59.0", - "@rollup/rollup-linux-arm64-gnu": "4.59.0", - "@rollup/rollup-linux-arm64-musl": "4.59.0", - "@rollup/rollup-linux-loong64-gnu": "4.59.0", - "@rollup/rollup-linux-loong64-musl": "4.59.0", - "@rollup/rollup-linux-ppc64-gnu": "4.59.0", - "@rollup/rollup-linux-ppc64-musl": "4.59.0", - "@rollup/rollup-linux-riscv64-gnu": "4.59.0", - "@rollup/rollup-linux-riscv64-musl": "4.59.0", - "@rollup/rollup-linux-s390x-gnu": "4.59.0", - "@rollup/rollup-linux-x64-gnu": "4.59.0", - "@rollup/rollup-linux-x64-musl": "4.59.0", - "@rollup/rollup-openbsd-x64": "4.59.0", - "@rollup/rollup-openharmony-arm64": "4.59.0", - "@rollup/rollup-win32-arm64-msvc": "4.59.0", - "@rollup/rollup-win32-ia32-msvc": "4.59.0", - "@rollup/rollup-win32-x64-gnu": "4.59.0", - "@rollup/rollup-win32-x64-msvc": "4.59.0", + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" } }, @@ -6910,9 +6911,9 @@ "peer": true }, "node_modules/sass": { - "version": "1.98.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.98.0.tgz", - "integrity": "sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A==", + "version": "1.99.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.99.0.tgz", + "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==", "license": "MIT", "dependencies": { "chokidar": "^4.0.0", @@ -6930,9 +6931,9 @@ } }, "node_modules/sax": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz", - "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", "license": "BlueOak-1.0.0", "engines": { "node": ">=11.0.0" @@ -7056,9 +7057,9 @@ "license": "MIT" }, "node_modules/smol-toml": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", - "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz", + "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==", "license": "BSD-3-Clause", "engines": { "node": ">= 18" @@ -7128,9 +7129,9 @@ "license": "MIT" }, "node_modules/stream-transform": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-3.4.0.tgz", - "integrity": "sha512-QO3OGhKyeIV8p6eRQdG+W6WounFw519zk690hHCNfhgfP9bylVS+NTXsuBc7n+RsGn31UgFPGrWYIgoAbArKEw==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-3.4.1.tgz", + "integrity": "sha512-4P7GsGr95QaRsN09ws40kVdaR6VAeeJtprKVQPOnRp9rOuuW8kzONeYbhikCrbg2RSsHqfIJOnAf+RMNZcKOFQ==", "license": "MIT" }, "node_modules/string_decoder": { @@ -7266,22 +7267,22 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", + "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -7391,12 +7392,12 @@ } }, "node_modules/typesense": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/typesense/-/typesense-3.0.2.tgz", - "integrity": "sha512-MNspMeIdhcyH0lDVwJX2rVBB3DSXbE1t3k318JC1e1AL1/sRyovi44Aze7n9JzRxnAoFnlhkIB3kWgWGIEozbQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/typesense/-/typesense-3.0.5.tgz", + "integrity": "sha512-Pw/yWosbqEOFMM/wQDsnS8FA6r3Qp5ilxuqZTMBoUc95SGCEBflMd39kvDEZZFoTORzNDxCLiiQ+LfYJTl1ulQ==", "license": "Apache-2.0", "dependencies": { - "axios": "^1.13.5", + "axios": "1.13.5", "loglevel": "^1.9.2", "tslib": "^2.8.1" }, @@ -7585,16 +7586,16 @@ } }, "node_modules/unstorage": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.4.tgz", - "integrity": "sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw==", + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", "license": "MIT", "dependencies": { "anymatch": "^3.1.3", "chokidar": "^5.0.0", "destr": "^2.0.5", - "h3": "^1.15.5", - "lru-cache": "^11.2.0", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.3" @@ -7770,9 +7771,9 @@ } }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -8301,9 +8302,9 @@ } }, "node_modules/vitefu": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.2.tgz", - "integrity": "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", "license": "MIT", "workspaces": [ "tests/deps/*", @@ -8311,7 +8312,7 @@ "tests/projects/workspace/packages/*" ], "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "vite": { @@ -8459,12 +8460,12 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", - "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", "license": "ISC", "peerDependencies": { - "zod": "^3.25 || ^4" + "zod": "^3.25.28 || ^4" } }, "node_modules/zod-to-ts": { diff --git a/package.json b/package.json index 4178539..4661872 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@clerk/astro": "^3.0.1", "@clerk/shared": "^4.0.0", "@clerk/themes": "^2.4.55", + "@popperjs/core": "^2.11.8", "astro": "^5.17.1", "bootstrap": "^5.3.8", "chalk": "^5.6.2", diff --git a/scripts/diagnose-join.ts b/scripts/diagnose-join.ts new file mode 100644 index 0000000..446a92e --- /dev/null +++ b/scripts/diagnose-join.ts @@ -0,0 +1,33 @@ +import 'dotenv/config'; +import chalk from 'chalk'; +import util from 'node:util'; +import { client } from '../src/db/typesense.ts'; + +const variants = [ + '$skus(*, $cards(*))', + '$skus(*,$cards(*))', + '$skus(*, card_id, $cards(*))', + '$skus(*, $cards(*, strategy:nest))', + '$skus(*, $cards(*, strategy:merge))', +]; + +const debug = await client.debug.retrieve(); +console.log(chalk.cyan(`Typesense server version: ${debug.version}`)); +console.log(); + +for (const include of variants) { + console.log(chalk.yellow(`include_fields: ${include}`)); + try { + const res: any = await client.collections('inventories').documents().search({ + q: '*', + query_by: 'content', + per_page: 1, + include_fields: include, + }); + const doc = res.hits?.[0]?.document; + console.log(util.inspect(doc, { depth: null, colors: false })); + } catch (err: any) { + console.log(chalk.red(` ERROR: ${err.message ?? err}`)); + } + console.log(); +} diff --git a/scripts/pokemon-helper.ts b/scripts/pokemon-helper.ts index f2c94f6..1943f38 100644 --- a/scripts/pokemon-helper.ts +++ b/scripts/pokemon-helper.ts @@ -4,6 +4,8 @@ import type { DBInstance } from '../src/db/index.ts'; import fs from "node:fs/promises"; import { sql } from 'drizzle-orm' +import * as util from 'util'; + const DollarToInt = (dollar: any) => { if (dollar === null) return null; @@ -63,7 +65,7 @@ export const createCardCollection = async () => { { name: 'releaseDate', type: 'int32' }, { name: 'marketPrice', type: 'int32', optional: true, sort: true }, { name: 'content', type: 'string', token_separators: ['/'] }, - { name: 'sku_id', type: 'string[]', optional: true, reference: 'skus.id', async_reference: true } + // { name: 'sku_id', type: 'string[]', optional: true, reference: 'skus.id', async_reference: true } ], }); console.log(chalk.green('Collection "cards" created successfully.')); @@ -84,11 +86,40 @@ export const createSkuCollection = async () => { { name: 'highestPrice', type: 'int32', optional: true }, { name: 'lowestPrice', type: 'int32', optional: true }, { name: 'marketPrice', type: 'int32', optional: true }, + { name: 'card_id', type: 'string', reference: 'cards.id', async_reference: true }, ] }); console.log(chalk.green('Collection "skus" created successfully.')); } +// Delete and recreate the 'inventory' index +export const createInventoryCollection = async () => { + try { + await client.collections('inventories').delete(); + } catch (error) { + // Ignore error, just means collection doesn't exist + } + await client.collections().create({ + name: 'inventories', + fields: [ + { name: 'id', type: 'string' }, + { name: 'userId', type: 'string' }, + { name: 'catalogName', type: 'string' }, + { name: 'card_id', type: 'string', reference: 'cards.id', async_reference: true }, + { name: 'sku_id', type: 'string', reference: 'skus.id', async_reference: true }, + { name: 'purchasePrice', type: 'int32', optional: true }, + // content,setName,productLineName,rarityName,energyType,cardType from cards for searching + { name: 'content', type: 'string', token_separators: ['/'] }, + { name: 'setName', type: 'string' }, + { name: 'productLineName', type: 'string' }, + { name: 'rarityName', type: 'string' }, + { name: 'energyType', type: 'string' }, + { name: 'cardType', type: 'string' }, + ] + }); + console.log(chalk.green('Collection "inventories" created successfully.')); +} + export const upsertCardCollection = async (db:DBInstance) => { const pokemon = await db.query.cards.findMany({ @@ -123,7 +154,7 @@ export const upsertCardCollection = async (db:DBInstance) => { content: [card.productName, card.productLineName, card.set?.setName || "", card.set?.setCode || "", card.number, card.rarityName, card.artist || ""].join(' '), releaseDate: card.tcgdata?.releaseDate ? Math.floor(new Date(card.tcgdata.releaseDate).getTime() / 1000) : 0, ...(marketPrice !== null && { marketPrice }), - sku_id: card.prices.map(price => price.skuId.toString()) + // sku_id: card.prices.map(price => price.skuId.toString()) }; }), { action: 'upsert' }); console.log(chalk.green('Collection "cards" indexed successfully.')); @@ -137,10 +168,39 @@ export const upsertSkuCollection = async (db:DBInstance) => { highestPrice: DollarToInt(sku.highestPrice), lowestPrice: DollarToInt(sku.lowestPrice), marketPrice: DollarToInt(sku.marketPrice), - })), { action: 'upsert' }); + card_id: sku.cardId.toString(), + })), { action: 'upsert' }); console.log(chalk.green('Collection "skus" indexed successfully.')); } +export const upsertInventoryCollection = async (db:DBInstance) => { + const inv = await db.query.inventory.findMany({ + with: { sku: { with: { card: { with: { set: true } } } } } + }); + await client.collections('inventories').documents().import(inv.map(i => ({ + id: i.inventoryId, + userId: i.userId, + catalogName: i.catalogName, + card_id: i.sku?.cardId.toString(), + sku_id: i.skuId.toString(), + purchasePrice: DollarToInt(i.purchasePrice), + productLineName: i.sku?.card?.productLineName, + rarityName: i.sku?.card?.rarityName, + setName: i.sku?.card?.set?.setName || "", + cardType: i.sku?.card?.cardType || "", + energyType: i.sku?.card?.energyType || "", + content: [ + i.sku?.card?.productName, + i.sku?.card?.productLineName, + i.sku?.card?.set?.setName || "", + i.sku?.card?.number, + i.sku?.card?.rarityName, + i.sku?.card?.artist || "" + ].join(' '), + })), { action: 'upsert' }); + console.log(chalk.green('Collection "inventories" indexed successfully.')); +} + @@ -184,4 +244,7 @@ where not exists (select 1 from cards where product_id=t.product_id and variant= `); console.log(`Inserted ${inserts.rowCount} rows into cards table`); + const skuUpdates = await db.execute(sql`update skus s set card_id = c.card_id from cards c where s.product_id = c.product_id and s.variant = c.variant and s.card_id is distinct from c.card_id`); + console.log(`Updated ${skuUpdates.rowCount} rows in skus table`); + } diff --git a/scripts/preload-tcgplayer.ts b/scripts/preload-tcgplayer.ts index 96b4db3..6a5fec4 100644 --- a/scripts/preload-tcgplayer.ts +++ b/scripts/preload-tcgplayer.ts @@ -204,9 +204,9 @@ async function syncProductLine(productLine: string, field: string, fieldValue: s }, }); - console.log(`item: ${item.setId}\tdetail: ${detailData.setId}`); - console.log(`item: ${item.setCode}\tdetail: ${detailData.setCode}`); - console.log(`item: ${item.setName}\tdetail: ${detailData.setName}`); + // console.log(`item: ${item.setId}\tdetail: ${detailData.setId}`); + // console.log(`item: ${item.setCode}\tdetail: ${detailData.setCode}`); + // console.log(`item: ${item.setName}\tdetail: ${detailData.setName}`); // set is... await db.insert(schema.sets).values({ setId: detailData.setId, diff --git a/scripts/reindex.ts b/scripts/reindex.ts index 7a93288..22fc3ab 100644 --- a/scripts/reindex.ts +++ b/scripts/reindex.ts @@ -3,9 +3,12 @@ import { db, ClosePool } from '../src/db/index.ts'; import * as Indexing from './pokemon-helper.ts'; -//await Indexing.createCardCollection(); -//await Indexing.createSkuCollection(); -await Indexing.upsertCardCollection(db); +// await Indexing.createCardCollection(); +await Indexing.createSkuCollection(); +await Indexing.createInventoryCollection(); + +// await Indexing.upsertCardCollection(db); await Indexing.upsertSkuCollection(db); +await Indexing.upsertInventoryCollection(db); await ClosePool(); console.log(chalk.green('Pokémon reindex complete.')); diff --git a/src/assets/css/main.scss b/src/assets/css/main.scss index a7a80f4..ee8ded4 100644 --- a/src/assets/css/main.scss +++ b/src/assets/css/main.scss @@ -32,44 +32,33 @@ $container-max-widths: ( :root { --total: 11; --radius: 40px; + --snow: hsl(13, 60%, 97%); + --lilac: hsl(262, 47%, 63%); + --purple: hsl(262, 47%, 45%); + --mauve: hsl(262, 80%, 82%); + --orchid: hsl(291, 47%, 60%); + --ice: hsl(160, 24%, 85%); + --hero-bg: hsl(258, 30%, 10%); + --edit: hsl(45, 100%, 71%); + --gold: hsl(45, 94%, 21%); + --delete: hsl(354, 71%, 63%); + --dark-red: hsl(354, 61%, 21%); } html { scroll-behavior: smooth; } -/* -------------------------------------------------- - View Transitions --------------------------------------------------- */ -@view-transition { - navigation: auto; -} - -::view-transition-group(card-image) { - animation-duration: 300ms; - animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); -} - -::view-transition-old(card-image), -::view-transition-new(card-image) { - width: 100%; - height: 100%; - object-fit: cover; -} - -/* Optional: fade everything else */ -::view-transition-old(root), -::view-transition-new(root) { - animation-duration: 150ms; -} - /* -------------------------------------------------- Layout -------------------------------------------------- */ +body { + min-height: 100dvh; +} + .wrapper { display: flex; flex-direction: column; - min-height: 100dvh !important; } .main { @@ -81,40 +70,71 @@ html { } /* -------------------------------------------------- - Energy Wheel + Typography -------------------------------------------------- */ -.energy-wheel { - position: relative; - width: calc(var(--radius) * 2); - height: calc(var(--radius) * 2); - margin: 5px 0 10px; +.copy-small { + font-size: 0.75rem; + opacity: 0.87; + + @media (min-width: 768px) { font-size: 0.85rem; } + @media (min-width: 1400px) { font-size: 1rem; } } -.energy-wheel-item { - position: absolute; - width: 25px; - height: 25px; - top: 50%; - left: 50%; - --angle: calc(360deg / var(--total) * var(--i)); - transform: rotate(var(--angle)) translateX(var(--radius)) rotate(calc(-1 * var(--angle))); +.eyebrow { + font-size: 0.8rem; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; +} - &:first-of-type { - z-index: 100; - } +.fs-7 { font-size: 0.9rem !important; } +.py-6 { padding-top: 5rem; padding-bottom: 5rem; } +.hover-white:hover { color: #fff !important; } + +$colors: mauve, lilac, "purple", "orchid", "snow"; +@each $c in $colors { + .text-#{$c} { color: var(--#{$c}); } +} + +.text-gradient { + background: linear-gradient( + 90deg, + var(--mauve), + var(--lilac), + var(--purple), + var(--snow), + var(--orchid), + var(--ice) + ); + background-size: 300% 300%; + -webkit-background-clip: text; + background-clip: text; + color: transparent; + animation: gradientShift 12s ease-in-out infinite; +} + +@keyframes gradientShift { + 0%, 100% { background-position: 0% 50%; } + 50% { background-position: 100% 50%; } } /* -------------------------------------------------- - Navbar & Icons + Navbar -------------------------------------------------- */ .navbar { - background-color: var(--bs-danger) !important; + background-color: rgb(126, 87, 194) !important; + border-bottom: #1d1f21 solid 1px; + z-index: 1030; +} + +.navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 640 640'%3e%3cpath fill='%23FFFAFA' d='M68.1 149.7C68.1 132 82.4 117.7 100.1 117.7C136.4 117.7 172.5 118.9 207.9 120.2C319.4 125.6 429.1 119.8 540.8 119.4C558.5 119.4 572.8 133.7 572.8 151.4C572.8 169.1 558.5 183.4 540.8 183.4C503.3 183.4 465.7 184.2 427.7 185C319 189.4 208.4 183.2 100.1 181.7C82.4 181.7 68.1 167.4 68.1 149.7zM552 348C479.7 341.2 405.9 353.8 333 353.2C254.4 353.3 174.2 342.4 96.9 347.5C79.3 348.7 64 335.3 62.9 317.7C61.8 300.1 75.1 284.8 92.7 283.7C173.8 278.2 253.1 289.1 333 289.2C408 289.4 482.4 276.8 558 284.2C575.6 285.9 588.5 301.5 586.8 319.1C585.1 336.7 569.6 349.6 552 348zM465.5 506.2C414 506.6 162.5 506.4 106.4 506.5C64.7 506.3 64.4 442.7 106.4 442.5C211 442.7 438.9 442.4 540.4 442C558.1 442 572.4 456.3 572.4 474C567.3 525.1 500.2 500.5 465.4 506.2z'/%3e%3c/svg%3e"); } .sticky-top { position: sticky; top: 0; - z-index: 1000; + z-index: 1030; } .nav-icon { @@ -131,93 +151,26 @@ html { } } -/* -------------------------------------------------- - Typography --------------------------------------------------- */ -.copy-small { - font-size: 0.75rem; - opacity: 0.87; +.navbar .logo-svg > svg { + width: 8rem; + transition: width 0.3s ease; + animation: logo-shrink linear both; + animation-timeline: scroll(); + animation-range: 0px 100px; - @media (min-width: 768px) { - font-size: 0.85rem; - } - @media (min-width: 1400px) { - font-size: 1rem; - } + @media (max-width: 1024px) { width: 20vw; } } -/* -------------------------------------------------- - Modal & Cards --------------------------------------------------- */ -.modal-xl { - @media (min-width: 768px) { - max-width: 95vw; +@keyframes logo-shrink { + to { + width: 5rem; + @media (max-width: 1024px) { width: 10vw; } } - @media (min-width: 1400px) { - max-width: 85vw; - } - @media (min-width: 1921px) { - max-width: 75vw; - } -} - -.image-grow { - transition: box-shadow 350ms ease, transform 350ms ease; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.24); - - &:hover, - &:focus { - box-shadow: 0 8px 10px rgba(0, 0, 0, 0.2); - transform: translateY(-0.9rem) scale(1.02); - } -} - -.card-modal { - background-color: rgba(1, 11, 18, 0.8); - cursor: default; -} - -.pokedex-page { - position: relative; - top: 50px; - z-index: 99; - - @media (min-width: 768px) { - top: 100px !important; - } -} - -.modal-nav-btn { - position: fixed; - top: 50%; - transform: translateY(-50%); - z-index: 1060; /* above modal backdrop (1050) */ - background: rgba(255,255,255,0.1); - border: 1px solid rgba(255,255,255,0.2); - color: white; - border-radius: 50%; - width: 48px; - height: 48px; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: background 0.2s; - backdrop-filter: blur(4px); -} -.modal-nav-btn:hover { background: rgba(255,255,255,0.25); } -.modal-nav-btn.d-none { display: none !important; } -.modal-nav-prev { left: 12px; } -.modal-nav-next { right: 12px; } - -@media (max-width: 768px) { - .modal-nav-btn { display: none !important; } /* use swipe on mobile */ } /* -------------------------------------------------- Navigation Tabs & Tier Colors -------------------------------------------------- */ - .nav-link { font-weight: 600; color: rgba(255, 255, 255, 0.67); @@ -245,12 +198,12 @@ html { } $tiers: ( - nm: rgba(156, 204, 102, 1), - lp: rgba(211, 225, 86, 1), - mp: rgba(255, 238, 87, 1), - hp: rgba(255, 201, 41, 1), - dmg: rgba(255, 167, 36, 1), - vendor: hsl(262, 47%, 55%) + nm: hsla(88, 50%, 67%, 1), + lp: hsla(66, 70%, 68%, 1), + mp: hsla(54, 100%, 73%, 1), + hp: hsla(46, 100%, 65%, 1), + dmg: hsla(36, 100%, 65%, 1), + vendor: hsla(262, 47%, 63%, 1) ); @each $name, $color in $tiers { @@ -258,231 +211,73 @@ $tiers: ( border-bottom: 3px solid $color; &:hover, - &:focus { - background-color: rgba($color, 0.67); - } + &:focus { background-color: rgba($color, 0.67); } &.active { background-color: $color; border-bottom-color: $color; - - @if $name == vendor { - color: rgba(255, 255, 255, 0.87); - } } } - /* price-row alert left borders */ .nav-#{$name} div.alert { border-left: 3px solid $color; } } -/* -------------------------------------------------- - Misc UI --------------------------------------------------- */ - -.dark-callout { - @media (min-width: 768px) { - background-color: rgba(44, 48, 59, 1); - } -} - -.price-area { - max-height: 75px; -} - -.card-image { - aspect-ratio: 23 / 32; - object-fit: cover; - z-index: 1; - cursor: pointer; -} - -/* Icon Sizes */ -.small-icon svg { max-height: 16px; width: 100%; margin-top: -0.25rem; } -.medium-icon svg { max-height: 32px; width: 100%; margin-left: -0.25rem; } - -/* Masked Image */ -.masked-image { - z-index: 1000; - opacity: 1; - filter: brightness(0); -} - -/* Decorative Background */ -.starburst, -.whos-that-pokemon { - mix-blend-mode: lighten; - opacity: 0.1; - object-fit: cover; -} - -.whos-that-pokemon { - aspect-ratio: 1 / 1; -} - -/* SVG Generic Sizes */ -.energy-icon svg, -.rarity-icon-large svg, -.set-icon svg, -.edition-icon svg { - width: 2rem; - z-index: 999; - @media (min-width: 1024px) { - width: 2.5rem; - } -} - -.rarity-icon-large svg, -.set-icon svg { - margin-bottom: -0.25rem; -} - -.filter-icon svg, -.search-button { - width: 2rem; - fill: rgba(255,255,255,0.87); - stroke: rgba(255,255,255,0.87); -} - -/* Form states */ -.form-check-input:checked { - background-color: var(--bs-success); - border-color: var(--bs-success-border-subtle); -} - -.form-check-input:focus { - border-color: var(--bs-success); - box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); -} - -/* Back to Top */ -#btn-back-to-top { - position: fixed; - bottom: 5vh; - right: 5vw; - display: none; - z-index: 2; -} - -.top-icon svg { - width: 2rem; - height: 2rem; - fill: var(--bs-info-bg-subtle); - stroke: var(--bs-info-bg-subtle); -} - -#btn-back-to-top:hover .top-icon svg { - fill: var(--bs-info-border-subtle); - stroke: var(--bs-info-border-subtle); -} - -.shadow-filter { - filter: - drop-shadow(0 5px 5px rgba(0, 0, 0, 0.3)) - drop-shadow(0 4px 6px rgba(0, 0, 0, 0.2)); -} - -.tooltip.volatility-popover .tooltip-inner { - background: #1d1f21; - color: #e9ecef; - padding: 0.9rem 1rem; - border-radius: 0.6rem; - text-align: left; - max-width: 260px; - border: 1px solid rgba(255, 255, 255, 0.08); -} - -.tooltip.volatility-popover .tooltip-arrow::before { - border-top-color: #1d1f21 !important; -} - -/* -------------------------------------------------- - Pricing --------------------------------------------------- */ - -.price-row { - position: relative; - z-index: 2; - margin-top: -1.25rem; - border-radius: 0.33rem; - background: linear-gradient( - 90deg, - map-get($tiers, nm) 21%, - map-get($tiers, lp) 42%, - map-get($tiers, mp) 63%, - map-get($tiers, hp) 74%, - map-get($tiers, dmg) 85% - ); -} - -.inventory-button { - width: 40px; - height: 40px; - margin-bottom: -2rem; - margin-right: -0.25rem; - border-radius: 0.33rem; - background-color: hsl(262, 47%, 55%); - color: #fff; -} - -.inventory-label { - width: 100%; - height: 100%; - font-size: 1rem; - font-weight: 700; -} - -.fs-7 { - font-size: 0.9rem !important; -} - -/* Price Label */ -.price-label { - font-size: 0.69rem; - font-weight: 600; - color: rgba(0, 0, 0, 0.87); - background-color: hsl(88, 50%, 60%); - border-radius: 0.33rem 0 0 0.33rem; - box-shadow: 0 5px 10px rgba(0, 0, 0, 0.35); - - @media (min-width: 768px) { font-size: 0.72rem; } - @media (min-width: 996px) { font-size: 0.75rem; } - @media (min-width: 1200px) { font-size: 0.8rem; } - @media (min-width: 1600px) { font-size: 1rem; } - - &:nth-of-type(2) { background-color: hsl(66, 70%, 61%); } - &:nth-of-type(3) { background-color: hsl(54, 100%, 67%); } - &:nth-of-type(4) { background-color: hsl(45, 100%, 58%); } - &:last-of-type { background-color: hsl(36, 100%, 57%); border-radius: 0.33rem; } -} - /* -------------------------------------------------- Search -------------------------------------------------- */ - @media (max-width: 768px) { .search-box, .search-button { - min-height: 48px; + min-height: 32px; } } -::placeholder { - color: rgba(255, 255, 255, 0.7) !important; - opacity: 1 !important; - font-size: 1rem; - line-height: 2rem; +.search-container { + @media (max-width: 768px) { + margin-top: 0.25rem; + width: 100%; + animation: search-inline-mobile linear both; + animation-timeline: scroll(); + animation-range: 0px 100px; + } +} + +@keyframes search-inline-mobile { + from { width: 100%; } + to { margin-top: 0 !important; width: auto; flex: 1; } +} + +.navbar .container { + @media (max-width: 768px) { + animation: navbar-nowrap linear both; + animation-timeline: scroll(); + animation-range: 0px 100px; + } +} + +.nav-user-btn { + animation: hide-user-btn linear both; + animation-timeline: scroll(); + animation-range: 0px 100px; +} + +@keyframes hide-user-btn { + to { + opacity: 0; + width: 0; + overflow: hidden; + pointer-events: none; + } } -/* Sticky Search Bar */ .search-bar { position: fixed; bottom: 0; width: 100%; height: 48px; - z-index: 1000; + z-index: 1030; transform: rotate(180deg); @media (min-width: 768px) { @@ -494,14 +289,439 @@ $tiers: ( } } +.search-input { + color: rgba(255, 255, 255, .94); + border: 1px solid rgba(94, 53, 177, 1); +} + +::placeholder { + color: rgba(255, 255, 255, 0.7) !important; + opacity: 1 !important; + font-size: 1rem; + line-height: 2rem; +} + +.form-control:focus { + border-color: rgb(179, 157, 219); + outline: 0; + box-shadow: 0 0 0 0.15rem rgb(179, 157, 219, .67); +} + .facet-list { max-height: 185px; overflow-y: auto; } /* -------------------------------------------------- - Circles + Buttons -------------------------------------------------- */ +.btn-purple { + background-color: var(--lilac); + border-color: var(--purple); + color: #fff; + &:hover { background-color: var(--purple); color: var(--snow); } +} + +.btn-purple-secondary { + background-color: transparent; + border-color: var(--lilac); + color: var(--mauve); + + &:hover { + background-color: hsla(262, 47%, 63%, 0.15); + border-color: var(--mauve); + color: var(--mauve); + } +} + +.btn-edit { + background-color: var(--edit); + border-color: var(--edit); + color: #fff; + + > span, + > svg.nav-icon { + fill: var(--gold); + stroke: var(--gold); + color: var(--gold); + } + + &:hover { + background-color: var(--gold); + border-color: var(--gold); + color: #fff; + + > span, + > svg.nav-icon { + fill: var(--edit); + stroke: var(--edit); + } + } +} + +.btn-delete { + background-color: var(--delete); + border-color: var(--delete); + color: #fff; + + &:hover { + background-color: var(--dark-red); + border-color: var(--dark-red); + color: #fff; + } +} + +.btn-outline-success svg.nav-icon { + fill: var(--bs-success); + stroke: var(--bs-success); +} + +.btn.btn-outline-success:hover, +.btn.btn-outline-success:hover svg.nav-icon { + fill: var(--bs-success-border-subtle); + stroke: var(--bs-success-border-subtle); +} + +// ── Inventory form condition buttons ────────────────────────────────────── +$cond-text: ( + nm: hsla(88, 50%, 67%, 1), + lp: hsla(66, 70%, 68%, 1), + mp: hsla(54, 100%, 73%, 1), + hp: hsla(46, 100%, 65%, 1), + dmg: hsla(36, 100%, 65%, 1), +); + +@each $name, $color in $tiers { + @if map-has-key($cond-text, $name) { + .btn-cond-#{$name} { + border-color: rgba($color, 0.4); + color: var(--bs-body-color); + background: transparent; + font-weight: 600; + transition: background-color 0.1s, border-color 0.1s; + + &:hover { background-color: rgba($color, 0.67); border-color: transparent; } + } + + .btn-check:checked + .btn-cond-#{$name} { + background-color: $color; + border-color: $color; + color: rgba(0, 0, 0, 0.94); + } + } +} + +/* -------------------------------------------------- + Forms +-------------------------------------------------- */ +.form-check-input { + &:checked { + background-color: var(--bs-success); + border-color: var(--bs-success-border-subtle); + } + + &:focus { + border-color: var(--bs-success); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); + } +} + +/* -------------------------------------------------- + Pricing +-------------------------------------------------- */ +.price-row { + position: relative; + z-index: 2; + margin-top: -1.25rem; + border-radius: 0.33rem; + background: linear-gradient( + 90deg, + map-get($tiers, nm) 21%, + map-get($tiers, lp) 42%, + map-get($tiers, mp) 63%, + map-get($tiers, hp) 74%, + map-get($tiers, dmg) 85% + ); +} + +.edit-bar { + position: relative; + z-index: 2; + padding: 0.5rem; + margin-top: -1.25rem !important; + border-radius: 0.33rem; + background: var(--bs-border-color); +} + +.price-label { + font-size: 0.69rem; + font-weight: 600; + color: rgba(0, 0, 0, 0.87); + background-color: hsl(88, 50%, 67%); + border-radius: 0.33rem 0 0 0.33rem; + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.35); + + @media (min-width: 768px) { font-size: 0.72rem; } + @media (min-width: 996px) { font-size: 0.75rem; } + @media (min-width: 1200px) { font-size: 0.8rem; } + @media (min-width: 1600px) { font-size: 1rem; } + + &:nth-of-type(2) { background-color: hsl(66, 70%, 68%); } + &:nth-of-type(3) { background-color: hsl(54, 100%, 73%); } + &:nth-of-type(4) { background-color: hsl(46, 100%, 65%); } + &:last-of-type { background-color: hsl(36, 100%, 65%); border-radius: 0.33rem; } +} + +.price-area { max-height: 75px; } + +.inventory-button, +.btn-vendor { + background-color: hsl(262, 47%, 63%); + color: #fff; + + &:hover { + background-color: hsl(262, 47%, 55%); + color: #fff; + } +} + +.inventory-button { + margin-bottom: -2.25rem; + margin-right: -0.25rem; + z-index: 2; + transition: opacity 350ms ease; +} + +/* -------------------------------------------------- + Cards & Modal +-------------------------------------------------- */ +.card-image { + aspect-ratio: 23 / 32; + object-fit: cover; + z-index: 1; + cursor: pointer; +} + +.image-grow { + transition: box-shadow 350ms ease, transform 350ms ease; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.24); + + &:hover, + &:focus { + box-shadow: 0 8px 10px rgba(0, 0, 0, 0.2); + transform: translateY(-0.9rem) scale(1.02); + } +} + +.col:has(.image-grow:hover) .inventory-button { + opacity: 0.20; + transition: opacity 350ms ease; +} + +.modal-xl { + @media (min-width: 768px) { max-width: 95vw; } + @media (min-width: 1400px) { max-width: 85vw; } + @media (min-width: 1921px) { max-width: 75vw; } +} + +.cl-modalBackdrop { + background-color: rgba(1, 11, 18, 0.8); +} + +.card-modal { + background-color: rgba(1, 11, 18, 0.8); + cursor: default; +} + +.pokedex-page { + position: relative; + top: 50px; + z-index: 99; + + @media (min-width: 768px) { top: 100px !important; } +} + +.modal-nav-btn { + position: fixed; + top: 50%; + transform: translateY(-50%); + z-index: 1060; + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + color: white; + border-radius: 50%; + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: background 0.2s; + backdrop-filter: blur(4px); + + &:hover { background: rgba(255, 255, 255, 0.25); } + &.d-none { display: none !important; } +} + +.modal-nav-prev { left: 12px; } +.modal-nav-next { right: 12px; } + +@media (max-width: 768px) { + .modal-nav-btn { display: none !important; } +} + +.card-nav-prev, +.card-nav-next { + transition: all 0.2s ease-in-out; + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 36px; + padding: 0.375rem 0.5rem; + will-change: opacity; + + &:hover:not(:disabled) { + background-color: var(--bs-secondary); + border-color: var(--bs-secondary); + color: white; + transform: translateY(-1px); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + } + + &:active:not(:disabled) { transform: translateY(0); } + &:disabled { cursor: not-allowed; } + + @media (max-width: 768px) { + min-width: 40px; + padding: 0.5rem; + } +} + +/* -------------------------------------------------- + SVG Icons +-------------------------------------------------- */ +.small-icon svg { max-height: 16px; width: 100%; margin-top: -0.25rem; } +.medium-icon svg { max-height: 32px; width: 100%; margin-left: -0.25rem; } + +.energy-icon svg, +.rarity-icon-large svg, +.set-icon svg, +.edition-icon svg { + width: 2rem; + z-index: 999; + @media (min-width: 1024px) { width: 2.5rem; } +} + +.rarity-icon-large svg, +.set-icon svg { + margin-bottom: -0.25rem; +} + +.filter-icon svg, +.search-button { + width: 1.5rem; + fill: rgba(255, 255, 255, 0.87); + stroke: rgba(255, 255, 255, 0.87); +} + +.delete-svg { + width: 1.25rem; + height: 1.25rem; + fill: var(--dark-red); + stroke: var(--dark-red); + filter: url(#shadow); +} + +.edit-svg { + width: 1.25rem; + height: 1.25rem; + fill: var(--gold); + stroke: var(--gold); + filter: url(#shadow); +} + +.btn:hover .delete-svg { + fill: var(--delete); + stroke: var(--delete); +} + +.btn:hover .edit-svg { + fill: var(--edit); + stroke: var(--edit); +} + +.shadow-filter { + filter: + drop-shadow(0 5px 5px rgba(0, 0, 0, 0.3)) + drop-shadow(0 4px 6px rgba(0, 0, 0, 0.2)); +} + +.top-icon svg { + width: 2rem; + height: 2rem; + fill: var(--bs-light-bg-subtle); + stroke: var(--bs-light-bg-subtle); +} + +#btn-back-to-top:hover .top-icon svg { + fill: var(--bs-light-border-subtle); + stroke: var(--bs-light-border-subtle); +} + +/* -------------------------------------------------- + Back to Top +-------------------------------------------------- */ +#btn-back-to-top { + position: fixed; + bottom: 5vh; + right: 5vw; + display: none; + z-index: 2; +} + +/* -------------------------------------------------- + Tooltips +-------------------------------------------------- */ +.tooltip.volatility-popover { + .tooltip-inner { + background: #1d1f21; + color: #e9ecef; + padding: 0.9rem 1rem; + border-radius: 0.6rem; + text-align: left; + max-width: 260px; + border: 1px solid rgba(255, 255, 255, 0.08); + } + + .tooltip-arrow::before { + border-top-color: #1d1f21 !important; + } +} + +/* -------------------------------------------------- + Decorative / Misc +-------------------------------------------------- */ +.dark-callout { + @media (min-width: 768px) { + background-color: rgba(44, 48, 59, 1); + } +} + +.masked-image { + z-index: 1; + opacity: 1; + filter: brightness(0); + animation: pokemon-pulse 2s ease-in-out infinite; +} + +.starburst, +.whos-that-pokemon { + mix-blend-mode: lighten; + opacity: 0.1; + object-fit: cover; +} + +.whos-that-pokemon { aspect-ratio: 1 / 1; } %circle-base { width: 1rem; @@ -528,126 +748,406 @@ $tiers: ( } /* -------------------------------------------------- - Buttons + Hero -------------------------------------------------- */ +.hero { background-color: var(--hero-bg); } -.btn-warning > span, -.btn-warning > svg.nav-icon { - fill: var(--bs-warning-border-subtle); - stroke: var(--bs-warning-border-subtle); - color: var(--bs-warning-border-subtle); +.hero-bg { + position: absolute; + inset: 0; + background: + radial-gradient(ellipse 60% 50% at 80% 50%, hsla(262, 70%, 40%, 0.25) 0%, transparent 70%), + radial-gradient(ellipse 40% 60% at 20% 80%, hsla(190, 70%, 30%, 0.15) 0%, transparent 70%); + pointer-events: none; } -.btn.btn-warning:hover > span, -.btn.btn-warning:hover > svg.nav-icon { - fill: var(--bs-warning-bg-subtle); - stroke: var(--bs-warning-bg-subtle); +.hero-wrapper { isolation: isolate; } + +.hero-cards-mockup { + position: relative; + height: 340px; } -.btn-outline-success svg.nav-icon { - fill: var(--bs-success); - stroke: var(--bs-success); +.mockup-card { + position: absolute; + width: 225px; + aspect-ratio: 23 / 32; + overflow: hidden; + border: 1px solid hsla(262, 50%, 50%, 0.3); } -.btn.btn-outline-success:hover, -.btn.btn-outline-success:hover svg.nav-icon { - fill: var(--bs-success-border-subtle); - stroke: var(--bs-success-border-subtle); +.mockup-card--1 { left: 20%; top: 2.5%; transform: rotate(-8deg); } +.mockup-card--2 { left: 40%; top: 7%; transform: rotate(2deg); z-index: 1; } +.mockup-card--3 { left: 60%; top: 0; transform: rotate(10deg); } + +.price-chip { + position: absolute; + bottom: 2.5%; + padding: 0.35rem 0.75rem; + border-radius: 2rem; + font-size: 0.8rem; + z-index: 2; +} + +.price-chip--nm { left: 30%; background: hsl(88, 50%, 67%); color: rgba(0, 0, 0, .87); } +.price-chip--lp { left: 64%; background: hsl(66, 70%, 68%); color: rgba(0, 0, 0, .87); } + +.stats-bar { background-color: hsla(262, 20%, 12%, 0.6); } + +/* -------------------------------------------------- + Feature Cards +-------------------------------------------------- */ +.feature-card { + background-color: hsla(262, 20%, 12%, 0.6); + border: 1px solid hsla(262, 30%, 40%, 0.2); + transition: border-color 0.2s, transform 0.2s; + + &:hover { + border-color: hsla(262, 50%, 60%, 0.5); + transform: translateY(-2px); + } +} + +.feature-icon { + color: var(--mauve); + width: 2.5rem; + height: 2.5rem; + display: flex; + align-items: center; } /* -------------------------------------------------- - Card Modal Navigation + Premium Section -------------------------------------------------- */ +.premium-section { + background-color: hsla(258, 30%, 8%, 0.8); + overflow-x: clip; +} -.card-nav-prev, -.card-nav-next { - transition: all 0.2s ease-in-out; +.premium-item { + background-color: hsla(262, 20%, 14%, 0.8); + border: 1px solid hsla(262, 30%, 35%, 0.2); + transition: border-color 0.2s; + + &:hover { border-color: hsla(262, 50%, 60%, 0.4); } +} + +.badge-coming { + display: inline-block; + flex-shrink: 0; + padding: 0.2rem 0.55rem; + border-radius: 2rem; + font-size: 0.65rem; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + background: hsla(262, 60%, 60%, 0.2); + color: var(--mauve); + border: 1px solid hsla(262, 50%, 60%, 0.3); + margin-top: 0.1rem; +} + +.compare-icon { font-size: 2.5rem; line-height: 1; } + +.cta-section { + background: linear-gradient(180deg, transparent, hsla(262, 30%, 10%, 0.8)); +} + +footer .logo-svg > svg { width: var(--logo-width, 8rem); } + +/* -------------------------------------------------- + Energy Wheel +-------------------------------------------------- */ +.energy-wheel { + position: relative; + width: calc(var(--radius) * 2); + height: calc(var(--radius) * 2); + margin: 5px 0 10px; +} + +.energy-wheel-item { + position: absolute; + width: 25px; + height: 25px; + top: 50%; + left: 50%; + --angle: calc(360deg / var(--total) * var(--i)); + transform: rotate(var(--angle)) translateX(var(--radius)) rotate(calc(-1 * var(--angle))); + + &:first-of-type { z-index: 100; } +} + +/* -------------------------------------------------- + Inventory Grid & List +-------------------------------------------------- */ +#gridView { row-gap: 1.5rem; } + +.inv-grid-card { + position: relative; + height: 100%; + display: flex; + flex-direction: column; +} + +.inv-grid-media { cursor: pointer; } + +.inv-grid-body { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 1rem; + padding: .85rem .15rem 0; +} + +.inv-grid-main { + min-width: 0; + flex: 1; +} + +.inv-grid-title { + font-size: 1.9rem; + line-height: 1.05; + font-weight: 500; + margin-bottom: .4rem; + color: var(--bs-emphasis-color); +} + +.inv-grid-meta { display: flex; flex-direction: column; gap: .2rem; } + +.inv-grid-submeta { + display: flex; + flex-wrap: wrap; + gap: .35rem; + font-size: .9rem; + color: var(--snow); +} + +.inv-grid-price { + min-width: 112px; + text-align: right; + flex-shrink: 0; + color: #f3f3f3 !important; +} + +.inv-grid-trend, +.inv-list-price-line { + display: flex; + align-items: center; + justify-content: flex-end; + gap: .35rem; + font-weight: 700; + line-height: 1.1; +} + +.inv-grid-value, +.inv-list-price { + font-size: 1.15rem; + color: #111; +} + +.inv-grid-trend, +.inv-list-price-line { + &.up .inv-grid-arrow { color: #2e7d32; } + &.down { color: #c62828; } +} + +.inv-grid-delta, +.inv-list-delta { + text-align: right; + &.up { color: #2e7d32; } + &.down { color: #c62828; } +} + +.inv-grid-qty, +.inv-list-qty { + margin-top: .35rem; + font-size: .9rem; + color: #1ea7a1; +} + +.inv-grid-cart, +.inv-list-cart { + position: absolute; + right: .35rem; + bottom: .1rem; + width: 2.15rem; + height: 2.15rem; + border-radius: 999px; + border: 1px solid #4cb7b3; + background: transparent; + color: #38a9a5; display: inline-flex; align-items: center; justify-content: center; - min-width: 36px; - padding: 0.375rem 0.5rem; - will-change: opacity; + transition: .15s ease; - &:hover:not(:disabled) { - background-color: var(--bs-secondary); - border-color: var(--bs-secondary); - color: white; - transform: translateY(-1px); - box-shadow: 0 2px 4px rgba(0,0,0,0.1); - } - - &:active:not(:disabled) { transform: translateY(0); } - &:disabled { cursor: not-allowed; } + &:hover { background: rgba(76, 183, 179, .08); color: #2a9a96; } } +#tableView { background: transparent; } + +.inv-list-wrap { border-radius: 0; overflow: visible; } + +.inv-list-table { + --bs-table-bg: transparent; + --bs-table-hover-bg: transparent; + --bs-table-color: inherit; + margin: 0; + + tbody, + tr, + td { border: 0 !important; background: transparent !important; } +} + +.inv-list-row + .inv-list-row .inv-list-cardcell { + border-top: 1px solid rgba(0, 0, 0, .08) !important; +} + +.inv-list-cardcell { padding: 0; } + +.inv-list-card { + position: relative; + display: flex; + align-items: center; + gap: 1rem; + min-height: 116px; + padding: .8rem 4.5rem .8rem .35rem; + background: #f3f3f3; + border: 1px solid rgba(0, 0, 0, .08); + border-radius: .35rem; +} + +.inv-list-thumb { + width: 70px; + flex: 0 0 70px; + cursor: pointer; + + img { width: 100%; display: block; border-radius: .25rem; } +} + +.inv-list-info { min-width: 0; flex: 1; } + +.inv-list-name { + font-size: 1.1rem; + font-weight: 700; + color: #111827; + margin-bottom: .35rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: none; + + &:hover { text-decoration: underline; } +} + +.inv-list-meta { display: flex; flex-direction: column; gap: .1rem; font-size: .95rem; color: #4b5563; } + +.inv-list-setlink { color: #5b6f8f; text-decoration: underline; text-underline-offset: 2px; } + +.inv-list-condition { + margin-top: .35rem; + display: flex; + flex-wrap: wrap; + gap: .35rem; + font-size: .95rem; + color: #2b7a78; +} + +.inv-list-right { margin-left: auto; min-width: 140px; text-align: right; padding-right: 1rem; } + @media (max-width: 768px) { - .card-nav-prev, - .card-nav-next { - min-width: 40px; - padding: 0.5rem; - } + .inv-grid-body { flex-direction: column; gap: .5rem; } + .inv-grid-price { min-width: 0; text-align: left; } + .inv-grid-trend { justify-content: flex-start; } + .inv-list-card { align-items: flex-start; padding-right: 3.75rem; } + .inv-list-right { min-width: 0; padding-right: .25rem; } } /* -------------------------------------------------- - Swipe Animation + Pokémon / Interactive -------------------------------------------------- */ +.pokemon-transition { transition: opacity 0.4s ease; } + +.pokemon-clickable { + cursor: pointer; + + &:focus-visible { + outline: 3px solid #ffc107; + outline-offset: 4px; + border-radius: 4px; + } +} + +@keyframes pokemon-pulse { + 0%, 100% { filter: brightness(0) drop-shadow(0 0 6px var(--bs-info-border-subtle)); } + 50% { filter: brightness(0) drop-shadow(0 0 18px var(--bs-info)); } +} + +.view-toggle-btn { + text-decoration: none; + color: var(--bs-secondary-color) !important; + + &.active, + &:hover { color: var(--bs-body-color) !important; } + + &.active svg, + &:hover svg { color: var(--bs-body-color); } +} + +#catalogList .list-group-item.active { + background-color: rgba(186, 104, 200, .5) !important; + color: var(--snow) !important; +} + +/* -------------------------------------------------- + View Transitions +-------------------------------------------------- */ +@view-transition { + navigation: auto; +} + +::view-transition-group(card-image) { + animation-duration: 300ms; + animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +} + +::view-transition-old(card-image), +::view-transition-new(card-image) { + width: 100%; + height: 100%; + object-fit: cover; +} + +::view-transition-old(root), +::view-transition-new(root) { + animation-duration: 150ms; +} -/* Smooth the hero image morph */ ::view-transition-group(card-hero) { animation-duration: 350ms; animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } -/* Fade the old image out quickly so it doesn't ghost */ -::view-transition-old(card-hero) { - display: none; -} +::view-transition-old(card-hero) { display: none; } -/* Fade the new image in after it's in position */ ::view-transition-new(card-hero) { animation-duration: 350ms; animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } -/* Suppress the default full-page crossfade so only the card morphs */ ::view-transition-old(root), -::view-transition-new(root) { - animation: none; -} +::view-transition-new(root) { animation: none; } -/* Sliding out (old content) */ -::view-transition-old(.modal-content) { - animation: slide-out 200ms ease-in forwards; -} +::view-transition-old(.modal-content) { animation: slide-out 200ms ease-in forwards; } +::view-transition-new(.modal-content) { animation: slide-in 200ms ease-out forwards; } -/* Sliding in (new content) */ -::view-transition-new(.modal-content) { - animation: slide-in 200ms ease-out forwards; -} +#cardModal[data-nav-direction="next"]::view-transition-old(.modal-content) { animation: slide-out-left 200ms ease-in forwards; } +#cardModal[data-nav-direction="next"]::view-transition-new(.modal-content) { animation: slide-in-right 200ms ease-out forwards; } +#cardModal[data-nav-direction="prev"]::view-transition-old(.modal-content) { animation: slide-out-right 200ms ease-in forwards; } +#cardModal[data-nav-direction="prev"]::view-transition-new(.modal-content) { animation: slide-in-left 200ms ease-out forwards; } -/* Direction-aware — set via dataset.navDirection */ -#cardModal[data-nav-direction="next"]::view-transition-old(.modal-content) { - animation: slide-out-left 200ms ease-in forwards; -} -#cardModal[data-nav-direction="next"]::view-transition-new(.modal-content) { - animation: slide-in-right 200ms ease-out forwards; -} -#cardModal[data-nav-direction="prev"]::view-transition-old(.modal-content) { - animation: slide-out-right 200ms ease-in forwards; -} -#cardModal[data-nav-direction="prev"]::view-transition-new(.modal-content) { - animation: slide-in-left 200ms ease-out forwards; -} - -/* The silhouette fades out while the colour image blooms in */ - -::view-transition-old(pokemon-reveal) { - animation: 300ms ease-in both fade-to-white; -} -::view-transition-new(pokemon-reveal) { - animation: 500ms ease-out both bloom-in; -} +::view-transition-old(pokemon-reveal) { animation: 300ms ease-in both fade-to-white; } +::view-transition-new(pokemon-reveal) { animation: 500ms ease-out both bloom-in; } @keyframes fade-to-white { to { opacity: 0; filter: brightness(3); } @@ -659,7 +1159,7 @@ $tiers: ( } /* -------------------------------------------------- - Input Fix (Safari) +Input Fix (Safari) input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; diff --git a/src/assets/js/main.js b/src/assets/js/main.js index abc4f3a..5eaa103 100644 --- a/src/assets/js/main.js +++ b/src/assets/js/main.js @@ -1,52 +1,97 @@ import * as bootstrap from 'bootstrap'; window.bootstrap = bootstrap; -import 'bootstrap/dist/js/bootstrap.bundle.min.js'; -// trap browser back and close the modal if open -const cardModal = document.getElementById('cardModal'); -const loadingMsg = cardModal.innerHTML; -// Push a new history state when the modal is shown -cardModal.addEventListener('shown.bs.modal', () => { - history.pushState({ modalOpen: true }, null, '#cardModal'); -}); -// Listen for the browser's back button (popstate event) -window.addEventListener('popstate', (e) => { - if (cardModal.classList.contains('show')) { - const modalInstance = bootstrap.Modal.getInstance(cardModal); - if (modalInstance) { - modalInstance.hide(); +document.addEventListener('DOMContentLoaded', () => { + + // Initialize all Bootstrap modals + document.querySelectorAll('.modal').forEach(modalEl => { + bootstrap.Modal.getOrCreateInstance(modalEl); + }); + + // Initialize tooltips + document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => { + if (!el._tooltipInstance) { + el._tooltipInstance = new bootstrap.Tooltip(el, { container: 'body' }); } - } -}); -// Trigger a back navigation when the modal is closed via its native controls (X, backdrop click) -cardModal.addEventListener('hide.bs.modal', () => { - cardModal.innerHTML = loadingMsg; - if (history.state && history.state.modalOpen) { - history.back(); - } -}); + }); + // ---------------- DASHBOARD LOGIC ---------------- + const toggleBtn = document.getElementById("toggleViewBtn"); + const gridView = document.getElementById("gridView"); + const tableView = document.getElementById("tableView"); + const searchInput = document.getElementById("inventorySearch"); + const tbody = document.getElementById("inventoryRows"); - import { Tooltip } from "bootstrap"; - - // Initialize all tooltips globally - const initTooltips = () => { - document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => { - if (!el._tooltipInstance) { - el._tooltipInstance = new Tooltip(el, { - container: 'body', // ensures tooltip is appended to body, important for modals - }); + if(toggleBtn && gridView && tableView && tbody) { + // TOGGLE GRID/TABLE + toggleBtn.addEventListener("click", () => { + if(gridView.style.display !== "none") { + gridView.style.display = "none"; + tableView.style.display = "block"; + toggleBtn.textContent = "Switch to Grid View"; + } else { + gridView.style.display = "block"; + tableView.style.display = "none"; + toggleBtn.textContent = "Switch to Table View"; } }); - }; - // Run on page load - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initTooltips); - } else { - initTooltips(); + // SEARCH FILTER + if(searchInput) { + searchInput.addEventListener("input", e => { + const term = e.target.value.toLowerCase(); + [...tbody.querySelectorAll("tr")].forEach(row => { + row.style.display = row.textContent.toLowerCase().includes(term) ? "" : "none"; + }); + }); + } + + // SORTING + document.querySelectorAll("th[data-key]").forEach(th => { + let sortAsc = true; + th.addEventListener("click", () => { + const key = th.dataset.key; + const indexMap = {name:0,set:1,condition:2,qty:3,price:4,market:5,gain:6}; + const idx = indexMap[key]; + const rows = [...tbody.querySelectorAll("tr")]; + + rows.sort((a,b) => { + let aText = a.children[idx].textContent.replace(/\$|,/g,'').toLowerCase(); + let bText = b.children[idx].textContent.replace(/\$|,/g,'').toLowerCase(); + if(!isNaN(aText) && !isNaN(bText)) return sortAsc ? aText-bText : bText-aText; + return sortAsc ? aText.localeCompare(bText) : bText.localeCompare(aText); + }); + + sortAsc = !sortAsc; + tbody.innerHTML=""; + rows.forEach(r => tbody.appendChild(r)); + }); + }); + + // INLINE EDITING + GAIN/LOSS UPDATE + tbody.addEventListener("input", e => { + const row = e.target.closest("tr"); + if(!row) return; + + const priceCell = row.querySelector(".editable-price"); + const qtyCell = row.querySelector(".editable-qty"); + const marketCell = row.children[5]; + const gainCell = row.querySelector(".gain"); + + if(e.target.classList.contains("editable-price")) { + e.target.textContent = e.target.textContent.replace(/[^\d.]/g,""); + } + if(e.target.classList.contains("editable-qty")) { + e.target.textContent = e.target.textContent.replace(/\D/g,""); + } + + const price = parseFloat(priceCell.textContent) || 0; + const qty = parseInt(qtyCell.textContent) || 0; + const market = parseFloat(marketCell.textContent) || 0; + const gain = market - price; + + gainCell.textContent = (gain>=0 ? "+" : "-") + Math.abs(gain); + gainCell.className = gain>=0 ? "gain text-success" : "gain text-danger"; + }); } - - // Optional: observe DOM changes for dynamically added tooltips (e.g., modals loaded later) - const observer = new MutationObserver(() => initTooltips()); - observer.observe(document.body, { childList: true, subtree: true }); \ No newline at end of file +}); \ No newline at end of file diff --git a/src/assets/js/priceChart.js b/src/assets/js/priceChart.js index dfa3f34..7f3ec31 100644 --- a/src/assets/js/priceChart.js +++ b/src/assets/js/priceChart.js @@ -3,11 +3,11 @@ import Chart from 'chart.js/auto'; const CONDITIONS = ["Near Mint", "Lightly Played", "Moderately Played", "Heavily Played", "Damaged"]; const CONDITION_COLORS = { - "Near Mint": { active: 'rgba(156, 204, 102, 1)', muted: 'rgba(156, 204, 102, 0.67)' }, - "Lightly Played": { active: 'rgba(211, 225, 86, 1)', muted: 'rgba(211, 225, 86, 0.67)' }, - "Moderately Played": { active: 'rgba(255, 238, 87, 1)', muted: 'rgba(255, 238, 87, 0.67)' }, - "Heavily Played": { active: 'rgba(255, 201, 41, 1)', muted: 'rgba(255, 201, 41, 0.67)' }, - "Damaged": { active: 'rgba(255, 167, 36, 1)', muted: 'rgba(255, 167, 36, 0.67)' }, + "Near Mint": { active: 'hsla(88, 50%, 67%, 1)', muted: 'hsla(88, 50%, 67%, 0.67)' }, + "Lightly Played": { active: 'hsla(66, 70%, 68%, 1)', muted: 'hsla(66, 70%, 68%, 0.67)' }, + "Moderately Played": { active: 'hsla(54, 100%, 73%, 1)', muted: 'hsla(54, 100%, 73%, 0.67)' }, + "Heavily Played": { active: 'hsla(46, 100%, 65%, 1)', muted: 'hsla(46, 100%, 65%, 0.67)' }, + "Damaged": { active: 'hsla(36, 100%, 65%, 1)', muted: 'hsla(36, 100%, 65%, 0.67)' }, }; const RANGE_DAYS = { '1m': 30, '3m': 90, '6m': 180, '1y': 365, 'all': Infinity }; @@ -32,6 +32,12 @@ function setEmptyState(isEmpty) { canvasWrapper.classList.toggle('d-none', isEmpty); } +function setChartVisible(visible) { + const modal = document.getElementById('cardModal'); + const chartWrapper = modal?.querySelector('#priceHistoryChart')?.closest('.alert'); + if (chartWrapper) chartWrapper.classList.toggle('d-none', !visible); +} + function buildChartData(history, rangeKey) { const cutoff = RANGE_DAYS[rangeKey] === Infinity ? new Date(0) @@ -39,20 +45,14 @@ function buildChartData(history, rangeKey) { const filtered = history.filter(r => new Date(r.calculatedAt) >= cutoff); - // Always build the full date axis for the selected window, even if sparse. - // Generate one label per day in the range so the x-axis reflects the - // chosen period rather than collapsing to only the days that have data. const dataDateSet = new Set(filtered.map(r => r.calculatedAt)); const allDates = [...dataDateSet].sort((a, b) => new Date(a) - new Date(b)); - // If we have real data, expand the axis to span from cutoff → today so - // empty stretches at the start/end of a range are visible. let axisLabels = allDates; if (allDates.length > 0 && RANGE_DAYS[rangeKey] !== Infinity) { const start = new Date(cutoff); const end = new Date(); const expanded = []; - // Step through every day in the window for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) { expanded.push(d.toISOString().split('T')[0]); } @@ -101,17 +101,9 @@ function buildChartData(history, rangeKey) { function updateChart() { if (!chartInstance) return; const { labels, datasets, hasData, activeConditionHasData } = buildChartData(allHistory, activeRange); - - // Always push the new labels/datasets to the chart so the x-axis - // reflects the selected time window — even when there's no data for - // the active condition. Then toggle the empty state overlay on top. chartInstance.data.labels = labels; chartInstance.data.datasets = datasets; chartInstance.update('none'); - - // Show the empty state overlay if the active condition has no points - // in this window, but leave the (empty) chart visible underneath so - // the axis communicates the selected period. setEmptyState(!hasData || !activeConditionHasData); } @@ -135,7 +127,6 @@ function initPriceChart(canvas) { const { labels, datasets, hasData, activeConditionHasData } = buildChartData(allHistory, activeRange); - // Render the chart regardless — show empty state overlay if needed setEmptyState(!hasData || !activeConditionHasData); chartInstance = new Chart(canvas.getContext('2d'), { @@ -202,9 +193,16 @@ function initFromCanvas(canvas) { activeCondition = "Near Mint"; activeRange = '1m'; const modal = document.getElementById('cardModal'); + modal?.querySelectorAll('.price-range-btn').forEach(b => { b.classList.toggle('active', b.dataset.range === '1m'); }); + + // Hide chart if the vendor tab is already active when the modal opens + // (e.g. opened via the inventory button) + const activeTab = modal?.querySelector('.nav-link.active')?.getAttribute('data-bs-target'); + setChartVisible(activeTab !== '#nav-vendor'); + initPriceChart(canvas); } @@ -225,6 +223,10 @@ function setup() { document.addEventListener('shown.bs.tab', (e) => { if (!modal.contains(e.target)) return; const target = e.target?.getAttribute('data-bs-target'); + + // Hide the chart when the vendor tab is active, show it for all others + setChartVisible(target !== '#nav-vendor'); + const conditionMap = { '#nav-nm': 'Near Mint', '#nav-lp': 'Lightly Played', diff --git a/src/components/BackToTop.astro b/src/components/BackToTop.astro index 934dfd2..76ed1ce 100644 --- a/src/components/BackToTop.astro +++ b/src/components/BackToTop.astro @@ -3,7 +3,7 @@ --- Inventory management placeholder @@ -43,17 +44,150 @@ import BackToTop from "./BackToTop.astro" - - + \ No newline at end of file diff --git a/src/components/InventoryTable.astro b/src/components/InventoryTable.astro new file mode 100644 index 0000000..c8b9a1e --- /dev/null +++ b/src/components/InventoryTable.astro @@ -0,0 +1,37 @@ +--- +const mockInventory = [ + { name: "Charizard", set: "Base Set", condition: "NM", qty: 2, price: 350, market: 400, gain: 50 }, + { name: "Pikachu", set: "Shining Legends", condition: "LP", qty: 5, price: 15, market: 20, gain: 5 }, +]; +--- + + + + + + Card + Set + Condition + Qty + Price + Market + Gain/Loss + + + + {mockInventory.map(card => ( + + {card.name} + {card.set} + {card.condition} + {card.qty} + ${card.price} + ${card.market} + = 0 ? "text-success" : "text-danger"}> + {card.gain >= 0 ? "+" : "-"}${Math.abs(card.gain)} + + + ))} + + + \ No newline at end of file diff --git a/src/components/NavBar.astro b/src/components/NavBar.astro index 52fbba4..6062831 100644 --- a/src/components/NavBar.astro +++ b/src/components/NavBar.astro @@ -1,12 +1,36 @@ --- - +import { UserButton, SignInButton, Show } from '@clerk/astro/components' +import logo from "/src/svg/logo/rat_light.svg?raw"; --- - - - - Rigid's App ThingRAT + + + + - - + + + + + + + Sign In + + + + + + + + + + + + + Sign In + + + + + \ No newline at end of file diff --git a/src/components/NavItems.astro b/src/components/NavItems.astro index 23cc1e0..8fa0c8a 100644 --- a/src/components/NavItems.astro +++ b/src/components/NavItems.astro @@ -1,16 +1,46 @@ --- +--- + + + ---- - - - - - Cards - - - - - - - - \ No newline at end of file + + + + Menu + + + + + + Browse Cards + + + + + + + + \ No newline at end of file diff --git a/src/components/Search.astro b/src/components/Search.astro index 457e8dd..afb516b 100644 --- a/src/components/Search.astro +++ b/src/components/Search.astro @@ -8,7 +8,6 @@ import { Show } from '@clerk/astro/components' const val = Number(start.value) || 0; start.value = (val + 20).toString(); } - // delete the triggering element if (e && e.detail && e.detail.elt) { e.detail.elt.remove(); } @@ -26,21 +25,47 @@ import { Show } from '@clerk/astro/components' - - - - - - Filters - - + + {Astro.url.pathname === '/pokemon' && ( + + + + + Filters + + )} - - - + + + - \ No newline at end of file + + \ No newline at end of file diff --git a/src/db/relations.ts b/src/db/relations.ts index 218d423..867180c 100644 --- a/src/db/relations.ts +++ b/src/db/relations.ts @@ -21,6 +21,13 @@ export const relations = defineRelations(schema, (r) => ({ }), history: r.many.priceHistory(), latestSales: r.many.salesHistory(), + inventories: r.many.inventory(), + }, + inventory: { + sku: r.one.skus({ + from: r.inventory.skuId, + to: r.skus.skuId, + }), }, cards: { prices: r.many.skus(), diff --git a/src/db/schema.ts b/src/db/schema.ts index 5c16cc7..c634396 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -1,5 +1,5 @@ //import { mysqlTable, int, varchar, boolean, decimal, datetime, index } from "drizzle-orm/mysql-core" -import { integer, varchar, boolean, decimal, timestamp, index, pgSchema, uniqueIndex, primaryKey } from "drizzle-orm/pg-core"; +import { integer, varchar, boolean, decimal, timestamp, index, pgSchema, uuid, primaryKey } from "drizzle-orm/pg-core"; export const pokeSchema = pgSchema("pokemon"); @@ -98,6 +98,7 @@ export const skus = pokeSchema.table('skus', { }, (table) => [ index('idx_product_id_condition').on(table.productId, table.variant, table.condition), + index('idx_card_id_condition').on(table.cardId, table.condition), ]); export const priceHistory = pokeSchema.table('price_history', { @@ -124,6 +125,20 @@ export const salesHistory = pokeSchema.table('sales_history',{ primaryKey({ name: 'pk_sales_history', columns: [table.skuId, table.orderDate] }) ]); +export const inventory = pokeSchema.table('inventory',{ + inventoryId: uuid().primaryKey().notNull().defaultRandom(), + userId: varchar({ length: 100 }).notNull(), + catalogName: varchar({ length: 100 }), + skuId: integer().notNull(), + quantity: integer(), + purchasePrice: decimal({ precision: 10, scale: 2 }), + note: varchar({ length:255 }), + createdAt: timestamp().notNull().defaultNow(), +}, +(table) => [ + index('idx_userid_skuId').on(table.userId, table.skuId) +]); + export const processingSkus = pokeSchema.table('processing_skus', { skuId: integer().primaryKey(), }); diff --git a/src/layouts/Main.astro b/src/layouts/Main.astro index dfe03d6..dd9a5a0 100644 --- a/src/layouts/Main.astro +++ b/src/layouts/Main.astro @@ -1,5 +1,8 @@ --- import '/src/assets/css/main.scss'; +import NavBar from '../components/NavBar.astro'; +import NavItems from '../components/NavItems.astro'; +import Search from '../components/Search.astro'; const { title } = Astro.props; --- @@ -26,18 +29,18 @@ const { title } = Astro.props; - + + + + - - - diff --git a/src/middleware.ts b/src/middleware.ts index 5810867..9246b0d 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,45 +1,78 @@ import { clerkMiddleware, createRouteMatcher, clerkClient } from '@clerk/astro/server'; +import type { MiddlewareNext } from 'astro'; +import 'dotenv/config'; + +declare global { + namespace App { + interface Locals { + canAddInventory: boolean; + } + } +} const isProtectedRoute = createRouteMatcher(['/pokemon']); const isAdminRoute = createRouteMatcher(['/admin']); const TARGET_ORG_ID = "org_3Baav9czkRLLlC7g89oJWqRRulK"; -export const onRequest = clerkMiddleware(async (auth, context) => { - const { isAuthenticated, userId, redirectToSignIn } = auth(); +export const onRequest = clerkMiddleware(async (auth, context, next) => { + const { isAuthenticated, userId, redirectToSignIn, has } = auth(); if (!isAuthenticated && isProtectedRoute(context.request)) { return redirectToSignIn(); } + // ── Inventory visibility check ────────────────────────────────────────────── + // Resolves to true if the user belongs to the target org OR has the feature + const canAddInventory = process.env.INVENTORY_ACCESS === 'true' || + ( + isAuthenticated && + userId && + ( + !!has({ permission: "org:feature:inventory_add" }) || // Clerk feature flag + (await getUserOrgIds(context, userId)).includes(TARGET_ORG_ID) + ) + ); + + // Expose the flag to your Astro pages via locals + context.locals.canAddInventory = Boolean(canAddInventory); + + // ── Admin route guard ─────────────────────────────────────────── if (isAdminRoute(context.request)) { if (!isAuthenticated || !userId) { return redirectToSignIn(); } try { - const client = await clerkClient(context); // pass context here + const client = await clerkClient(context); const memberships = await client.organizations.getOrganizationMembershipList({ organizationId: TARGET_ORG_ID, }); - console.log("Total memberships found:", memberships.data.length); - console.log("Current userId:", userId); - console.log("Memberships:", JSON.stringify(memberships.data.map(m => ({ - userId: m.publicUserData?.userId, - role: m.role, - })), null, 2)); - const userMembership = memberships.data.find( (m) => m.publicUserData?.userId === userId ); if (!userMembership || userMembership.role !== "org:admin") { - return context.redirect("/"); + return new Response(null, { status: 404 }); } } catch (e) { console.error("Clerk membership check failed:", e); return context.redirect("/"); } } -}); \ No newline at end of file + + return next(); +}); + +// ── Helper: fetch all org IDs the current user belongs to ─────────────────── +async function getUserOrgIds(context: any, userId: string): Promise { + try { + const client = await clerkClient(context); + const memberships = await client.users.getOrganizationMembershipList({ userId }); + return memberships.data.map((m) => m.organization.id); + } catch (e) { + console.error("Failed to fetch user org memberships:", e); + return []; + } +} diff --git a/src/pages/404.astro b/src/pages/404.astro index 0fefd4c..d8d8fd0 100644 --- a/src/pages/404.astro +++ b/src/pages/404.astro @@ -1,7 +1,7 @@ --- export const prerender = false; import Layout from '../layouts/Main.astro'; -import NavItems from '../components/NavItems.astro'; +import Search from '../components/Search.astro'; import NavBar from '../components/NavBar.astro'; import Footer from '../components/Footer.astro'; import pokedexList from '../data/pokedex.json'; @@ -19,12 +19,10 @@ const pokemon = pokedexList.find(p => p["#"] === randomNumber); const pokemonName = pokemon?.Name || "Unknown Pokémon"; --- - - - - + + - 404Page Not Found + 404 - Page Not Found Sorry, the page you are looking for does not exist. Return to the home page or search for another Pokémon. @@ -67,7 +65,7 @@ const pokemonName = pokemon?.Name || "Unknown Pokémon"; >??? @@ -76,36 +74,11 @@ const pokemonName = pokemon?.Name || "Unknown Pokémon"; + - -
Return to the home page or search for another Pokémon. @@ -67,7 +65,7 @@ const pokemonName = pokemon?.Name || "Unknown Pokémon"; >??? @@ -76,36 +74,11 @@ const pokemonName = pokemon?.Name || "Unknown Pokémon";