diff --git a/.github/workflows/previews.yml b/.github/workflows/previews.yml new file mode 100644 index 0000000..3fa362a --- /dev/null +++ b/.github/workflows/previews.yml @@ -0,0 +1,39 @@ +name: Build & Generate Example Previews + +on: + push: + paths: + - 'src/pages/examples/**' + - 'scripts/generate-previews.mjs' + workflow_dispatch: + +jobs: + build-previews: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '20' + + # Playwright dipendenze di sistema (WebGL headless) + - run: npm ci + - run: npx playwright install --with-deps chromium + + - run: npm run build + - run: npm run preview & sleep 2 + + - name: Generate previews (JPG + WebP + GIF) + run: | + CAPTURE_SECS=3 FPS=12 READY_TIMEOUT_MS=60000 \ + VIEWPORT_W=1024 VIEWPORT_H=576 \ + npm run previews + + - name: Commit thumbs & manifest + run: | + git config user.name "ci-bot" + git config user.email "ci@example" + git add public/examples src/data/examples.manifest.json + git commit -m "chore: update example previews" || echo "no changes" + git push || true diff --git a/docs/.gitignore b/docs/.gitignore index dcc7f1d..d71b64b 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -23,3 +23,6 @@ public/examples/** # macOS-specific files .DS_Store + +#screen recordings +.tmp_frames/ \ No newline at end of file diff --git a/docs/package.json b/docs/package.json index 09ec101..c637315 100644 --- a/docs/package.json +++ b/docs/package.json @@ -6,7 +6,9 @@ "dev": "tsc --project tsconfig.build.json & astro dev --host", "start": "tsc --project tsconfig.build.json & astro dev", "build": "tsc --project tsconfig.build.json & astro check && astro build", - "preview": "astro preview", + "preview": "astro preview --port 4321", + "previews": "BASE_URL=http://localhost:4321 node scripts/generate-previews.mjs", + "update-previews": "npm run build && npm run preview & sleep 2 && npm run previews", "astro": "astro" }, "dependencies": { @@ -29,4 +31,4 @@ "devDependencies": { "@types/three": "~0.171.0" } -} +} \ No newline at end of file diff --git a/docs/public/examples-reviews/add-remove.gif b/docs/public/examples-reviews/add-remove.gif new file mode 100644 index 0000000..3586956 Binary files /dev/null and b/docs/public/examples-reviews/add-remove.gif differ diff --git a/docs/public/examples-reviews/add-remove.webp b/docs/public/examples-reviews/add-remove.webp new file mode 100644 index 0000000..d8b5d4d Binary files /dev/null and b/docs/public/examples-reviews/add-remove.webp differ diff --git a/docs/public/examples-reviews/animation.gif b/docs/public/examples-reviews/animation.gif new file mode 100644 index 0000000..66c31c9 Binary files /dev/null and b/docs/public/examples-reviews/animation.gif differ diff --git a/docs/public/examples-reviews/animation.webp b/docs/public/examples-reviews/animation.webp new file mode 100644 index 0000000..f61ce2f Binary files /dev/null and b/docs/public/examples-reviews/animation.webp differ diff --git a/docs/public/examples-reviews/euler.gif b/docs/public/examples-reviews/euler.gif new file mode 100644 index 0000000..b612760 Binary files /dev/null and b/docs/public/examples-reviews/euler.gif differ diff --git a/docs/public/examples-reviews/euler.webp b/docs/public/examples-reviews/euler.webp new file mode 100644 index 0000000..e45213c Binary files /dev/null and b/docs/public/examples-reviews/euler.webp differ diff --git a/docs/public/examples-reviews/first.gif b/docs/public/examples-reviews/first.gif new file mode 100644 index 0000000..fb71ac9 Binary files /dev/null and b/docs/public/examples-reviews/first.gif differ diff --git a/docs/public/examples-reviews/first.webp b/docs/public/examples-reviews/first.webp new file mode 100644 index 0000000..1ad4776 Binary files /dev/null and b/docs/public/examples-reviews/first.webp differ diff --git a/docs/public/examples-reviews/frustum-culling.gif b/docs/public/examples-reviews/frustum-culling.gif new file mode 100644 index 0000000..9e254ff Binary files /dev/null and b/docs/public/examples-reviews/frustum-culling.gif differ diff --git a/docs/public/examples-reviews/frustum-culling.webp b/docs/public/examples-reviews/frustum-culling.webp new file mode 100644 index 0000000..d38a3eb Binary files /dev/null and b/docs/public/examples-reviews/frustum-culling.webp differ diff --git a/docs/public/examples-reviews/instances-array.gif b/docs/public/examples-reviews/instances-array.gif new file mode 100644 index 0000000..85ac07d Binary files /dev/null and b/docs/public/examples-reviews/instances-array.gif differ diff --git a/docs/public/examples-reviews/instances-array.webp b/docs/public/examples-reviews/instances-array.webp new file mode 100644 index 0000000..698fc2d Binary files /dev/null and b/docs/public/examples-reviews/instances-array.webp differ diff --git a/docs/public/examples-reviews/instances-custom-data.gif b/docs/public/examples-reviews/instances-custom-data.gif new file mode 100644 index 0000000..21b454a Binary files /dev/null and b/docs/public/examples-reviews/instances-custom-data.gif differ diff --git a/docs/public/examples-reviews/instances-custom-data.webp b/docs/public/examples-reviews/instances-custom-data.webp new file mode 100644 index 0000000..d8a7473 Binary files /dev/null and b/docs/public/examples-reviews/instances-custom-data.webp differ diff --git a/docs/public/examples-reviews/raycasting.gif b/docs/public/examples-reviews/raycasting.gif new file mode 100644 index 0000000..7bc6224 Binary files /dev/null and b/docs/public/examples-reviews/raycasting.gif differ diff --git a/docs/public/examples-reviews/raycasting.webp b/docs/public/examples-reviews/raycasting.webp new file mode 100644 index 0000000..cd0aeb3 Binary files /dev/null and b/docs/public/examples-reviews/raycasting.webp differ diff --git a/docs/public/examples-reviews/sorting.gif b/docs/public/examples-reviews/sorting.gif new file mode 100644 index 0000000..0df711d Binary files /dev/null and b/docs/public/examples-reviews/sorting.gif differ diff --git a/docs/public/examples-reviews/sorting.webp b/docs/public/examples-reviews/sorting.webp new file mode 100644 index 0000000..8fc9681 Binary files /dev/null and b/docs/public/examples-reviews/sorting.webp differ diff --git a/docs/public/examples-reviews/tween.gif b/docs/public/examples-reviews/tween.gif new file mode 100644 index 0000000..519ae72 Binary files /dev/null and b/docs/public/examples-reviews/tween.gif differ diff --git a/docs/public/examples-reviews/tween.webp b/docs/public/examples-reviews/tween.webp new file mode 100644 index 0000000..34d927c Binary files /dev/null and b/docs/public/examples-reviews/tween.webp differ diff --git a/docs/public/examples-thumbs/add-remove.jpg b/docs/public/examples-thumbs/add-remove.jpg new file mode 100644 index 0000000..2f79431 Binary files /dev/null and b/docs/public/examples-thumbs/add-remove.jpg differ diff --git a/docs/public/examples-thumbs/animation.jpg b/docs/public/examples-thumbs/animation.jpg new file mode 100644 index 0000000..01c73da Binary files /dev/null and b/docs/public/examples-thumbs/animation.jpg differ diff --git a/docs/public/examples-thumbs/euler.jpg b/docs/public/examples-thumbs/euler.jpg new file mode 100644 index 0000000..8852040 Binary files /dev/null and b/docs/public/examples-thumbs/euler.jpg differ diff --git a/docs/public/examples-thumbs/first.jpg b/docs/public/examples-thumbs/first.jpg new file mode 100644 index 0000000..0e01c39 Binary files /dev/null and b/docs/public/examples-thumbs/first.jpg differ diff --git a/docs/public/examples-thumbs/frustum-culling.jpg b/docs/public/examples-thumbs/frustum-culling.jpg new file mode 100644 index 0000000..b27d42c Binary files /dev/null and b/docs/public/examples-thumbs/frustum-culling.jpg differ diff --git a/docs/public/examples-thumbs/instances-array.jpg b/docs/public/examples-thumbs/instances-array.jpg new file mode 100644 index 0000000..bd5f040 Binary files /dev/null and b/docs/public/examples-thumbs/instances-array.jpg differ diff --git a/docs/public/examples-thumbs/instances-custom-data.jpg b/docs/public/examples-thumbs/instances-custom-data.jpg new file mode 100644 index 0000000..6aebedd Binary files /dev/null and b/docs/public/examples-thumbs/instances-custom-data.jpg differ diff --git a/docs/public/examples-thumbs/raycasting.jpg b/docs/public/examples-thumbs/raycasting.jpg new file mode 100644 index 0000000..a28af35 Binary files /dev/null and b/docs/public/examples-thumbs/raycasting.jpg differ diff --git a/docs/public/examples-thumbs/sorting.jpg b/docs/public/examples-thumbs/sorting.jpg new file mode 100644 index 0000000..9c9e152 Binary files /dev/null and b/docs/public/examples-thumbs/sorting.jpg differ diff --git a/docs/public/examples-thumbs/tween.jpg b/docs/public/examples-thumbs/tween.jpg new file mode 100644 index 0000000..c930335 Binary files /dev/null and b/docs/public/examples-thumbs/tween.jpg differ diff --git a/docs/scripts/generate-previews.mjs b/docs/scripts/generate-previews.mjs new file mode 100644 index 0000000..a17840d --- /dev/null +++ b/docs/scripts/generate-previews.mjs @@ -0,0 +1,177 @@ +import { chromium } from 'playwright'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { globby } from 'globby'; +import sharp from 'sharp'; +import ffmpegPath from 'ffmpeg-static'; +import { execa } from 'execa'; + +// ===== Config ===== +const BASE_URL = process.env.BASE_URL || 'http://localhost:4321'; +const EXAMPLES_GLOB = process.env.EXAMPLES_GLOB || 'public/examples/**/index.js'; +const OUT_DIR = process.env.OUT_DIR || 'public/examples-reviews'; +const THUMBS_DIR = process.env.THUMBS_DIR || 'public/examples-thumbs'; +const MANIFEST_PATH = process.env.MANIFEST_PATH || 'src/data/examples.manifest.json'; + +const VIEWPORT = { + width: Number(process.env.VIEWPORT_W || 1024), + height: Number(process.env.VIEWPORT_H || 576) // 16:9 +}; + +const READY_TIMEOUT_MS = Number(process.env.READY_TIMEOUT_MS || 60000); // 60s +const CAPTURE_SECS = Number(process.env.CAPTURE_SECS || 3); // durata animazione +const FPS = Number(process.env.FPS || 12); // frame rate animazione +const QUALITY_JPG = Number(process.env.QUALITY_JPG || 82); + +// ===== Utils ===== +async function ensureDir(dir) { await fs.mkdir(dir, { recursive: true }); } + +function slugFromFile(f) { + // src/pages/examples/particles/index.astro -> particles + const parts = f.split(path.sep); + const idx = parts.indexOf('examples'); + return parts[idx + 1]; +} + +async function routesFromFiles() { + const files = await globby(EXAMPLES_GLOB); + return files.map(f => ({ + slug: slugFromFile(f), + url: `${BASE_URL}/instanced-mesh/examples/${slugFromFile(f)}/` + })); +} + +async function waitReady(page) { + // intercetto l’evento custom e i flag più comuni + await page.addInitScript(() => { + window.__EX_SHOT = { ready: false }; + window.addEventListener('exampleshot:ready', () => { window.__EX_SHOT.ready = true; }, { once: true }); + }); + + try { + await page.waitForFunction(() => { + return ( + window.__EX_SHOT?.ready === true || + window.__EXAMPLE_READY === true || + document.documentElement.getAttribute('data-example-ready') === 'true' || + !!document.querySelector('[data-example-ready], canvas[data-ready="true"], canvas') + ); + }, { timeout: READY_TIMEOUT_MS }); + } catch (_) { + // timeout: va bene, useremo il fallback (dopo 60s scattiamo comunque) + } + + // piccola stabilizzazione + await page.waitForTimeout(300); +} + +async function recordFrames(page, framesDir, totalFrames) { + await ensureDir(framesDir); + for (let i = 0; i < totalFrames; i++) { + const buf = await page.screenshot({ type: 'png' }); + const p = path.join(framesDir, `frame_${String(i).padStart(4, '0')}.png`); + await fs.writeFile(p, buf); + // pacing + const delay = 1000 / FPS; + await page.waitForTimeout(delay); + } +} + +async function encodeWithFFmpeg(inPattern, outGif, outWebp) { + // GIF (palette per qualità migliore) + await execa(ffmpegPath, [ + '-y', + '-framerate', String(FPS), + '-i', inPattern, + '-vf', `fps=${FPS},scale=${VIEWPORT.width}:${VIEWPORT.height}:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse`, + '-loop', '0', + outGif + ]); + + // WebP animata (di solito molto più leggera) + await execa(ffmpegPath, [ + '-y', + '-framerate', String(FPS), + '-i', inPattern, + '-vf', `fps=${FPS},scale=${VIEWPORT.width}:${VIEWPORT.height}:flags=lanczos`, + '-loop', '0', + '-an', + '-c:v', 'libwebp', + '-q:v', '65', // più alto = più piccolo + '-preset', 'picture', + outWebp + ]); +} + +async function generateOne(page, route) { + const { slug, url } = route; + const framesDir = path.join('.tmp_frames', slug); + const posterJpg = path.join(THUMBS_DIR, `${slug}.jpg`); + const animGif = path.join(OUT_DIR, `${slug}.gif`); + const animWebp = path.join(OUT_DIR, `${slug}.webp`); + + await ensureDir(OUT_DIR); + await ensureDir(THUMBS_DIR); + + await page.goto(url, { waitUntil: 'domcontentloaded' }); + await page.waitForLoadState('networkidle', { timeout: 15000 }).catch(()=>{}); + await waitReady(page); + + // cattura animazione + const totalFrames = Math.max(1, Math.floor(CAPTURE_SECS * FPS)); + await recordFrames(page, framesDir, totalFrames); + + // poster = primo frame -> jpg + const first = path.join(framesDir, 'frame_0000.png'); + const buf = await fs.readFile(first); + await sharp(buf).resize(VIEWPORT.width, VIEWPORT.height, { fit: 'cover' }) + .jpeg({ mozjpeg: true, quality: QUALITY_JPG }) + .toFile(posterJpg); + + // encoding animazioni + const inPattern = path.join(framesDir, 'frame_%04d.png'); + await encodeWithFFmpeg(inPattern, animGif, animWebp); + + // cleanup frames temporanei + await fs.rm(framesDir, { recursive: true, force: true }); + + return { + slug, + title: slug.replace(/[-_]/g, ' '), + url: url.replace(BASE_URL, ''), // path relativo + posterJpg: `/instanced-mesh/${posterJpg.replace(/^public\//, '')}`, + animGif: `/instanced-mesh/${animGif.replace(/^public\//, '')}`, + animWebp: `/instanced-mesh/${animWebp.replace(/^public\//, '')}` + }; +} + +async function main() { + const routes = await routesFromFiles(); + + const browser = await chromium.launch({ + headless: true, + // flags utili in CI con WebGL + args: ['--use-gl=swiftshader', '--no-sandbox'] + }); + const page = await browser.newPage({ viewport: VIEWPORT }); + + const manifest = []; + for (const r of routes) { + console.log('→', r.url); + try { + const entry = await generateOne(page, r); + manifest.push(entry); + console.log(' ✓', r.slug); + } catch (e) { + console.error(' ✗', r.slug, e?.message || e); + } + } + + await browser.close(); + + await ensureDir(path.dirname(MANIFEST_PATH)); + await fs.writeFile(MANIFEST_PATH, JSON.stringify(manifest, null, 2)); + console.log(`\n✓ Manifest aggiornato: ${MANIFEST_PATH} (${manifest.length} elementi)`); +} + +main().catch(err => { console.error(err); process.exit(1); }); diff --git a/docs/src/components/Example/ExampleGrid.astro b/docs/src/components/Example/ExampleGrid.astro new file mode 100644 index 0000000..a7c3877 --- /dev/null +++ b/docs/src/components/Example/ExampleGrid.astro @@ -0,0 +1,645 @@ +--- +import itemsJson from "../../data/examples.manifest.json"; +type ExampleItem = { + slug: string; + title: string; + url: string; + posterJpg?: string; + animWebp?: string; + category?: string; // Future use +}; +const items: ExampleItem[] = itemsJson as ExampleItem[]; +type Categories = Record; +const categories: Categories = {}; +for (const item of items) { + const cat = item.category || "examples"; + if (!categories[cat]) categories[cat] = []; + categories[cat].push(item); +} +const categoryEntries: [string, ExampleItem[]][] = Object.entries(categories); +// Astro state for selected example +let selected: ExampleItem | undefined = undefined; +let selectedUrl: string | undefined = undefined; +let selectedTitle: string | undefined = undefined; +if (Astro.url.searchParams.has("example")) { + const slug = Astro.url.searchParams.get("example"); + selected = items.find((i: ExampleItem) => i.slug === slug); + if (selected) { + selectedUrl = selected.url; + selectedTitle = selected.title; + } +} +--- + +
+
+ + + +
+
+ +
+
+
+ + + + +
diff --git a/docs/src/content/docs/examples-list/list.mdx b/docs/src/content/docs/examples-list/list.mdx new file mode 100644 index 0000000..e7c2776 --- /dev/null +++ b/docs/src/content/docs/examples-list/list.mdx @@ -0,0 +1,9 @@ +--- +title: Examples +description: Learn more about the project I’m working on. +template: splash +--- + +import ExamplesGrid from "../../../components/Example/ExampleGrid.astro"; + + diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx index 2b6bbf6..512b1bd 100644 --- a/docs/src/content/docs/index.mdx +++ b/docs/src/content/docs/index.mdx @@ -8,6 +8,9 @@ hero: - text: Tutorial link: /instanced-mesh/getting-started/00-introduction/ icon: right-arrow + - text: Examples + link: /instanced-mesh/examples-list/list/ + icon: right-arrow - text: API link: /instanced-mesh/api/readme icon: right-arrow @@ -18,4 +21,4 @@ hero: import Intro from '../../components/Intro/Intro.astro'; - + diff --git a/docs/src/data/examples.manifest.json b/docs/src/data/examples.manifest.json new file mode 100644 index 0000000..05e015f --- /dev/null +++ b/docs/src/data/examples.manifest.json @@ -0,0 +1,82 @@ +[ + { + "slug": "add-remove", + "title": "add remove", + "url": "/instanced-mesh/examples/add-remove/", + "posterJpg": "/instanced-mesh/examples-thumbs/add-remove.jpg", + "animGif": "/instanced-mesh/examples-reviews/add-remove.gif", + "animWebp": "/instanced-mesh/examples-reviews/add-remove.webp" + }, + { + "slug": "animation", + "title": "animation", + "url": "/instanced-mesh/examples/animation/", + "posterJpg": "/instanced-mesh/examples-thumbs/animation.jpg", + "animGif": "/instanced-mesh/examples-reviews/animation.gif", + "animWebp": "/instanced-mesh/examples-reviews/animation.webp" + }, + { + "slug": "euler", + "title": "euler", + "url": "/instanced-mesh/examples/euler/", + "posterJpg": "/instanced-mesh/examples-thumbs/euler.jpg", + "animGif": "/instanced-mesh/examples-reviews/euler.gif", + "animWebp": "/instanced-mesh/examples-reviews/euler.webp" + }, + { + "slug": "first", + "title": "first", + "url": "/instanced-mesh/examples/first/", + "posterJpg": "/instanced-mesh/examples-thumbs/first.jpg", + "animGif": "/instanced-mesh/examples-reviews/first.gif", + "animWebp": "/instanced-mesh/examples-reviews/first.webp" + }, + { + "slug": "frustum-culling", + "title": "frustum culling", + "url": "/instanced-mesh/examples/frustum-culling/", + "posterJpg": "/instanced-mesh/examples-thumbs/frustum-culling.jpg", + "animGif": "/instanced-mesh/examples-reviews/frustum-culling.gif", + "animWebp": "/instanced-mesh/examples-reviews/frustum-culling.webp" + }, + { + "slug": "instances-array", + "title": "instances array", + "url": "/instanced-mesh/examples/instances-array/", + "posterJpg": "/instanced-mesh/examples-thumbs/instances-array.jpg", + "animGif": "/instanced-mesh/examples-reviews/instances-array.gif", + "animWebp": "/instanced-mesh/examples-reviews/instances-array.webp" + }, + { + "slug": "instances-custom-data", + "title": "instances custom data", + "url": "/instanced-mesh/examples/instances-custom-data/", + "posterJpg": "/instanced-mesh/examples-thumbs/instances-custom-data.jpg", + "animGif": "/instanced-mesh/examples-reviews/instances-custom-data.gif", + "animWebp": "/instanced-mesh/examples-reviews/instances-custom-data.webp" + }, + { + "slug": "raycasting", + "title": "raycasting", + "url": "/instanced-mesh/examples/raycasting/", + "posterJpg": "/instanced-mesh/examples-thumbs/raycasting.jpg", + "animGif": "/instanced-mesh/examples-reviews/raycasting.gif", + "animWebp": "/instanced-mesh/examples-reviews/raycasting.webp" + }, + { + "slug": "sorting", + "title": "sorting", + "url": "/instanced-mesh/examples/sorting/", + "posterJpg": "/instanced-mesh/examples-thumbs/sorting.jpg", + "animGif": "/instanced-mesh/examples-reviews/sorting.gif", + "animWebp": "/instanced-mesh/examples-reviews/sorting.webp" + }, + { + "slug": "tween", + "title": "tween", + "url": "/instanced-mesh/examples/tween/", + "posterJpg": "/instanced-mesh/examples-thumbs/tween.jpg", + "animGif": "/instanced-mesh/examples-reviews/tween.gif", + "animWebp": "/instanced-mesh/examples-reviews/tween.webp" + } +] \ No newline at end of file diff --git a/docs/tsconfig.json b/docs/tsconfig.json index f8af6a8..35410f8 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -2,6 +2,7 @@ "extends": "astro/tsconfigs/strict", "compilerOptions": { "strictNullChecks": false, + "resolveJsonModule": false }, "exclude": [ "public/examples", diff --git a/package-lock.json b/package-lock.json index f709610..ebf2065 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,17 @@ }, "devDependencies": { "@eslint/js": "^9.28.0", + "@playwright/test": "^1.55.0", "@stylistic/eslint-plugin": "^4.4.0", "@three.ez/main": "^0.5.10", "@types/three": "^0.177.0", "eslint": "^9.28.0", + "execa": "^9.6.0", + "ffmpeg-static": "^5.2.0", + "globby": "^14.1.0", "meshoptimizer": "^0.23.0", + "playwright": "^1.55.0", + "sharp": "^0.34.3", "simplex-noise": "^4.0.3", "three-hex-tiling": "^0.1.5", "typescript": "^5.8.3", @@ -31,6 +37,21 @@ "three": ">=0.159.0" } }, + "node_modules/@derhuerst/http-basic": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/@derhuerst/http-basic/-/http-basic-8.2.4.tgz", + "integrity": "sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==", + "dev": true, + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^2.0.0", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@dimforge/rapier3d-compat": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", @@ -38,6 +59,16 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/@emnapi/runtime": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", + "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", @@ -718,6 +749,424 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.0" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.4.4" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -756,6 +1205,21 @@ "node": ">= 8" } }, + "node_modules/@playwright/test": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz", + "integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==", + "dev": true, + "dependencies": { + "playwright": "1.55.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@rollup/pluginutils": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", @@ -1059,6 +1523,24 @@ "win32" ] }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@stylistic/eslint-plugin": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.4.0.tgz", @@ -1110,6 +1592,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", + "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "undici-types": "~7.10.0" + } + }, "node_modules/@types/stats.js": { "version": "0.17.3", "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.3.tgz", @@ -1408,6 +1901,18 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1518,6 +2023,12 @@ "node": ">=8" } }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, "node_modules/bvh.js": { "version": "0.0.13", "resolved": "https://registry.npmjs.org/bvh.js/-/bvh.js-0.0.13.tgz", @@ -1534,6 +2045,12 @@ "node": ">=6" } }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1589,6 +2106,19 @@ "node": ">= 6" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1609,6 +2139,16 @@ "dev": true, "license": "MIT" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1616,6 +2156,21 @@ "dev": true, "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1656,6 +2211,24 @@ "dev": true, "license": "MIT" }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/esbuild": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", @@ -1896,6 +2469,32 @@ "node": ">=0.10.0" } }, + "node_modules/execa": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -1979,6 +2578,37 @@ "dev": true, "license": "MIT" }, + "node_modules/ffmpeg-static": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.2.0.tgz", + "integrity": "sha512-WrM7kLW+do9HLr+H6tk7LzQ7kPqbAgLjdzNE32+u3Ff11gXt9Kkkd2nusGFrlWMIe+XaA97t+I8JS7sZIrvRgA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@derhuerst/http-basic": "^8.2.0", + "env-paths": "^2.2.0", + "https-proxy-agent": "^5.0.0", + "progress": "^2.0.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -2070,7 +2700,23 @@ "darwin" ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/glob-parent": { @@ -2099,6 +2745,47 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -2123,6 +2810,43 @@ "node": ">=8" } }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2160,6 +2884,18 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2206,6 +2942,42 @@ "node": ">=0.12.0" } }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2410,6 +3182,34 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -2486,6 +3286,24 @@ "node": ">=6" } }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2506,6 +3324,18 @@ "node": ">=8" } }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2526,6 +3356,50 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz", + "integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==", + "dev": true, + "dependencies": { + "playwright-core": "1.55.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.55.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz", + "integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -2565,6 +3439,30 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "dev": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2596,6 +3494,20 @@ ], "license": "MIT" }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -2707,6 +3619,26 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", @@ -2720,6 +3652,48 @@ "node": ">=10" } }, + "node_modules/sharp": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.4", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2743,6 +3717,27 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/simplex-noise": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/simplex-noise/-/simplex-noise-4.0.3.tgz", @@ -2750,6 +3745,18 @@ "dev": true, "license": "MIT" }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2760,6 +3767,27 @@ "node": ">=0.10.0" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2846,6 +3874,13 @@ "typescript": ">=4.8.4" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "optional": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -2859,6 +3894,12 @@ "node": ">= 0.8.0" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, "node_modules/typescript": { "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", @@ -2896,6 +3937,26 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -2916,6 +3977,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "node_modules/vite": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", @@ -3079,6 +4146,18 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yoctocolors": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", + "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index a9bd82c..db0592d 100644 --- a/package.json +++ b/package.json @@ -52,11 +52,17 @@ }, "devDependencies": { "@eslint/js": "^9.28.0", + "@playwright/test": "^1.55.0", "@stylistic/eslint-plugin": "^4.4.0", "@three.ez/main": "^0.5.10", "@types/three": "^0.177.0", "eslint": "^9.28.0", + "execa": "^9.6.0", + "ffmpeg-static": "^5.2.0", + "globby": "^14.1.0", "meshoptimizer": "^0.23.0", + "playwright": "^1.55.0", + "sharp": "^0.34.3", "simplex-noise": "^4.0.3", "three-hex-tiling": "^0.1.5", "typescript": "^5.8.3",