From cb0b1a110360027fee98fd4637cf1bc1e009989a Mon Sep 17 00:00:00 2001 From: fadeltd Date: Fri, 30 Oct 2026 01:09:51 +0700 Subject: [PATCH 01/14] Batch 3 scaffold: offline, OG image, ES workers, share subpath, about.ts - C-8: drop the unneeded mode dependency in MergePane; lint is clean. - ES worker output, so a worker's dynamic import stays a separate chunk (Prettier, sql-formatter). - C-5: vite-plugin-pwa precaches every built asset; external registerSW.js, no manifest. - C-6: one static 1200x630 og.png (source in scripts/og-card.html), og:image and twitter:image on every page. - C-7: src/lib/registry/about.ts, read only by scripts/seo.ts, typed Record. - Core share subpath (encodeCompact / decodeCompact), with diff share links rebuilt on it. - JSON tool persists to IndexedDB (stateVersion 2); dompurify pinned for the SVG inspector. - ColorField and Swatch move to components/ui. --- .github/workflows/ci.yml | 2 +- CLAUDE.md | 4 +- index.html | 5 + package.json | 2 + packages/core/package.json | 4 + packages/core/src/diff/share.ts | 50 +- packages/core/src/share/compact.test.ts | 43 + packages/core/src/share/compact.ts | 72 + packages/core/src/share/index.ts | 1 + pnpm-lock.yaml | 3857 ++++++++++++++++- public/og.png | Bin 0 -> 51007 bytes scripts/og-card.html | 21 + scripts/seo.ts | 5 +- .../color => components/ui}/ColorField.tsx | 0 src/{tools/color => components/ui}/Swatch.tsx | 0 src/lib/registry/about.ts | 37 + src/lib/registry/index.ts | 8 +- src/tools/color/ColorTool.tsx | 4 +- src/tools/contrast/ContrastTool.tsx | 4 +- src/tools/diff/MergePane.tsx | 6 +- vite.config.ts | 25 +- wrangler.jsonc | 2 +- 22 files changed, 3882 insertions(+), 270 deletions(-) create mode 100644 packages/core/src/share/compact.test.ts create mode 100644 packages/core/src/share/compact.ts create mode 100644 packages/core/src/share/index.ts create mode 100644 public/og.png create mode 100644 scripts/og-card.html rename src/{tools/color => components/ui}/ColorField.tsx (100%) rename src/{tools/color => components/ui}/Swatch.tsx (100%) create mode 100644 src/lib/registry/about.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62f00b5..fe59540 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: set -euo pipefail pnpm pack --pack-destination "$RUNNER_TEMP" >/dev/null LIST=$(tar -tzf "$RUNNER_TEMP"/fadeltd-devtools-core-*.tgz) - for sub in base64 color cron css csv diff env escape hash html-text id-gen json jwt list lorem number qr random slug sql text text-bytes text-stats timestamp unicode url yaml-toml; do + for sub in base64 color cron css csv diff env escape hash html-text id-gen json jwt list lorem number qr random share slug sql text text-bytes text-stats timestamp unicode url yaml-toml; do # A here-string, not `echo | grep -q`: grep -q stops reading at the # first match, and under pipefail the writer's SIGPIPE fails the step. grep -qx "package/dist/$sub/index.d.ts" <<< "$LIST" || { echo "::error::Missing dist/$sub/index.d.ts"; exit 1; } diff --git a/CLAUDE.md b/CLAUDE.md index 832e7f8..b182b75 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,7 +25,9 @@ This file holds the rules. The maintainer also keeps `ROADMAP.md` and `TODO.md` `src/lib/registry/index.ts` drives the router, the home grid, the sidebar, the command palette, the build-time prerender, and the sitemap. -**Adding a tool = its engine in `packages/core/src//` (with an `index.ts` and an `exports` entry in `packages/core/package.json`) + one folder under `src/tools/` + one entry in the registry. Nothing else.** If a change requires hand-editing the router, the sidebar, or the SEO scripts, the abstraction has been broken — fix that instead of working around it. +**Adding a tool = its engine in `packages/core/src//` (with an `index.ts` and an `exports` entry in `packages/core/package.json`) + one folder under `src/tools/` + one entry in the registry + its paragraph in `src/lib/registry/about.ts`. Nothing else.** If a change requires hand-editing the router, the sidebar, or the SEO scripts, the abstraction has been broken — fix that instead of working around it. + +`about.ts` holds one honest paragraph per tool (which edge cases it actually handles) for the prerendered HTML. It is a separate module because only `scripts/seo.ts` reads it: in the registry it would ship in the preloaded chunk every visitor downloads. Never import it from `src/`. Typecheck fails when a tool has no paragraph. The one deliberate exception is the GitHub issue forms. They are static YAML that GitHub reads directly, so nothing in the build can generate them. **When you add a tool or a registry category, add it to the "Which tool?" dropdown in `.github/ISSUE_TEMPLATE/bug_report.yml`**, using the registry `title`. If the new tool changes what this project will or won't build, update the list of standing no's in `tool_request.yml` as well. diff --git a/index.html b/index.html index 42ce577..63d1e58 100644 --- a/index.html +++ b/index.html @@ -18,6 +18,11 @@ content="Diff, base64, JSON and list tools that run entirely in your browser. Nothing you paste is ever uploaded." /> + + + + +
diff --git a/package.json b/package.json index 1785f53..c40b990 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "cmdk": "1.1.1", "codemirror": "6.0.2", "comlink": "4.4.2", + "dompurify": "3.4.17", "idb-keyval": "6.3.0", "lucide-react": "1.47.0", "prettier": "3.9.10", @@ -51,6 +52,7 @@ "typescript": "7.0.2", "vite": "8.3.0", "vite-node": "^6.0.0", + "vite-plugin-pwa": "2.0.0", "vitest": "5.0.1", "wrangler": "4.136.1" }, diff --git a/packages/core/package.json b/packages/core/package.json index 05bfdc2..d74cebb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -78,6 +78,10 @@ "types": "./dist/random/index.d.ts", "default": "./dist/random/index.js" }, + "./share": { + "types": "./dist/share/index.d.ts", + "default": "./dist/share/index.js" + }, "./slug": { "types": "./dist/slug/index.d.ts", "default": "./dist/slug/index.js" diff --git a/packages/core/src/diff/share.ts b/packages/core/src/diff/share.ts index 5fd705e..e71491a 100644 --- a/packages/core/src/diff/share.ts +++ b/packages/core/src/diff/share.ts @@ -1,19 +1,16 @@ -import { base64ToBytes, bytesToBase64 } from '../base64/codec.ts' +import { decodeCompact, encodeCompact, fragmentValue } from '../share/compact.ts' /** - * Share links: both sides, compressed into the URL fragment. + * Share links: both sides, compressed into the URL fragment (`#d=1.…`). * * The fragment is never sent to the server, so this keeps the no-network * promise -- but the link *is* the content. It sits in browser history and * wherever it is pasted, which is why it is capped and the button says so. - * - * Format: `1.`. The version - * prefix lets the shape change later without misreading old links. + * The encoding itself lives in `../share`. */ /** The fragment key: `#d=`. */ export const SHARE_PARAM = 'd' -const VERSION = '1.' /** Encoded length past which no link is offered. Survives chat apps and trackers. */ export const MAX_SHARE_CHARS = 8000 @@ -27,9 +24,7 @@ export interface SharedDiff { /** The fragment value (without `#d=`) for these two sides. */ export async function encodeShare(left: string, right: string): Promise { - const json = new TextEncoder().encode(JSON.stringify([left, right])) - const packed = await bytesOf(new Blob([json]).stream().pipeThrough(new CompressionStream('deflate-raw'))) - return VERSION + bytesToBase64(packed, { alphabet: 'base64url', padding: false }) + return encodeCompact([left, right]) } /** @@ -37,36 +32,9 @@ export async function encodeShare(left: string, right: string): Promise * `null` for anything that is not an intact link this version wrote. */ export async function decodeShare(hash: string): Promise { - const value = hash.startsWith(`#${SHARE_PARAM}=`) ? hash.slice(SHARE_PARAM.length + 2) : hash - if (!value.startsWith(VERSION)) return null - try { - const packed = base64ToBytes(value.slice(VERSION.length), { alphabet: 'base64url', lenient: true }) - const stream = new Blob([packed as Uint8Array]).stream() - const json = await bytesOf( - stream.pipeThrough(new DecompressionStream('deflate-raw')).pipeThrough(capped(MAX_SHARE_DECODED_BYTES)), - ) - const parsed: unknown = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(json)) - if (!Array.isArray(parsed) || parsed.length !== 2) return null - const [left, right] = parsed as unknown[] - if (typeof left !== 'string' || typeof right !== 'string') return null - return { left, right } - } catch { - return null - } -} - -async function bytesOf(stream: ReadableStream): Promise { - return new Uint8Array(await new Response(stream).arrayBuffer()) -} - -/** Passes bytes through, erroring -- and so cancelling the source -- once past `limit`. */ -function capped(limit: number): TransformStream { - let total = 0 - return new TransformStream({ - transform(chunk, controller) { - total += chunk.length - if (total > limit) controller.error(new RangeError(`Share link inflates past ${limit} bytes`)) - else controller.enqueue(chunk) - }, - }) + const parsed = await decodeCompact(fragmentValue(hash, SHARE_PARAM), { maxDecodedBytes: MAX_SHARE_DECODED_BYTES }) + if (!Array.isArray(parsed) || parsed.length !== 2) return null + const [left, right] = parsed as unknown[] + if (typeof left !== 'string' || typeof right !== 'string') return null + return { left, right } } diff --git a/packages/core/src/share/compact.test.ts b/packages/core/src/share/compact.test.ts new file mode 100644 index 0000000..1295c75 --- /dev/null +++ b/packages/core/src/share/compact.test.ts @@ -0,0 +1,43 @@ +import fc from 'fast-check' +import { describe, expect, it } from 'vitest' +import { decodeCompact, encodeCompact, fragmentValue } from './compact.ts' + +const OPTS = { maxDecodedBytes: 1_000_000 } + +describe('compact encoding', () => { + it('round-trips any JSON value', async () => { + await fc.assert( + fc.asyncProperty(fc.jsonValue(), async (value) => { + const back = await decodeCompact(await encodeCompact(value), OPTS) + expect(back).toEqual(JSON.parse(JSON.stringify(value))) + }), + { numRuns: 100 }, + ) + }) + + it('is URL-safe', async () => { + expect(await encodeCompact({ a: 'ü/+=?#&' })).toMatch(/^1\.[A-Za-z0-9_-]+$/) + }) + + it('returns null for garbage, never throws', async () => { + await fc.assert( + fc.asyncProperty(fc.string(), async (s) => { + const r = await decodeCompact(s, OPTS) + // A random string is almost never a valid deflate stream of JSON. + if (r !== null) expect(s.startsWith('1.')).toBe(true) + }), + { numRuns: 200 }, + ) + expect(await decodeCompact('2.abc', OPTS)).toBeNull() + }) + + it('refuses a value that inflates past the cap', async () => { + const big = await encodeCompact('x'.repeat(50_000)) + expect(await decodeCompact(big, { maxDecodedBytes: 1000 })).toBeNull() + }) + + it('reads a fragment value with or without its key', () => { + expect(fragmentValue('#p=1.abc', 'p')).toBe('1.abc') + expect(fragmentValue('1.abc', 'p')).toBe('1.abc') + }) +}) diff --git a/packages/core/src/share/compact.ts b/packages/core/src/share/compact.ts new file mode 100644 index 0000000..d21c91a --- /dev/null +++ b/packages/core/src/share/compact.ts @@ -0,0 +1,72 @@ +import { base64ToBytes, bytesToBase64 } from '../base64/codec.ts' + +/** + * Compact, versioned encoding of a JSON value for a URL fragment. + * + * Format: `1.`. The version prefix lets the + * shape change later without misreading old links. Decoding is capped, + * because a crafted link can inflate enormously. + * + * A fragment is never sent to the server, so a link built from this keeps + * the no-network promise -- but the link *is* the content. It sits in + * browser history and wherever it is pasted, which is why callers cap it + * and say so. + */ + +const VERSION = '1.' + +export interface DecodeOptions { + /** Stop inflating past this many bytes and return null. */ + maxDecodedBytes: number +} + +/** `1.` for any JSON-serialisable value. */ +export async function encodeCompact(value: unknown): Promise { + const json = new TextEncoder().encode(JSON.stringify(value)) + const packed = await bytesOf(new Blob([json]).stream().pipeThrough(new CompressionStream('deflate-raw'))) + return VERSION + bytesToBase64(packed, { alphabet: 'base64url', padding: false }) +} + +/** + * The JSON value inside an `encodeCompact` string, or `null` for anything + * that is not an intact value this version wrote. Never throws. The value is + * untrusted: validate its shape before using it. + */ +export async function decodeCompact(text: string, options: DecodeOptions): Promise { + if (!text.startsWith(VERSION)) return null + try { + const packed = base64ToBytes(text.slice(VERSION.length), { alphabet: 'base64url', lenient: true }) + const stream = new Blob([packed as Uint8Array]).stream() + const json = await bytesOf( + stream.pipeThrough(new DecompressionStream('deflate-raw')).pipeThrough(capped(options.maxDecodedBytes)), + ) + return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(json)) as unknown + } catch { + return null + } +} + +/** + * The value of `#key=…` in a location hash, or the bare value when `hash` + * carries no `#key=` prefix. + */ +export function fragmentValue(hash: string, key: string): string { + const prefix = `#${key}=` + return hash.startsWith(prefix) ? hash.slice(prefix.length) : hash +} + +async function bytesOf(stream: ReadableStream): Promise { + return new Uint8Array(await new Response(stream).arrayBuffer()) +} + +/** Passes bytes through, erroring -- and so cancelling the source -- once past `limit`. */ +function capped(limit: number): TransformStream { + let total = 0 + return new TransformStream({ + transform(chunk, controller) { + total += chunk.length + if (total > limit) controller.error(new RangeError(`Inflates past ${limit} bytes`)) + else controller.enqueue(chunk) + }, + }) +} diff --git a/packages/core/src/share/index.ts b/packages/core/src/share/index.ts new file mode 100644 index 0000000..6a9e342 --- /dev/null +++ b/packages/core/src/share/index.ts @@ -0,0 +1 @@ +export { decodeCompact, encodeCompact, fragmentValue, type DecodeOptions } from './compact.ts' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 38b8636..6d214bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: comlink: specifier: 4.4.2 version: 4.4.2 + dompurify: + specifier: 3.4.17 + version: 3.4.17 idb-keyval: specifier: 6.3.0 version: 6.3.0 @@ -62,7 +65,7 @@ importers: version: 4.3.3 '@tailwindcss/vite': specifier: 4.3.3 - version: 4.3.3(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1)) + version: 4.3.3(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) '@types/node': specifier: 24.9.2 version: 24.9.2 @@ -74,7 +77,7 @@ importers: version: 19.3.0(@types/react@19.3.0) '@vitejs/plugin-react': specifier: 6.1.1 - version: 6.1.1(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1)) + version: 6.1.1(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) fast-check: specifier: 4.10.2 version: 4.10.2 @@ -89,13 +92,16 @@ importers: version: 7.0.2 vite: specifier: 8.3.0 - version: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1) + version: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) vite-node: specifier: ^6.0.0 - version: 6.0.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1) + version: 6.0.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) + vite-plugin-pwa: + specifier: 2.0.0 + version: 2.0.0(supports-color@10.2.2)(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(workbox-build@7.4.1(supports-color@10.2.2))(workbox-window@7.4.1) vitest: specifier: 5.0.1 - version: 5.0.1(@types/node@24.9.2)(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1)) + version: 5.0.1(@types/node@24.9.2)(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) wrangler: specifier: 4.136.1 version: 4.136.1(@types/node@24.9.2) @@ -141,10 +147,517 @@ importers: version: 7.0.2 vitest: specifier: 5.0.1 - version: 5.0.1(@types/node@24.9.2)(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1)) + version: 5.0.1(@types/node@24.9.2)(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) packages: + '@apideck/better-ajv-errors@0.3.7': + resolution: {integrity: sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw==} + engines: {node: '>=10'} + peerDependencies: + ajv: '>=8' + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.29.7': + resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.29.10': + resolution: {integrity: sha512-/YY9soQuWa8TURtHXHT+GbklpbAA+dZErxFGHIr6qgDg96xuGWrTAs1+MfjgVJPF0YLQsOVsOYs8lhgsPbOinQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.29.7': + resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.29.7': + resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.29.7': + resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.29.7': + resolution: {integrity: sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.11': + resolution: {integrity: sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.29.7': + resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.10': + resolution: {integrity: sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.11': + resolution: {integrity: sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7': + resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7': + resolution: {integrity: sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7': + resolution: {integrity: sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7': + resolution: {integrity: sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7': + resolution: {integrity: sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7': + resolution: {integrity: sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.29.7': + resolution: {integrity: sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.29.7': + resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.29.7': + resolution: {integrity: sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.29.7': + resolution: {integrity: sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.29.7': + resolution: {integrity: sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.29.7': + resolution: {integrity: sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.29.11': + resolution: {integrity: sha512-iARF4jjHSM0v8DImCACPXrwEltBP3DgqDpXTvjAMBAVpyEY0LiGss78wvI+KtmW+IJNEvZ5sOhPDCsOsXsIybA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.29.7': + resolution: {integrity: sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.29.7': + resolution: {integrity: sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.29.11': + resolution: {integrity: sha512-f8kTDM/nTvpLeafb+TJ4jboFr/Y3BbC+QzgPpPGzSKJqWLnj9LW0WLqv4XKA11LX5sr+EgvLE6Ix3YsCEusodw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.29.7': + resolution: {integrity: sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.29.7': + resolution: {integrity: sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.29.7': + resolution: {integrity: sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.29.7': + resolution: {integrity: sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.29.7': + resolution: {integrity: sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.29.7': + resolution: {integrity: sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.29.7': + resolution: {integrity: sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.29.7': + resolution: {integrity: sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.29.7': + resolution: {integrity: sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.29.7': + resolution: {integrity: sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.29.7': + resolution: {integrity: sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.29.7': + resolution: {integrity: sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.29.7': + resolution: {integrity: sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.29.7': + resolution: {integrity: sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.29.7': + resolution: {integrity: sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.29.7': + resolution: {integrity: sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.29.8': + resolution: {integrity: sha512-6iSnEK0zlkLKU4heofK/AdmRD4e2SHVpJMtrwnTCzhnaM98ria4rTrOXBBi45BTTYnJtO8txnPsX4fChYXkmeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.29.7': + resolution: {integrity: sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7': + resolution: {integrity: sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.29.7': + resolution: {integrity: sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7': + resolution: {integrity: sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.29.7': + resolution: {integrity: sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.29.7': + resolution: {integrity: sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.29.7': + resolution: {integrity: sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.29.7': + resolution: {integrity: sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.29.7': + resolution: {integrity: sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.29.7': + resolution: {integrity: sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.29.7': + resolution: {integrity: sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.29.7': + resolution: {integrity: sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.29.7': + resolution: {integrity: sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.29.8': + resolution: {integrity: sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.29.7': + resolution: {integrity: sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.29.7': + resolution: {integrity: sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.29.7': + resolution: {integrity: sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.29.11': + resolution: {integrity: sha512-5xWGhfuMgbOfIjsk/I2c4uMnoqRwQdKYi4QjvybQrcEVE0G9/p3qmIkp2Sxe8YF8DGNNVs5whfwNqIM5684XoQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.29.7': + resolution: {integrity: sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.29.7': + resolution: {integrity: sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.29.7': + resolution: {integrity: sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.29.7': + resolution: {integrity: sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.29.7': + resolution: {integrity: sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.29.7': + resolution: {integrity: sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.29.7': + resolution: {integrity: sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.29.7': + resolution: {integrity: sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/runtime@7.29.10': + resolution: {integrity: sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.10': + resolution: {integrity: sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.11': + resolution: {integrity: sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==} + engines: {node: '>=6.9.0'} + '@cloudflare/kv-asset-handler@0.5.0': resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} engines: {node: '>=22.0.0'} @@ -537,6 +1050,10 @@ packages: cpu: [x64] os: [win32] + '@isaacs/cliui@9.0.0': + resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} + engines: {node: '>=18'} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -547,6 +1064,9 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + '@jridgewell/sourcemap-codec@1.6.0': resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} @@ -568,6 +1088,13 @@ packages: '@marijn/find-cluster-break@1.0.4': resolution: {integrity: sha512-Wy0V7+SGUjnF9/TkiM1hKVDPj7jKXduPNboMVtHTA8dySMURWqfg/JZ9E2Sq8JgSJmkl7k7Qe9FLeMSrSraWmQ==} + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + libc: [glibc] + '@oxc-project/types@0.150.0': resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==} @@ -966,86 +1493,273 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@sindresorhus/is@7.2.0': - resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} - engines: {node: '>=18'} + '@rollup/plugin-babel@6.1.0': + resolution: {integrity: sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + rollup: + optional: true - '@speed-highlight/core@1.2.24': - resolution: {integrity: sha512-qeW2e1l78afw8VhRPfPQ1Gjj+KU5XFQ/OFV5ti6eTa9bruO7mJyZtA4vw0ofqmA3tKCkROE9xLk3VZoeRc98nw==} + '@rollup/plugin-node-resolve@16.0.3': + resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true - '@tailwindcss/node@4.3.3': - resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + '@rollup/plugin-replace@6.0.3': + resolution: {integrity: sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true - '@tailwindcss/oxide-android-arm64@4.3.3': - resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} - engines: {node: '>= 20'} + '@rollup/plugin-terser@1.0.2': + resolution: {integrity: sha512-+UihHnuLnQDQ/9wog7ym+7oawCXkjTIo0WwkK7s//0mkktTN0VhYk6fGVxH+NtGzW064w5RCGgreMDxRVhHCzw==} + engines: {node: '>=20.0.0'} + peerDependencies: + rollup: ^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.5.1': + resolution: {integrity: sha512-P9ZvnfouRu4MSC+ilERaw6FmZs8zqKhpHxEs4aGDGWQAMzQGXnhvCSvoNwokfNvdt7t3Y5Y4p6gStQgezeIqug==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.66.0': + resolution: {integrity: sha512-7y4XeoUHPecjfUDRmap8U0q6bjfLAPn7uOGT8BKyqy8uNDOjsGS8rlBhOx6sZLVwwjN6hapvGl2mW9/IS6UNww==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.66.0': + resolution: {integrity: sha512-e8WugYZXpgqrlMXLCudYVRAxAcK7AIY36JxRDwFRD38L9q62G1VsEUW6ijXxzjxYjNB8i1zo3B/ycIQ/qjLC/Q==} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.3.3': - resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} - engines: {node: '>= 20'} + '@rollup/rollup-darwin-arm64@4.66.0': + resolution: {integrity: sha512-6vkDCfyDjNLokh+Rt1D2KtNgFFMEPdkWha/mbtP4cSjh9xiN8T/JBS/A7nDYKjmMAXHrvR9J3AzUk2LtFbIBLg==} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.3.3': - resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} - engines: {node: '>= 20'} + '@rollup/rollup-darwin-x64@4.66.0': + resolution: {integrity: sha512-/mzZAX0+Q6eXxvkR0HB0RZCCrsbmFD8DpsvCQoMYABDfq/DjqkbfytuJ7GvZ4AhR/Mgis2fXbZrGqfbcK2Z5fA==} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.3.3': - resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} - engines: {node: '>= 20'} + '@rollup/rollup-freebsd-arm64@4.66.0': + resolution: {integrity: sha512-JQJx9E8tUjGqed1RevDmoHaS3V30ODdV8uyvGxlwd4K5se6bkPrMeltSybqAtpJ+ltDRfo7FORQaJG2Hd5nGog==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.66.0': + resolution: {integrity: sha512-GEaUQQjMaG7X1304qMgqLjzCKv6CdDirzJSS+UqUs3xhJ9X2Kjzu0JDbLoUU42FWxwmZqz+SsgU1SlQv9GNRVA==} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': - resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} - engines: {node: '>= 20'} + '@rollup/rollup-linux-arm-gnueabihf@4.66.0': + resolution: {integrity: sha512-DgmAU9N0NhDQDzHgFUocDio7zRO8jR/nY/obDGvCR8zbJ6J77Fg6X49yPuExJUpYPbC82EOaRhG969pWzUSnHQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.66.0': + resolution: {integrity: sha512-zG09YZ+sk/HoHq+P9gYB0EseQ3M20RbDuKGWey6fbiMDV1LxfXvhgSW9jDaCcvFPMgfLXpimzXY+UYvA8R2mLQ==} cpu: [arm] os: [linux] + libc: [musl] - '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': - resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} - engines: {node: '>= 20'} + '@rollup/rollup-linux-arm64-gnu@4.66.0': + resolution: {integrity: sha512-vlArhgyXbcVc1D4WZPoiYuqJIaMiSHBj6etRXua0KlfjlZeuiQyVAPYi0/N49O4mSp3u1iGUBLEjNa8ec81XZg==} cpu: [arm64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-arm64-musl@4.3.3': - resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} - engines: {node: '>= 20'} + '@rollup/rollup-linux-arm64-musl@4.66.0': + resolution: {integrity: sha512-RxJZtk0G87YPotDaRDvRqStV8cbqLSC5TroHvQHHKsqBg0ok64RxKgtXeFsa7FBcy7mb4crBrG3xK8USWIqWNA==} cpu: [arm64] os: [linux] libc: [musl] - '@tailwindcss/oxide-linux-x64-gnu@4.3.3': - resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} - engines: {node: '>= 20'} - cpu: [x64] + '@rollup/rollup-linux-loong64-gnu@4.66.0': + resolution: {integrity: sha512-qlGcw959PrLLkZhiBxF3OZ6SQvj+OKY85mCqGNgSRs4LZlDJ6ICqmz6UdxtINdhcXYwNIbZoapQVBAmZlD2hUQ==} + cpu: [loong64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-x64-musl@4.3.3': - resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} - engines: {node: '>= 20'} - cpu: [x64] + '@rollup/rollup-linux-loong64-musl@4.66.0': + resolution: {integrity: sha512-TvTI/7Cy0WvRA1SXgwzu6bwXBnMpmeT/ln5q7WvSpqmXy1VbmEnY8HVIDQNBiaHJvoWqRr0M4FNo7qiqtyMaFw==} + cpu: [loong64] os: [linux] libc: [musl] - '@tailwindcss/oxide-wasm32-wasi@4.3.3': - resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] - bundledDependencies: - - '@napi-rs/wasm-runtime' - - '@emnapi/core' - - '@emnapi/runtime' - - '@tybys/wasm-util' - - '@emnapi/wasi-threads' - - tslib - + '@rollup/rollup-linux-ppc64-gnu@4.66.0': + resolution: {integrity: sha512-UZEDkSoDRZ50NyLj70nO6mpIK3dvs2cPTTvQb1bA/PBDmr9fvn5KiESuGfh6srd7Zi6Lt9Sv2sVrFr1QoHLvPQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.66.0': + resolution: {integrity: sha512-VRbxTvagLh3HRcysx1CNq+HHcvJbSebNI6fCbk7jwmmWHkXjrSIV3igXBpRHUTFdg0pagWGu50xoSZXnrZHjVQ==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.66.0': + resolution: {integrity: sha512-uWdJpvYqh0LE/i+YBL3sMX0a2kUkICENjmrPmyBmMOFc3jMrs6MSQH/pMD+VIpGEoUcr3XkHGLLfymzIqPSRRg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.66.0': + resolution: {integrity: sha512-3FUd88pxgWAjK60OgCrika1AcNG+u8zaCdWjg4TlcZTDkt5TGyci8ZU64l3OJ9ax0MEjw3P80B/3PoSQDCLwJQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.66.0': + resolution: {integrity: sha512-qT2rMmgyej62g5AdwktK+Bt2lBu497pzWZe12TTQ5JmZjOYPnEwcPEAOSLTBxBV+6gIES75rXm219v/QkT397A==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.66.0': + resolution: {integrity: sha512-E46rnZgp7J84Qwe8RMlF+rgimnDo/9fuiGbUArGB0ZkMWf97O7yeSCfKls5x/YJOF0RcuSe9RctuKe1hlxICiA==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.66.0': + resolution: {integrity: sha512-Uz1HiZe+qBzqgdPsON+z0wRx3WdATuyzwvGT6cr2VZVrqqko0ZJVfkrQJtwDMuDcAfXYdo7XucaxJ7lTLjEcgg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.66.0': + resolution: {integrity: sha512-Ph7TX3Ex5qdfcb7Tc6eEPugVkRfJ501eTovO4T5029YObfpZ7dzn3RiwpFvGp50nk2QUhvBMZ0PFGlEj6pOYTg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.66.0': + resolution: {integrity: sha512-nZYPEya7ZkeNxUEiF7qmNZbyLTyRN3MK5cO22KqsaoneUzXJuzBIQRw+7BEQy9LGj2O5vrCE9V3EpK06FYo1tQ==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.66.0': + resolution: {integrity: sha512-u7aTfS3R6C4hRsqXSzaO6qLZYLVuMkHMvcWlux4IM8IJif1VCl2cgPBSo5gSNoFoStQypQP+qwq8W1IyFBf1QQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.66.0': + resolution: {integrity: sha512-e+i9ueHrH1Bafb0cf0woAIVJbYcoC8vRaD+vz+Xi8w6jz9cYJSV3vyKO6ELGSZ6g4W4MJOm2WPLN4/nQf77h7w==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.66.0': + resolution: {integrity: sha512-KfTCbrtr2zXTqH2E1F+uYlFTrYibxHKqPAHobpd8Kfg49cEHmuHebOidCTDRgqG1suaPFX7oejFQeGE71zWKfA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.66.0': + resolution: {integrity: sha512-xA+neBh1bqkqaS3b48T2UHZnDsTIpb9B0h+TdBzynjF/ymCpYndhfY10RqW0qSKB1HqqQA83mINVhAdwA+mETg==} + cpu: [x64] + os: [win32] + + '@sindresorhus/is@7.2.0': + resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} + engines: {node: '>=18'} + + '@speed-highlight/core@1.2.24': + resolution: {integrity: sha512-qeW2e1l78afw8VhRPfPQ1Gjj+KU5XFQ/OFV5ti6eTa9bruO7mJyZtA4vw0ofqmA3tKCkROE9xLk3VZoeRc98nw==} + + '@tailwindcss/node@4.3.3': + resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + + '@tailwindcss/oxide-android-arm64@4.3.3': + resolution: {integrity: sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.3': + resolution: {integrity: sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.3': + resolution: {integrity: sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.3': + resolution: {integrity: sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3': + resolution: {integrity: sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.3': + resolution: {integrity: sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.3': + resolution: {integrity: sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.3': + resolution: {integrity: sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.3': + resolution: {integrity: sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.3': + resolution: {integrity: sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + '@tailwindcss/oxide-win32-arm64-msvc@4.3.3': resolution: {integrity: sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==} engines: {node: '>= 20'} @@ -1076,6 +1790,10 @@ packages: '@tanstack/virtual-core@3.17.10': resolution: {integrity: sha512-eMorKWIi2nekElu+8DgSMJR4iR+sTDeh+W8VjKiMH79yUfEDJ3W9aL/96/wwbxNjyV6EfN39HKszSxjd90zAqQ==} + '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': + resolution: {integrity: sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==} + engines: {node: '>=12'} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1096,6 +1814,12 @@ packages: '@types/react@19.3.0': resolution: {integrity: sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==} + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@typescript/typescript-aix-ppc64@7.0.2': resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} engines: {node: '>=16.20.0'} @@ -1246,6 +1970,14 @@ packages: '@vitest/spy@5.0.1': resolution: {integrity: sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q==} + acorn@8.19.0: + resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1253,17 +1985,97 @@ packages: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + babel-plugin-polyfill-corejs2@0.4.17: + resolution: {integrity: sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.14.2: + resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.8: + resolution: {integrity: sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.11.34: + resolution: {integrity: sha512-Aqr8mA1kIvyJ3UY5YAY7ib8LSo1DmDMbMnVqGPtsKW1LxaFQFwn9Eb3yvq2fUKJsRvaXjnAV7R5osqETapzNiA==} + engines: {node: '>=6.0.0'} + hasBin: true + blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + brace-expansion@2.1.7: + resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==} + + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} + + browserslist@4.29.5: + resolution: {integrity: sha512-khTmKu5NGYTotqcYo8Bew2k4RAE9PuXRSFRXG2lQBrCEQHyeQ/vX6gKMxfokwywBsLiXZMnHsmWMG1pnO8J7uQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + cac@7.0.0: resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} engines: {node: '>=20.19.0'} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + caniuse-lite@1.0.30001819: + resolution: {integrity: sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -1290,6 +2102,13 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-es@3.1.1: resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} @@ -1297,6 +2116,10 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} + core-js-compat@3.51.0: + resolution: {integrity: sha512-52r6fkXgxv4GLMjmFcMWRx7X9bw071xTTdrDcdyPGRqU2oE+9pCG0bAOMifSBdLw0JRBZKEF0q3B55qPlLuE8g==} + engines: {node: '>=6.4.0'} + crelt@1.0.7: resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==} @@ -1304,9 +2127,50 @@ packages: resolution: {integrity: sha512-OHTec7+5nosN4UsitwgBDQ0bofGZF3H4pLqgqa93NR3nExNlhtVHcO2JY3jRognrFH8Bl9oAooT0hyCjSyOU+Q==} hasBin: true + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypto-random-string@2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -1321,6 +2185,21 @@ packages: discontinuous-range@1.0.0: resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} + dompurify@3.4.17: + resolution: {integrity: sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.467: + resolution: {integrity: sha512-Nw9X371rtZmYcBSnEijnCDONILjs93biCZO34juC7MBOmK8E+GFnyD4T/WvyrlJkppul7oxg6TwvtKst9SODTw==} + enhanced-resolve@5.25.1: resolution: {integrity: sha512-nGXts5znJzmWPu+mIE9izCOzdg63oJca2mDzGWWTth7sr4aCToKcoyFVBQwN75Ij5Pf6p510EwkTqViTRzDV+w==} engines: {node: '>=10.13.0'} @@ -1328,17 +2207,60 @@ packages: error-stack-parser-es@1.0.5: resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + es-abstract-get@1.0.0: + resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} + engines: {node: '>= 0.4'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.4: + resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} + engines: {node: '>= 0.4'} + esbuild@0.28.1: resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eta@4.6.0: + resolution: {integrity: sha512-lW6is4T1NFOYnmqGZIfvixqj7A7sSvScF+DN8EK6K58xI5MZ5UvYe0GjopxOXQtZvUn4eDdVuZ8XSoYWTMEKwA==} + engines: {node: '>=20'} + expect-type@1.4.0: resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} @@ -1347,6 +2269,15 @@ packages: resolution: {integrity: sha512-iK2f+YrcmoeGqk6fA0ea2bptcu/itMIm4NfEozq6N25+aG6h7s5HZbB/k1aV7b5w5sFLMCbbtRUsTVR+BgC3xw==} engines: {node: '>=12.17.0'} + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-uri@3.1.9: + resolution: {integrity: sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1356,61 +2287,302 @@ packages: picomatch: optional: true + filelist@1.0.6: + resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.2.0: + resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} + get-own-enumerable-property-symbols@3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + glob@11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + idb-keyval@6.3.0: resolution: {integrity: sha512-um+2dgAWmYsu615EXpWVwSmapJhON0G43t3Ka/EVaohzPQXSMqKEqeDK/oIW3Ow+BXaF2PvSc+oBTFp793A5Ow==} - jiti@2.7.0: - resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} - hasBin: true + idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} - jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} - lightningcss-android-arm64@1.32.0: - resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [android] + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} - lightningcss-android-arm64@1.33.0: - resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [android] + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} - lightningcss-darwin-arm64@1.32.0: - resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} - lightningcss-darwin-arm64@1.33.0: - resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} - lightningcss-darwin-x64@1.32.0: - resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] + is-core-module@2.17.0: + resolution: {integrity: sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-document.all@1.0.0: + resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} + engines: {node: '>= 0.4'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-obj@1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-regexp@1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@4.2.3: + resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} + engines: {node: 20 || >=22} + + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jsonfile@6.2.1: + resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] lightningcss-darwin-x64@1.33.0: resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} @@ -1530,6 +2702,16 @@ packages: resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} engines: {node: '>= 12.0.0'} + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lru-cache@11.5.3: + resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lucide-react@1.47.0: resolution: {integrity: sha512-o8C23aXpNQypRY73W7fW02EyvWJinEMXgKeGjFoKym0zj3Q73hD97A1IQps1g8C14HTGsOpZL0Am+xjfgFZAbg==} peerDependencies: @@ -1541,13 +2723,32 @@ packages: magic-string@1.4.1: resolution: {integrity: sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + miniflare@5.20260921.0-alpha: resolution: {integrity: sha512-vHH/unOYvV2jA1Q9SdkmzrQhhMoksdwg5jegu6ZeKaaRzgxZhVbt1NdTpQjHF2VTgiBjgP8SiUlUMfruB3N3SQ==} engines: {node: '>=22.0.0'} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} + + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + moo@0.5.3: resolution: {integrity: sha512-m2fmM2dDm7GZQsY7KK2cme8agi+AAljILjQnof7p1ZMDe6dQ4bdnSMx0cPppudoeNv5hEFQirN6u+O4fDE0IWA==} + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nanoid@3.3.19: resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1557,10 +2758,30 @@ packages: resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} hasBin: true + node-releases@2.0.60: + resolution: {integrity: sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==} + engines: {node: '>=18'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + obug@2.2.1: resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} engines: {node: '>=12.20.0'} + own-keys@1.0.2: + resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} + engines: {node: '>= 0.4'} + oxlint@1.85.0: resolution: {integrity: sha512-bc26s97nuvPj1ViyPsqmKecVkUWFMEdtayO8MaQ6oiLfs1pj94cQlZZhrh4BPNlr9HQosjhIlwgZKsfcwmcNgg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1574,6 +2795,20 @@ packages: vite-plus: optional: true + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} @@ -1587,6 +2822,10 @@ packages: resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + postcss@8.5.28: resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} @@ -1596,6 +2835,14 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-bytes@5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + + pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + pure-rand@8.4.2: resolution: {integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==} @@ -1655,6 +2902,41 @@ packages: resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} engines: {node: '>=0.10.0'} + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regenerate-unicode-properties@10.3.0: + resolution: {integrity: sha512-9ns8odR8e9q3otKeHj3DE80jZYKg6zVeZKE+zQmRKXFoVUcuiTGeO6iusDhPRoIQQPVPRvH+SfBhrTud4eczuw==} + engines: {node: '>=4'} + + regenerate@1.5.0: + resolution: {integrity: sha512-1s+CxXjJ71i7LNVy8nYwg47XV4y6lAnOEhsMtSKq3WpmPfhl9LuKlTqKIn/3yUpE8lBz+W+TT88Bxyhnr0U4yw==} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + regexpu-core@6.5.3: + resolution: {integrity: sha512-E8VgUoSK+LMQrLboDxavm76MTBGrQhx9RiNuomRmYnZ10Uw12HrI8fkxOOncktb2hBpzIZ9SV/347euQHG5z+g==} + engines: {node: '>=4'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.4: + resolution: {integrity: sha512-EWV80YgFaVgFrjGCAsV7oybp4GOxbls5RzBg8OB0nKEVXsBJR1JlV8Di1JhCROg3MzTfe1RI8yitIm1eOs68iA==} + hasBin: true + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve@1.22.13: + resolution: {integrity: sha512-Dj/cW9zBV2aRj6XzCxuGIs3ESZKv02jixne3q6jFZYZ3/Tg09gHHviSMDL4kObh9pqSahdRHEnDoaP2gn8YLhA==} + engines: {node: '>= 0.4'} + hasBin: true + ret@0.1.15: resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} engines: {node: '>=0.12'} @@ -1664,14 +2946,51 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rollup@4.66.0: + resolution: {integrity: sha512-Onq6/qO4ARhxIs+jYiHE0pYehadLyMECAiGEqQ+0/aWND3+2PmifJIIqBLYtuBODqHTlOUEQFtIVPyCy0Lck7A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + scheduler@0.28.0: resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true + serialize-javascript@7.1.4: + resolution: {integrity: sha512-FqlEuoTq/zRmA4Lg6aOqyH3lCHbUtCB0bmHCeFniX2GBdDqMJM3FwdvEhlE5Pim1dlt2FfBdVPmUNNiGhhKqpw==} + engines: {node: '>=20.0.0'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + sharp@0.35.4: resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==} engines: {node: '>=20.9.0'} @@ -1681,9 +3000,41 @@ packages: '@types/node': optional: true + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + smob@1.6.2: + resolution: {integrity: sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==} + engines: {node: '>=20.0.0'} + smol-toml@1.9.1: resolution: {integrity: sha512-j7WyPdf0CJoutMzMoPLF/bVXHtKZIT9Rb4eGeEbWw8X2t5oirjPgpL+OpaENZrILYaXqYwnVH+qgUwVbl+pUJQ==} engines: {node: '>= 18'} @@ -1692,6 +3043,17 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.8.0: + resolution: {integrity: sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==} + engines: {node: '>= 12'} + sql-formatter@15.9.0: resolution: {integrity: sha512-3A/N+v+mA5/PDU+FlO4uTFE0L/Mmg63c/Ny161Qv2b1Mxp9IX8War+LLmq17LA/Kgu4KXAR7iutK2JaHWJMMtw==} hasBin: true @@ -1702,6 +3064,34 @@ packages: std-env@4.2.0: resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.1.0: + resolution: {integrity: sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + + strip-comments@2.0.1: + resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} + engines: {node: '>=10'} + style-mod@4.1.4: resolution: {integrity: sha512-XXWIQt633/EpAFx8aZDOTjBzrCaGmhvEQlQo6MVPfa2OzO2cWo+4hV9h+6UkHYlXGfy+ODXKUdP7Pthmcu5ATw==} @@ -1709,6 +3099,10 @@ packages: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + tailwindcss@4.3.3: resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} @@ -1716,6 +3110,19 @@ packages: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} + temp-dir@2.0.0: + resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} + engines: {node: '>=8'} + + tempy@0.6.0: + resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} + engines: {node: '>=10'} + + terser@5.51.2: + resolution: {integrity: sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==} + engines: {node: '>=10'} + hasBin: true + tinybench@6.1.4: resolution: {integrity: sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==} engines: {node: '>=20.0.0'} @@ -1731,11 +3138,35 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + type-fest@0.16.0: + resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} + engines: {node: '>=10'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.5: + resolution: {integrity: sha512-0FHJvLPqZ7KJzp17O13jfsAjsqazgrxBu2zEK95PmUz8lv2+GjRuxUInCr2Rk9Dms3ihN21zJ929ZO43yJ95QQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} + engines: {node: '>= 0.4'} + typescript@7.0.2: resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} engines: {node: '>=16.20.0'} hasBin: true + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} @@ -1746,6 +3177,40 @@ packages: unenv@2.0.0-rc.24: resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + unique-string@2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + + update-browserslist-db@1.3.4: + resolution: {integrity: sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uqr@0.1.3: resolution: {integrity: sha512-0rjE8iEJe4YmT9TOhwsZtqCMRLc5DXZUI2UEYUUg63ikBkqqE5EYWaI0etFe/5KUcmcYwLih2RND1kq+hrUJXA==} @@ -1774,6 +3239,18 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + vite-plugin-pwa@2.0.0: + resolution: {integrity: sha512-6G9dsPKT9QKYpeTuTLJM28zZL3HsXUTUM/QUZBEgQk2jbfrPJDq2i8gTewAKqi1c9Mt6MiidrL4SFI+HthiQMg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@vite-pwa/assets-generator': ^1.0.0 || ^2.0.0 + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + workbox-build: ^7.4.1 + workbox-window: ^7.4.1 + peerDependenciesMeta: + '@vite-pwa/assets-generator': + optional: true + vite@8.3.0: resolution: {integrity: sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1861,11 +3338,81 @@ packages: w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.24: + resolution: {integrity: sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} hasBin: true + workbox-background-sync@7.4.1: + resolution: {integrity: sha512-HhT7KE8tOWDm02wRNshXUnUPofMlhenF2DBdUnDPOubhizzPeItkYTmAB6td1Z2cjYPa98vzEiPLEuzn5hN66g==} + + workbox-broadcast-update@7.4.1: + resolution: {integrity: sha512-uAlgslKLvbQY+suirIdnBCSYrcgBhjp81Nj4l1lj/Jmj0MJO2CJERnCJjT0GFVwmReV0N+zs78K6gqd5gr9/+A==} + + workbox-build@7.4.1: + resolution: {integrity: sha512-SDhxIvEAde9Gy/5w4Yo1Jh/M49Z0qE3q0oteyE8zGq0DScxFqVBcCtIXFuLtmtxRQZCMbf0prco4VyEu3KBQuw==} + engines: {node: '>=20.0.0'} + + workbox-cacheable-response@7.4.1: + resolution: {integrity: sha512-8xaFoJdDc2OjrlbbL3gEeBO1WKcMwRqwLRupgqahYXu75yXajPLuwrbXMrIGZuWYXrQwk0xDjOxZ/ujCy/oJYw==} + + workbox-core@7.4.1: + resolution: {integrity: sha512-DT+vu46eh/2vRsSHTY4Xmc32Z1rr9PRlQUXr1Dx30ZuXRWwOsvZgGgcwxcasubQLQmbTNYZjv44LkBAQ4tT5tQ==} + + workbox-expiration@7.4.1: + resolution: {integrity: sha512-lRKUF7b+OGbeXkQk1s6MHXOa3d7Xxf7Of31W6c6hCfipfIyrtdWZ89stq21AHZMaoG7VNFoHply4Ox+rU31TWg==} + + workbox-google-analytics@7.4.1: + resolution: {integrity: sha512-Mks1JwLEt++ZAkF6sS1OpSh9RtAMIsiDgRpK+codiHGIPXeaUOgi4cPc3GFadUl8V5QPeypEk8Oxgl3HlwVzHw==} + + workbox-navigation-preload@7.4.1: + resolution: {integrity: sha512-C4KVsjPcYKJOhr631AxR9XoG2rLF3QiTk5aMv36MXOjtWvm8axwNFAtKUPGsWUwLXXAMgYM1En7fsvndaXeXRQ==} + + workbox-precaching@7.4.1: + resolution: {integrity: sha512-cdr/9qByww7yzEp7zg/qI4ukUrrNjQLgN+ONQRpjy/VqGQXwkgHwr00KksGJK8v0VifwDXBb8a4cWNZH71jn3Q==} + + workbox-range-requests@7.4.1: + resolution: {integrity: sha512-7i2oxAUE82gHdAJBCAQ04JzNOdRPqzuOzGfoUyJpFSmeqBNYGPrAH8GPoPjUQTfp+NycwrD2H68VtuF8qxv0vQ==} + + workbox-recipes@7.4.1: + resolution: {integrity: sha512-gnbVfmV4/TtmQaM4x9AtuXhcdstJsep3XMVeztOrQVPT+R6+6DeBjGTCQ7fFCXm+4GEHUA5VEBTyi5+4gWGeog==} + + workbox-routing@7.4.1: + resolution: {integrity: sha512-yubJGErZOusuidAenaL5ypfhQOa7urxP/f8E0ws7FPb4039RiWXUWBAyUkmUoOL/BcQGen3h0J8872d51IYxtA==} + + workbox-strategies@7.4.1: + resolution: {integrity: sha512-GZxpaw9NbmOelj7667uZ2kpk5BFpOGbO4X0qjwh5ls8XQ8C+Lha5LQchTiUzsTFSS+NlUpftYAyOVXvQUrcqOQ==} + + workbox-streams@7.4.1: + resolution: {integrity: sha512-HWWtraKUbJknd9kgqGcpQ3G114HOPYvqs8HaJMDs2ebLNAimDkVDaWfAXE6Ybl+m8U6KsCE6pWyLYuigWmnAXw==} + + workbox-sw@7.4.1: + resolution: {integrity: sha512-fez5f2DUlDJWTFYkCWQpY10N8gtztd849NswCbVFk0QlcSM4HT5A8x4g4ii650yem4I8tHY0R7JZahwp3ltIPw==} + + workbox-window@7.4.1: + resolution: {integrity: sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A==} + workerd@1.20260921.1: resolution: {integrity: sha512-4HyG7G1W4ksa6tUZ8bV2jxDRWuL5PXnHm9+Z1sjFPb9OZNoYtXz4y7QQRh4ibi0BF/lOmlAVjbhkUqsAVZuUKA==} engines: {node: '>=16'} @@ -1893,6 +3440,9 @@ packages: utf-8-validate: optional: true + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@2.9.1: resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} engines: {node: '>= 14.6'} @@ -1906,55 +3456,724 @@ packages: snapshots: - '@cloudflare/kv-asset-handler@0.5.0': {} - - '@cloudflare/unenv-preset@2.16.2(unenv@2.0.0-rc.24)(workerd@1.20260921.1)': + '@apideck/better-ajv-errors@0.3.7(ajv@8.20.0)': dependencies: - unenv: 2.0.0-rc.24 - optionalDependencies: - workerd: 1.20260921.1 + ajv: 8.20.0 + jsonpointer: 5.0.1 + leven: 3.1.0 - '@cloudflare/workerd-darwin-64@1.20260921.1': - optional: true + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 - '@cloudflare/workerd-darwin-arm64@1.20260921.1': - optional: true + '@babel/compat-data@7.29.7': {} - '@cloudflare/workerd-linux-64@1.20260921.1': - optional: true + '@babel/core@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helpers': 7.29.10 + '@babel/parser': 7.29.11 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.10(supports-color@10.2.2) + '@babel/types': 7.29.11 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@10.2.2) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color - '@cloudflare/workerd-linux-arm64@1.20260921.1': - optional: true + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.11 + '@babel/types': 7.29.11 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 - '@cloudflare/workerd-windows-64@1.20260921.1': - optional: true + '@babel/helper-annotate-as-pure@7.29.7': + dependencies: + '@babel/types': 7.29.11 - '@codemirror/autocomplete@6.20.3': + '@babel/helper-compilation-targets@7.29.7': dependencies: - '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.5 - '@codemirror/view': 6.43.12 - '@lezer/common': 1.5.2 + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.29.5 + lru-cache: 5.1.1 + semver: 6.3.1 - '@codemirror/commands@6.11.1': + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.5 - '@codemirror/view': 6.43.12 - '@lezer/common': 1.5.2 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/traverse': 7.29.10(supports-color@10.2.2) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color - '@codemirror/language@6.12.4': + '@babel/helper-create-regexp-features-plugin@7.29.10(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@codemirror/state': 6.7.5 - '@codemirror/view': 6.43.12 - '@lezer/common': 1.5.2 - '@lezer/highlight': 1.2.3 - '@lezer/lr': 1.4.10 - style-mod: 4.1.4 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + regexpu-core: 6.5.3 + semver: 6.3.1 - '@codemirror/lint@6.9.7': + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@codemirror/state': 6.7.5 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + debug: 4.4.3(supports-color@10.2.2) + lodash.debounce: 4.0.8 + resolve: 1.22.13 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-member-expression-to-functions@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/traverse': 7.29.10(supports-color@10.2.2) + '@babel/types': 7.29.11 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/traverse': 7.29.10(supports-color@10.2.2) + '@babel/types': 7.29.11 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.10(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.29.7': + dependencies: + '@babel/types': 7.29.11 + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-wrap-function': 7.29.7(supports-color@10.2.2) + '@babel/traverse': 7.29.10(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) + '@babel/helper-optimise-call-expression': 7.29.7 + '@babel/traverse': 7.29.10(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/traverse': 7.29.10(supports-color@10.2.2) + '@babel/types': 7.29.11 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.11': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helper-wrap-function@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.10(supports-color@10.2.2) + '@babel/types': 7.29.11 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.29.10': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.11 + + '@babel/parser@7.29.11': + dependencies: + '@babel/types': 7.29.11 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.10(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.10(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + + '@babel/plugin-syntax-import-assertions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.10(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-arrow-functions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/traverse': 7.29.10(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-block-scoping@7.29.11(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.29.11(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/traverse': 7.29.10(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/template': 7.29.7 + + '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.10(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.10(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-duplicate-keys@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.10(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-dynamic-import@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-explicit-resource-management@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-export-namespace-from@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/traverse': 7.29.10(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-member-expression-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-modules-amd@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.29.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.10(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.10(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-new-target@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-numeric-separator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/traverse': 7.29.10(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-annotate-as-pure': 7.29.7 + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-regenerator@7.29.8(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-regexp-modifiers@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.10(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-reserved-words@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-shorthand-properties@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-spread@7.29.11(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-template-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-typeof-symbol@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-escapes@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-property-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.10(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.10(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-unicode-sets-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.10(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-import-assertions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-block-scoped-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-block-scoping': 7.29.11(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-classes': 7.29.11(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-computed-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-dotall-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-duplicate-keys': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-dynamic-import': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-explicit-resource-management': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-exponentiation-operator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-function-name': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-json-strings': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-member-expression-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-modules-systemjs': 7.29.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-modules-umd': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-new-target': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-numeric-separator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-object-super': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-property-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-regenerator': 7.29.8(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-regexp-modifiers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-reserved-words': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-spread': 7.29.11(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-typeof-symbol': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-escapes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-property-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-sets-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.7(supports-color@10.2.2)) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + core-js-compat: 3.51.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/types': 7.29.11 + esutils: 2.0.3 + + '@babel/runtime@7.29.10': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.11 + '@babel/types': 7.29.11 + + '@babel/traverse@7.29.10(supports-color@10.2.2)': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.11 + '@babel/template': 7.29.7 + '@babel/types': 7.29.11 + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.11': + dependencies: + '@babel/helper-string-parser': 7.29.11 + '@babel/helper-validator-identifier': 7.29.7 + + '@cloudflare/kv-asset-handler@0.5.0': {} + + '@cloudflare/unenv-preset@2.16.2(unenv@2.0.0-rc.24)(workerd@1.20260921.1)': + dependencies: + unenv: 2.0.0-rc.24 + optionalDependencies: + workerd: 1.20260921.1 + + '@cloudflare/workerd-darwin-64@1.20260921.1': + optional: true + + '@cloudflare/workerd-darwin-arm64@1.20260921.1': + optional: true + + '@cloudflare/workerd-linux-64@1.20260921.1': + optional: true + + '@cloudflare/workerd-linux-arm64@1.20260921.1': + optional: true + + '@cloudflare/workerd-windows-64@1.20260921.1': + optional: true + + '@codemirror/autocomplete@6.20.3': + dependencies: + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.5 + '@codemirror/view': 6.43.12 + '@lezer/common': 1.5.2 + + '@codemirror/commands@6.11.1': + dependencies: + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.5 + '@codemirror/view': 6.43.12 + '@lezer/common': 1.5.2 + + '@codemirror/language@6.12.4': + dependencies: + '@codemirror/state': 6.7.5 + '@codemirror/view': 6.43.12 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + style-mod: 4.1.4 + + '@codemirror/lint@6.9.7': + dependencies: + '@codemirror/state': 6.7.5 '@codemirror/view': 6.43.12 crelt: 1.0.7 @@ -2176,6 +4395,8 @@ snapshots: '@img/sharp-win32-x64@0.35.4': optional: true + '@isaacs/cliui@9.0.0': {} + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -2188,6 +4409,11 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/sourcemap-codec@1.6.0': {} '@jridgewell/trace-mapping@0.3.31': @@ -2212,6 +4438,9 @@ snapshots: '@marijn/find-cluster-break@1.0.4': {} + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + '@oxc-project/types@0.150.0': {} '@oxlint/binding-android-arm-eabi@1.85.0': @@ -2469,11 +4698,129 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} - '@sindresorhus/is@7.2.0': {} + '@rollup/plugin-babel@6.1.0(@babel/core@7.29.7(supports-color@10.2.2))(rollup@4.66.0)(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) + '@rollup/pluginutils': 5.5.1(rollup@4.66.0) + optionalDependencies: + rollup: 4.66.0 + transitivePeerDependencies: + - supports-color - '@speed-highlight/core@1.2.24': {} + '@rollup/plugin-node-resolve@16.0.3(rollup@4.66.0)': + dependencies: + '@rollup/pluginutils': 5.5.1(rollup@4.66.0) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.13 + optionalDependencies: + rollup: 4.66.0 - '@tailwindcss/node@4.3.3': + '@rollup/plugin-replace@6.0.3(rollup@4.66.0)': + dependencies: + '@rollup/pluginutils': 5.5.1(rollup@4.66.0) + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.66.0 + + '@rollup/plugin-terser@1.0.2(rollup@4.66.0)': + dependencies: + serialize-javascript: 7.1.4 + smob: 1.6.2 + terser: 5.51.2 + optionalDependencies: + rollup: 4.66.0 + + '@rollup/pluginutils@5.5.1(rollup@4.66.0)': + dependencies: + '@types/estree': 1.0.9 + estree-walker: 2.0.2 + picomatch: 4.0.7 + optionalDependencies: + rollup: 4.66.0 + + '@rollup/rollup-android-arm-eabi@4.66.0': + optional: true + + '@rollup/rollup-android-arm64@4.66.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.66.0': + optional: true + + '@rollup/rollup-darwin-x64@4.66.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.66.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.66.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.66.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.66.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.66.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.66.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.66.0': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.66.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.66.0': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.66.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.66.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.66.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.66.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.66.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.66.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.66.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.66.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.66.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.66.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.66.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.66.0': + optional: true + + '@sindresorhus/is@7.2.0': {} + + '@speed-highlight/core@1.2.24': {} + + '@tailwindcss/node@4.3.3': dependencies: '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.25.1 @@ -2534,12 +4881,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 - '@tailwindcss/vite@4.3.3(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1))': + '@tailwindcss/vite@4.3.3(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))': dependencies: '@tailwindcss/node': 4.3.3 '@tailwindcss/oxide': 4.3.3 tailwindcss: 4.3.3 - vite: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1) + vite: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) '@tanstack/react-virtual@3.14.12(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: @@ -2549,6 +4896,13 @@ snapshots: '@tanstack/virtual-core@3.17.10': {} + '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': + dependencies: + ejs: 3.1.10 + json5: 2.2.3 + magic-string: 0.30.21 + string.prototype.matchall: 4.1.0 + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -2570,6 +4924,10 @@ snapshots: dependencies: csstype: 3.2.3 + '@types/resolve@1.20.2': {} + + '@types/trusted-types@2.0.7': {} + '@typescript/typescript-aix-ppc64@7.0.2': optional: true @@ -2630,151 +4988,678 @@ snapshots: '@typescript/typescript-win32-x64@7.0.2': optional: true - '@vitejs/plugin-react@6.1.1(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1))': + '@vitejs/plugin-react@6.1.1(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1) + vite: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) - '@vitest/mocker@5.0.1(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1))': + '@vitest/mocker@5.0.1(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))': dependencies: '@jridgewell/trace-mapping': 0.3.31 '@vitest/spy': 5.0.1 estree-walker: 3.0.3 magic-string: 1.4.1 optionalDependencies: - vite: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1) + vite: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) '@vitest/spy@5.0.1': {} + acorn@8.19.0: {} + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.9 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + argparse@2.0.1: {} aria-hidden@1.2.6: dependencies: tslib: 2.8.1 + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + assertion-error@2.0.1: {} + async-function@1.0.0: {} + + async@3.2.6: {} + + at-least-node@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + core-js-compat: 3.51.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.11.34: {} + blake3-wasm@2.1.5: {} + brace-expansion@2.1.7: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.12: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.29.5: + dependencies: + baseline-browser-mapping: 2.11.34 + caniuse-lite: 1.0.30001819 + electron-to-chromium: 1.5.467 + node-releases: 2.0.60 + update-browserslist-db: 1.3.4(browserslist@4.29.5) + + buffer-from@1.1.2: {} + cac@7.0.0: {} + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + caniuse-lite@1.0.30001819: {} + chai@6.2.2: {} - change-case@5.4.4: {} + change-case@5.4.4: {} + + clsx@2.1.1: {} + + cmdk@1.1.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-dialog': 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + codemirror@6.0.2: + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/commands': 6.11.1 + '@codemirror/language': 6.12.4 + '@codemirror/lint': 6.9.7 + '@codemirror/search': 6.7.2 + '@codemirror/state': 6.7.5 + '@codemirror/view': 6.43.12 + + comlink@4.4.2: {} + + commander@2.20.3: {} + + common-tags@1.8.2: {} + + convert-source-map@2.0.0: {} + + cookie-es@3.1.1: {} + + cookie@1.1.1: {} + + core-js-compat@3.51.0: + dependencies: + browserslist: 4.29.5 + + crelt@1.0.7: {} + + cronstrue@3.32.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-random-string@2.0.0: {} + + csstype@3.2.3: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@4.4.3(supports-color@10.2.2): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 + + deepmerge@4.3.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + diff@9.0.0: {} + + discontinuous-range@1.0.0: {} + + dompurify@3.4.17: + optionalDependencies: + '@types/trusted-types': 2.0.7 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ejs@3.1.10: + dependencies: + jake: 10.9.4 + + electron-to-chromium@1.5.467: {} + + enhanced-resolve@5.25.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + error-stack-parser-es@1.0.5: {} + + es-abstract-get@1.0.0: + dependencies: + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + is-callable: 1.2.7 + object-inspect: 1.13.4 + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.4 + function.prototype.name: 1.2.0 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.2 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.5 + typed-array-length: 1.0.8 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.24 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.3.2: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + es-to-primitive@1.3.4: + dependencies: + es-abstract-get: 1.0.0 + es-define-property: 1.0.1 + es-errors: 1.3.0 + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 + + escalade@3.2.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + eta@4.6.0: {} + + expect-type@1.4.0: {} + + fast-check@4.10.2: + dependencies: + pure-rand: 8.4.2 + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-uri@3.1.9: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + filelist@1.0.6: + dependencies: + minimatch: 5.1.9 + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.2.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + es-define-property: 1.0.1 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + hasown: 2.0.4 + is-callable: 1.2.7 + is-document.all: 1.0.0 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-own-enumerable-property-symbols@3.0.2: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + glob@11.1.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.2.3 + minimatch: 10.2.6 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-bigints@1.1.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + idb-keyval@6.3.0: {} + + idb@7.1.1: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.4 + side-channel: 1.1.1 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} - clsx@2.1.1: {} + is-core-module@2.17.0: + dependencies: + hasown: 2.0.4 - cmdk@1.1.1(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + is-data-view@1.0.2: dependencies: - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-dialog': 1.1.23(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@radix-ui/react-id': 1.1.4(@types/react@19.3.0)(react@19.3.0) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - react: 19.3.0 - react-dom: 19.3.0(react@19.3.0) - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 - codemirror@6.0.2: + is-date-object@1.1.0: dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/commands': 6.11.1 - '@codemirror/language': 6.12.4 - '@codemirror/lint': 6.9.7 - '@codemirror/search': 6.7.2 - '@codemirror/state': 6.7.5 - '@codemirror/view': 6.43.12 + call-bound: 1.0.4 + has-tostringtag: 1.0.2 - comlink@4.4.2: {} + is-document.all@1.0.0: + dependencies: + call-bound: 1.0.4 - commander@2.20.3: {} + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 - cookie-es@3.1.1: {} + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 - cookie@1.1.1: {} + is-map@2.0.3: {} - crelt@1.0.7: {} + is-module@1.0.0: {} - cronstrue@3.32.0: {} + is-negative-zero@2.0.3: {} - csstype@3.2.3: {} + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 - detect-libc@2.1.2: {} + is-obj@1.0.1: {} - detect-node-es@1.1.0: {} + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 - diff@9.0.0: {} + is-regexp@1.0.0: {} - discontinuous-range@1.0.0: {} + is-set@2.0.3: {} - enhanced-resolve@5.25.1: + is-shared-array-buffer@1.0.4: dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.3 + call-bound: 1.0.4 - error-stack-parser-es@1.0.5: {} + is-stream@2.0.1: {} - es-module-lexer@2.3.2: {} + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 - esbuild@0.28.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.28.1 - '@esbuild/android-arm': 0.28.1 - '@esbuild/android-arm64': 0.28.1 - '@esbuild/android-x64': 0.28.1 - '@esbuild/darwin-arm64': 0.28.1 - '@esbuild/darwin-x64': 0.28.1 - '@esbuild/freebsd-arm64': 0.28.1 - '@esbuild/freebsd-x64': 0.28.1 - '@esbuild/linux-arm': 0.28.1 - '@esbuild/linux-arm64': 0.28.1 - '@esbuild/linux-ia32': 0.28.1 - '@esbuild/linux-loong64': 0.28.1 - '@esbuild/linux-mips64el': 0.28.1 - '@esbuild/linux-ppc64': 0.28.1 - '@esbuild/linux-riscv64': 0.28.1 - '@esbuild/linux-s390x': 0.28.1 - '@esbuild/linux-x64': 0.28.1 - '@esbuild/netbsd-arm64': 0.28.1 - '@esbuild/netbsd-x64': 0.28.1 - '@esbuild/openbsd-arm64': 0.28.1 - '@esbuild/openbsd-x64': 0.28.1 - '@esbuild/openharmony-arm64': 0.28.1 - '@esbuild/sunos-x64': 0.28.1 - '@esbuild/win32-arm64': 0.28.1 - '@esbuild/win32-ia32': 0.28.1 - '@esbuild/win32-x64': 0.28.1 + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 - estree-walker@3.0.3: + is-typed-array@1.1.15: dependencies: - '@types/estree': 1.0.9 + which-typed-array: 1.1.24 - expect-type@1.4.0: {} + is-weakmap@2.0.2: {} - fast-check@4.10.2: + is-weakref@1.1.1: dependencies: - pure-rand: 8.4.2 + call-bound: 1.0.4 - fdir@6.5.0(picomatch@4.0.7): - optionalDependencies: - picomatch: 4.0.7 + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 - fsevents@2.3.3: - optional: true + isarray@2.0.5: {} - get-nonce@1.0.1: {} + isexe@2.0.0: {} - graceful-fs@4.2.11: {} + jackspeak@4.2.3: + dependencies: + '@isaacs/cliui': 9.0.0 - idb-keyval@6.3.0: {} + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.6 + picocolors: 1.1.1 jiti@2.7.0: {} + js-tokens@4.0.0: {} + + jsesc@3.1.0: {} + + json-schema-traverse@1.0.0: {} + + json5@2.2.3: {} + jsonc-parser@3.3.1: {} + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonpointer@5.0.1: {} + kleur@4.1.5: {} + leven@3.1.0: {} + lightningcss-android-arm64@1.32.0: optional: true @@ -2873,6 +5758,14 @@ snapshots: lightningcss-win32-arm64-msvc: 1.33.0 lightningcss-win32-x64-msvc: 1.33.0 + lodash.debounce@4.0.8: {} + + lru-cache@11.5.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + lucide-react@1.47.0(react@19.3.0): dependencies: react: 19.3.0 @@ -2885,6 +5778,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 + math-intrinsics@1.1.0: {} + miniflare@5.20260921.0-alpha(@types/node@24.9.2): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -2898,8 +5793,20 @@ snapshots: - bufferutil - utf-8-validate + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.12 + + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.7 + + minipass@7.1.3: {} + moo@0.5.3: {} + ms@2.1.3: {} + nanoid@3.3.19: {} nearley@2.20.1: @@ -2909,8 +5816,30 @@ snapshots: railroad-diagrams: 1.0.0 randexp: 0.4.6 + node-releases@2.0.60: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + has-symbols: 1.1.0 + object-keys: 1.1.1 + obug@2.2.1: {} + own-keys@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + oxlint@1.85.0: optionalDependencies: '@oxlint/binding-android-arm-eabi': 1.85.0 @@ -2933,6 +5862,17 @@ snapshots: '@oxlint/binding-win32-ia32-msvc': 1.85.0 '@oxlint/binding-win32-x64-msvc': 1.85.0 + package-json-from-dist@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.3 + minipass: 7.1.3 + path-to-regexp@6.3.0: {} pathe@2.0.3: {} @@ -2941,6 +5881,8 @@ snapshots: picomatch@4.0.7: {} + possible-typed-array-names@1.1.0: {} + postcss@8.5.28: dependencies: nanoid: 3.3.19 @@ -2949,6 +5891,10 @@ snapshots: prettier@3.9.10: {} + pretty-bytes@5.6.0: {} + + pretty-bytes@6.1.1: {} + pure-rand@8.4.2: {} railroad-diagrams@1.0.0: {} @@ -3000,6 +5946,56 @@ snapshots: react@19.3.0: {} + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regenerate-unicode-properties@10.3.0: + dependencies: + regenerate: 1.5.0 + + regenerate@1.5.0: {} + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + regexpu-core@6.5.3: + dependencies: + regenerate: 1.5.0 + regenerate-unicode-properties: 10.3.0 + regjsgen: 0.8.0 + regjsparser: 0.13.4 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + regjsgen@0.8.0: {} + + regjsparser@0.13.4: + dependencies: + jsesc: 3.1.0 + + require-from-string@2.0.2: {} + + resolve@1.22.13: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.17.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + ret@0.1.15: {} rolldown@1.2.9: @@ -3023,10 +6019,87 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.9 '@rolldown/binding-win32-x64-msvc': 1.2.9 + rollup@4.66.0: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.66.0 + '@rollup/rollup-android-arm64': 4.66.0 + '@rollup/rollup-darwin-arm64': 4.66.0 + '@rollup/rollup-darwin-x64': 4.66.0 + '@rollup/rollup-freebsd-arm64': 4.66.0 + '@rollup/rollup-freebsd-x64': 4.66.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.66.0 + '@rollup/rollup-linux-arm-musleabihf': 4.66.0 + '@rollup/rollup-linux-arm64-gnu': 4.66.0 + '@rollup/rollup-linux-arm64-musl': 4.66.0 + '@rollup/rollup-linux-loong64-gnu': 4.66.0 + '@rollup/rollup-linux-loong64-musl': 4.66.0 + '@rollup/rollup-linux-ppc64-gnu': 4.66.0 + '@rollup/rollup-linux-ppc64-musl': 4.66.0 + '@rollup/rollup-linux-riscv64-gnu': 4.66.0 + '@rollup/rollup-linux-riscv64-musl': 4.66.0 + '@rollup/rollup-linux-s390x-gnu': 4.66.0 + '@rollup/rollup-linux-x64-gnu': 4.66.0 + '@rollup/rollup-linux-x64-musl': 4.66.0 + '@rollup/rollup-openbsd-x64': 4.66.0 + '@rollup/rollup-openharmony-arm64': 4.66.0 + '@rollup/rollup-win32-arm64-msvc': 4.66.0 + '@rollup/rollup-win32-ia32-msvc': 4.66.0 + '@rollup/rollup-win32-x64-gnu': 4.66.0 + '@rollup/rollup-win32-x64-msvc': 4.66.0 + fsevents: 2.3.3 + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + scheduler@0.28.0: {} + semver@6.3.1: {} + semver@7.8.5: {} + serialize-javascript@7.1.4: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + sharp@0.35.4(@types/node@24.9.2): dependencies: '@img/colour': 1.1.0 @@ -3060,12 +6133,59 @@ snapshots: '@img/sharp-win32-x64': 0.35.4 '@types/node': 24.9.2 + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} + signal-exit@4.1.0: {} + + smob@1.6.2: {} + smol-toml@1.9.1: {} source-map-js@1.2.1: {} + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.8.0: {} + sql-formatter@15.9.0: dependencies: argparse: 2.0.1 @@ -3075,14 +6195,85 @@ snapshots: std-env@4.2.0: {} + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.matchall@4.1.0: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.1 + + string.prototype.trim@1.2.11: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.2 + has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 + + string.prototype.trimend@1.0.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.2 + + stringify-object@3.3.0: + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + + strip-comments@2.0.1: {} + style-mod@4.1.4: {} supports-color@10.2.2: {} + supports-preserve-symlinks-flag@1.0.0: {} + tailwindcss@4.3.3: {} tapable@2.3.3: {} + temp-dir@2.0.0: {} + + tempy@0.6.0: + dependencies: + is-stream: 2.0.1 + temp-dir: 2.0.0 + type-fest: 0.16.0 + unique-string: 2.0.0 + + terser@5.51.2: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.19.0 + commander: 2.20.3 + source-map-support: 0.5.21 + tinybench@6.1.4: {} tinyexec@1.3.0: {} @@ -3094,6 +6285,40 @@ snapshots: tslib@2.8.1: {} + type-fest@0.16.0: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.5: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.8: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + typescript@7.0.2: optionalDependencies: '@typescript/typescript-aix-ppc64': 7.0.2 @@ -3117,6 +6342,13 @@ snapshots: '@typescript/typescript-win32-arm64': 7.0.2 '@typescript/typescript-win32-x64': 7.0.2 + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + undici-types@7.16.0: {} undici@7.29.0: {} @@ -3125,6 +6357,31 @@ snapshots: dependencies: pathe: 2.0.3 + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + unique-string@2.0.0: + dependencies: + crypto-random-string: 2.0.0 + + universalify@2.0.1: {} + + upath@1.2.0: {} + + update-browserslist-db@1.3.4(browserslist@4.29.5): + dependencies: + browserslist: 4.29.5 + escalade: 3.2.0 + picocolors: 1.1.1 + uqr@0.1.3: {} use-callback-ref@1.3.3(@types/react@19.3.0)(react@19.3.0): @@ -3142,13 +6399,13 @@ snapshots: optionalDependencies: '@types/react': 19.3.0 - vite-node@6.0.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1): + vite-node@6.0.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1): dependencies: cac: 7.0.0 es-module-lexer: 2.3.2 obug: 2.2.1 pathe: 2.0.3 - vite: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1) + vite: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) transitivePeerDependencies: - '@types/node' - '@vitejs/devtools' @@ -3163,7 +6420,18 @@ snapshots: - tsx - yaml - vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1): + vite-plugin-pwa@2.0.0(supports-color@10.2.2)(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1))(workbox-build@7.4.1(supports-color@10.2.2))(workbox-window@7.4.1): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + pretty-bytes: 6.1.1 + tinyglobby: 0.2.17 + vite: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) + workbox-build: 7.4.1(supports-color@10.2.2) + workbox-window: 7.4.1 + transitivePeerDependencies: + - supports-color + + vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 @@ -3175,12 +6443,13 @@ snapshots: esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.7.0 + terser: 5.51.2 yaml: 2.9.1 - vitest@5.0.1(@types/node@24.9.2)(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1)): + vitest@5.0.1(@types/node@24.9.2)(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.1(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1)) + '@vitest/mocker': 5.0.1(vite@8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) chai: 6.2.2 es-module-lexer: 2.3.2 expect-type: 1.4.0 @@ -3191,7 +6460,7 @@ snapshots: tinybench: 6.1.4 tinyexec: 1.3.0 tinyglobby: 0.2.17 - vite: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.1) + vite: 8.3.0(@types/node@24.9.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.9.2 @@ -3200,11 +6469,169 @@ snapshots: w3c-keyname@2.2.8: {} + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.2.0 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.24 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.24: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 + workbox-background-sync@7.4.1: + dependencies: + idb: 7.1.1 + workbox-core: 7.4.1 + + workbox-broadcast-update@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-build@7.4.1(supports-color@10.2.2): + dependencies: + '@apideck/better-ajv-errors': 0.3.7(ajv@8.20.0) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/preset-env': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/runtime': 7.29.10 + '@rollup/plugin-babel': 6.1.0(@babel/core@7.29.7(supports-color@10.2.2))(rollup@4.66.0)(supports-color@10.2.2) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.66.0) + '@rollup/plugin-replace': 6.0.3(rollup@4.66.0) + '@rollup/plugin-terser': 1.0.2(rollup@4.66.0) + '@trickfilm400/rollup-plugin-off-main-thread': 3.0.0-pre1 + ajv: 8.20.0 + common-tags: 1.8.2 + eta: 4.6.0 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 11.1.0 + pretty-bytes: 5.6.0 + rollup: 4.66.0 + source-map: 0.8.0 + stringify-object: 3.3.0 + strip-comments: 2.0.1 + tempy: 0.6.0 + upath: 1.2.0 + workbox-background-sync: 7.4.1 + workbox-broadcast-update: 7.4.1 + workbox-cacheable-response: 7.4.1 + workbox-core: 7.4.1 + workbox-expiration: 7.4.1 + workbox-google-analytics: 7.4.1 + workbox-navigation-preload: 7.4.1 + workbox-precaching: 7.4.1 + workbox-range-requests: 7.4.1 + workbox-recipes: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + workbox-streams: 7.4.1 + workbox-sw: 7.4.1 + workbox-window: 7.4.1 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + + workbox-cacheable-response@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-core@7.4.1: {} + + workbox-expiration@7.4.1: + dependencies: + idb: 7.1.1 + workbox-core: 7.4.1 + + workbox-google-analytics@7.4.1: + dependencies: + workbox-background-sync: 7.4.1 + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + + workbox-navigation-preload@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-precaching@7.4.1: + dependencies: + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + + workbox-range-requests@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-recipes@7.4.1: + dependencies: + workbox-cacheable-response: 7.4.1 + workbox-core: 7.4.1 + workbox-expiration: 7.4.1 + workbox-precaching: 7.4.1 + workbox-routing: 7.4.1 + workbox-strategies: 7.4.1 + + workbox-routing@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-strategies@7.4.1: + dependencies: + workbox-core: 7.4.1 + + workbox-streams@7.4.1: + dependencies: + workbox-core: 7.4.1 + workbox-routing: 7.4.1 + + workbox-sw@7.4.1: {} + + workbox-window@7.4.1: + dependencies: + '@types/trusted-types': 2.0.7 + workbox-core: 7.4.1 + workerd@1.20260921.1: optionalDependencies: '@cloudflare/workerd-darwin-64': 1.20260921.1 @@ -3232,6 +6659,8 @@ snapshots: ws@8.21.0: {} + yallist@3.1.1: {} + yaml@2.9.1: {} youch-core@0.3.3: diff --git a/public/og.png b/public/og.png new file mode 100644 index 0000000000000000000000000000000000000000..49afded665878ec8b54009af876f4c48711963f9 GIT binary patch literal 51007 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 literal 0 HcmV?d00001 diff --git a/scripts/og-card.html b/scripts/og-card.html new file mode 100644 index 0000000..56f64ba --- /dev/null +++ b/scripts/og-card.html @@ -0,0 +1,21 @@ + + +
+
+
devtools.fadeltd.dev
+

Developer tools that never upload what you paste.

+
diffjsonbase64jwtlistyaml ↔ tomlsqlcron+ 19 more
+
diff --git a/scripts/seo.ts b/scripts/seo.ts index ae11418..fffe95a 100644 --- a/scripts/seo.ts +++ b/scripts/seo.ts @@ -4,7 +4,8 @@ * A client-only SPA serves one empty
for every URL, which is * this architecture's biggest SEO weakness. So after `vite build` we write a * real HTML file per tool route, each with its own title, description, - * canonical, Open Graph tags, JSON-LD and an

plus a static summary. + * canonical, Open Graph tags, JSON-LD and an

plus a static summary and + * the tool's paragraph from src/lib/registry/about.ts. * * This composes with Cloudflare rather than fighting it: real assets take * priority over `not_found_handling`, so /diff serves the prerendered file @@ -13,6 +14,7 @@ */ import { mkdir, readFile, writeFile } from 'node:fs/promises' import { join } from 'node:path' +import { ABOUT } from '../src/lib/registry/about' import { VISIBLE_TOOLS } from '../src/lib/registry/index' import { CATEGORY_LABELS } from '../src/lib/registry/types' @@ -69,6 +71,7 @@ function bodyFor(tool: (typeof VISIBLE_TOOLS)[number]): string { '
', `

${escapeHtml(tool.title)}

`, `

${escapeHtml(tool.blurb)}

`, + `

${escapeHtml(ABOUT[tool.slug])}

`, `

Category: ${escapeHtml(CATEGORY_LABELS[tool.category])}.`, ` Runs entirely in your browser — nothing you paste is uploaded.

`, `

Also handles: ${escapeHtml(tool.keywords.join(', '))}.

`, diff --git a/src/tools/color/ColorField.tsx b/src/components/ui/ColorField.tsx similarity index 100% rename from src/tools/color/ColorField.tsx rename to src/components/ui/ColorField.tsx diff --git a/src/tools/color/Swatch.tsx b/src/components/ui/Swatch.tsx similarity index 100% rename from src/tools/color/Swatch.tsx rename to src/components/ui/Swatch.tsx diff --git a/src/lib/registry/about.ts b/src/lib/registry/about.ts new file mode 100644 index 0000000..c747569 --- /dev/null +++ b/src/lib/registry/about.ts @@ -0,0 +1,37 @@ +import type { ToolSlug } from './index' + +/** + * One honest paragraph per tool: which edge cases it actually handles. + * Rendered only into the prerendered HTML by scripts/seo.ts, so none of this + * reaches an app chunk -- do not import it from src/. `Record` + * makes typecheck fail when a tool has no paragraph. + */ +export const ABOUT: Record = { + 'diff': 'TODO', + 'text-stats': 'TODO', + 'base64': 'TODO', + 'list': 'TODO', + 'id-gen': 'TODO', + 'json': 'TODO', + 'html-text': 'TODO', + 'timestamp': 'TODO', + 'jwt': 'TODO', + 'url': 'TODO', + 'hash': 'TODO', + 'hmac': 'TODO', + 'slug': 'TODO', + 'text-bytes': 'TODO', + 'number': 'TODO', + 'unicode': 'TODO', + 'escape': 'TODO', + 'env': 'TODO', + 'color': 'TODO', + 'contrast': 'TODO', + 'lorem': 'TODO', + 'cron': 'TODO', + 'csv': 'TODO', + 'yaml-toml': 'TODO', + 'qr': 'TODO', + 'sql': 'TODO', + 'css': 'TODO', +} diff --git a/src/lib/registry/index.ts b/src/lib/registry/index.ts index 5fc2194..e31756c 100644 --- a/src/lib/registry/index.ts +++ b/src/lib/registry/index.ts @@ -33,7 +33,8 @@ import type { ToolDef } from './types' * The single source of truth. This array drives the router, the home grid, the * sidebar, the command palette, the build-time prerender and the sitemap. * - * Adding a tool = one folder under src/tools/ + one entry here. If a change + * Adding a tool = one folder under src/tools/ + one entry here (+ its + * paragraph in ./about.ts, which typecheck enforces). If a change * needs the router or the sitemap script edited by hand, this abstraction has * been broken -- fix that instead of working around it. */ @@ -185,8 +186,9 @@ const RAW_TOOLS = [ category: 'data', icon: Braces, load: () => import('@/tools/json/JsonTool'), - store: { kind: 'local' }, - stateVersion: 1, + // idb: a pasted payload can run to tens of MB, far past localStorage. + store: { kind: 'idb' }, + stateVersion: 2, status: 'beta', }, { diff --git a/src/tools/color/ColorTool.tsx b/src/tools/color/ColorTool.tsx index 7ec8689..2437789 100644 --- a/src/tools/color/ColorTool.tsx +++ b/src/tools/color/ColorTool.tsx @@ -8,8 +8,8 @@ import { useShortcuts } from '@/lib/keys/useShortcuts' import { useToolUsageTracker } from '@/lib/prefs' import { copyText } from '@/lib/util/clipboard' import { formatNumber } from '@fadeltd/devtools-core/color' -import { ColorField, parseField } from './ColorField' -import { Swatch } from './Swatch' +import { ColorField, parseField } from '@/components/ui/ColorField' +import { Swatch } from '@/components/ui/Swatch' interface State { input: string diff --git a/src/tools/contrast/ContrastTool.tsx b/src/tools/contrast/ContrastTool.tsx index d5e097e..5130ffa 100644 --- a/src/tools/contrast/ContrastTool.tsx +++ b/src/tools/contrast/ContrastTool.tsx @@ -6,8 +6,8 @@ import { Button } from '@/components/ui/Button' import { CopyButton } from '@/components/ui/CopyButton' import { useToolState } from '@/lib/persist/useToolState' import { useToolUsageTracker } from '@/lib/prefs' -import { ColorField, parseField } from '@/tools/color/ColorField' -import { Swatch } from '@/tools/color/Swatch' +import { ColorField, parseField } from '@/components/ui/ColorField' +import { Swatch } from '@/components/ui/Swatch' import { checkContrast, formatRatio, diff --git a/src/tools/diff/MergePane.tsx b/src/tools/diff/MergePane.tsx index a4adb39..8302fa0 100644 --- a/src/tools/diff/MergePane.tsx +++ b/src/tools/diff/MergePane.tsx @@ -174,9 +174,9 @@ export function MergePane(props: MergePaneProps) { }, }) return () => onReady(null) - // `mode` matters: which underlying view exists changes with it, so the - // handle has to be rebuilt. - }, [onReady, mode]) + // No `mode` dependency: jump and focus read the refs when called, so one + // handle serves both the split and the unified view. + }, [onReady]) return
} diff --git a/vite.config.ts b/vite.config.ts index 64a1b10..49e2b74 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,12 +2,35 @@ import { fileURLToPath } from 'node:url' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' import { defineConfig } from 'vite' +import { VitePWA } from 'vite-plugin-pwa' export default defineConfig({ - plugins: [react(), tailwindcss()], + plugins: [ + react(), + tailwindcss(), + // Offline: precache every built asset. No web manifest, so no install + // banner. The registration script is an external file, which keeps it + // out of the entry bundle and inside `script-src 'self'`. + VitePWA({ + registerType: 'autoUpdate', + injectRegister: 'script', + manifest: false, + workbox: { + globPatterns: ['**/*.{js,css,html,woff2,svg}'], + // Only the shell: the per-tool HTML that scripts/seo.ts writes after + // this runs is for crawlers, and they never run a service worker. + globIgnores: ['*/**/index.html'], + navigateFallback: '/index.html', + maximumFileSizeToCacheInBytes: 4 * 1024 * 1024, + }, + }), + ], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) }, }, + // ES workers keep a worker's dynamic import() as a separate chunk; IIFE + // inlined it, so the CSS worker loaded Prettier even to minify. + worker: { format: 'es' }, build: { target: 'es2023', // No manualChunks: let the bundler split on the registry's dynamic imports. diff --git a/wrangler.jsonc b/wrangler.jsonc index 8595b38..59c20fb 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -6,7 +6,7 @@ "directory": "./dist/", // Serves /index.html with 200 for unmatched paths, so /diff survives a hard // refresh. Real files still win, which is what makes the prerendered - // per-tool HTML in scripts/prerender.ts work. + // per-tool HTML in scripts/seo.ts work. "not_found_handling": "single-page-application", "html_handling": "drop-trailing-slash" }, From ad18e07dff8815c9f2f86217ce0b801c24935f41 Mon Sep 17 00:00:00 2001 From: fadeltd Date: Fri, 30 Oct 2026 01:19:36 +0700 Subject: [PATCH 02/14] SEO: one honest paragraph per tool Replaces the 27 TODO placeholders in src/lib/registry/about.ts with one paragraph per tool, rendered only into the prerendered HTML by scripts/seo.ts. Each paragraph names edge cases the tool actually handles, and every claim was checked against that tool's engine and tests in packages/core, its shell, or its registry store kind. Features still in flight on other branches are not described. --- src/lib/registry/about.ts | 54 +++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/lib/registry/about.ts b/src/lib/registry/about.ts index c747569..c19274c 100644 --- a/src/lib/registry/about.ts +++ b/src/lib/registry/about.ts @@ -7,31 +7,31 @@ import type { ToolSlug } from './index' * makes typecheck fail when a tool has no paragraph. */ export const ABOUT: Record = { - 'diff': 'TODO', - 'text-stats': 'TODO', - 'base64': 'TODO', - 'list': 'TODO', - 'id-gen': 'TODO', - 'json': 'TODO', - 'html-text': 'TODO', - 'timestamp': 'TODO', - 'jwt': 'TODO', - 'url': 'TODO', - 'hash': 'TODO', - 'hmac': 'TODO', - 'slug': 'TODO', - 'text-bytes': 'TODO', - 'number': 'TODO', - 'unicode': 'TODO', - 'escape': 'TODO', - 'env': 'TODO', - 'color': 'TODO', - 'contrast': 'TODO', - 'lorem': 'TODO', - 'cron': 'TODO', - 'csv': 'TODO', - 'yaml-toml': 'TODO', - 'qr': 'TODO', - 'sql': 'TODO', - 'css': 'TODO', + 'diff': 'Lines are aligned first and only then refined to word level, so a changed word is highlighted as a whole word rather than as scattered characters. The ignore options cover every whitespace difference (like git diff -w), case, and leading or trailing whitespace, which also makes CRLF against LF count as no change, while highlights keep pointing at the original text. The copied unified patch has 3 lines of context and deliberately skips those options, so applying it reproduces the right side byte for byte. A share link compresses both sides into the URL fragment and is offered only up to 8,000 characters.', + 'text-stats': 'Characters are counted four ways, because they disagree: graphemes (a family emoji is one), code points, UTF-16 units (what String.length reports) and UTF-8 bytes. Words come from Intl.Segmenter rather than splitting on spaces, so Chinese and Japanese text is segmented into words instead of counted as one. The sentence count re-joins breaks after abbreviations such as "Dr." and after initials, and reading time assumes 238 words per minute, or 150 read aloud. Word frequency can drop a deliberately short stop-word list and also counts two- and three-word phrases.', + 'base64': 'Text is encoded as UTF-8 through TextEncoder, so emoji and CJK round-trip, and decoding bytes that are not valid UTF-8 reports an error instead of returning replacement characters. Pasted input is cleaned in the open: a data: URI header, PEM or MIME line wrapping, percent-encoding and a BOM are each stripped and reported, the base64url alphabet is detected, and an invalid character is located by line and column. A file\'s type comes from its magic bytes, so a JPEG saved as .png is flagged; files over 1 MB are encoded off the main thread and files over 25 MB are refused.', + 'list': 'A trailing newline never creates a phantom blank line, CRLF and lone CR endings are split cleanly so no carriage return stays glued to a line, mixed line endings are flagged, and a BOM is kept out of line 1. Natural sort orders file10 after file9 and compares long digit runs exactly, and dedupe treats NFC and NFD forms of the same text as one line. Shuffle draws from crypto.getRandomValues in 64 KB chunks with rejection sampling, so 100,000 lines neither crash nor bias toward the start, and a seed makes it reproducible. A bad regex fails only its own step.', + 'id-gen': 'Every random character is drawn from crypto.getRandomValues with rejection sampling, so no alphabet size skews the distribution, and the entropy figure counts only the random part, since a prefix like sk_live_ is public. UUID v7 follows RFC 9562, using its 12-bit counter so ids made in the same millisecond still sort in generation order even if the clock steps backwards, and it is rated weak because it reveals its creation time. Pasting a UUID or ULID shows its version, variant and embedded time. Only the options are saved, never the generated values.', + 'json': 'Every distinct syntax error is reported with its line, column and a caret, placed on a trailing comma itself rather than the closing brace, with tabs measured as visual columns. A fix-up pass repairs pasted JavaScript: comments, trailing commas, single quotes, bare keys, a JSONP wrapper, the )]}\' XSSI prefix and a BOM. Integers a double cannot hold, such as int64 ids, are flagged, and where the browser supports JSON.rawJSON their digits survive formatting, minifying and key sorting, which never reorders arrays. JSON stringified inside string values can be expanded in place, and paths work as JSON Pointer, dot or jq.', + 'html-text': 'Whitespace collapses the way a browser renders it,
 is kept verbatim, and paragraphs and headings get one blank line between them, never more. Ordered lists are numbered from their start attribute, nested lists are indented, and table cells are joined with tabs. Script, style, template, noscript, head and svg contents are dropped, as are elements with the hidden attribute or inline display:none, while aria-hidden elements are kept because they are often visible. Entities decode as browsers do, including the windows-1252 remap for references 128 to 159, and malformed markup never throws.',
+  'timestamp': 'The unit is inferred from the digit count: up to 11 digits is seconds, 12 to 14 milliseconds, 15 to 17 microseconds, and 18 or more nanoseconds. Values are held as BigInt, so a 19-digit nanosecond timestamp keeps the digits a double would lose, and negative or fractional epochs work too. An ISO 8601 time without an offset is read as wall time in the chosen zone: a time inside a DST gap moves forward, and an ambiguous one takes the earlier instant. Output covers half-hour and quarter-hour offsets, the ISO week, an HTTP date and relative time.',
+  'jwt': 'The token is never saved, not even for a reload. exp, iat and nbf are shown as dates with an adjustable clock skew, and the decoder warns about millisecond timestamps, exp before iat, alg none in any casing, and integers too large for a double. Signatures are verified with WebCrypto for HS, RS, PS and ES at 256, 384 and 512 bits and for EdDSA (Ed25519), against a secret, a JWK, a JWKS matched by kid, a PEM or PKCS#1 key, or an X.509 certificate. HS256 checked with an RSA public key is refused as algorithm confusion.',
+  'url': 'Three encodings are kept distinct and tested against the platform: component matches encodeURIComponent, URI matches encodeURI, and form matches URLSearchParams, where + means a space and %2B always means a plus. Strict decoding stops at the first malformed escape and points at it; lenient decoding leaves bad sequences as typed and counts them. Double encoding (%25 followed by two hex digits) is detected, and a lone surrogate is reported by position instead of throwing. The query table keeps duplicate keys in order and tells a bare flag from a key with an empty value.',
+  'hash': 'Text is hashed as UTF-8 exactly as typed, never trimmed, and the tool points out trailing newlines, CRLF pairs and lone surrogates, any of which changes the digest. One trailing newline can be stripped, the inverse of what echo adds. The digests are tested against the FIPS 180 vectors. A digest pasted for comparison can be hex in either case, base64, base64url, an SRI string or a line of sha256sum output, and the tool names the algorithm it matches.',
+  'hmac': 'The key is never saved. It can be entered as UTF-8 text, hex or base64, with a bad character reported at its position, and an empty key works even though WebCrypto alone rejects one. The GitHub, Stripe and Slack presets reproduce each provider\'s documented signature: a Stripe header passes when any v1 value matches, as during secret rotation, Stripe and Slack timestamps must fall within 300 seconds, and GitHub\'s legacy SHA-1 header is pointed out. The body is signed byte for byte, and the HMAC itself is tested against RFC 4231 and RFC 2202.',
+  'slug': 'ASCII mode transliterates rather than deletes: diacritics are stripped, ss replaces the German sharp s, & becomes "and", and camelCase splits while acronyms stay whole; Unicode mode keeps CJK and accents instead. A length limit cuts at a separator boundary. Safe filenames replace characters forbidden on any platform, strip trailing dots and spaces, prefix Windows device names such as CON and LPT1 even with an extension, and cap at 255 UTF-8 bytes on a grapheme boundary while keeping the extension. Lines that would collide on a case-insensitive filesystem, or differ only by NFC and NFD form, are reported.',
+  'text-bytes': 'Text is always encoded as UTF-8, and a BOM or CRLF survives as bytes, so the round trip is exact. Pasted bytes are read leniently about layout: any whitespace or commas, 0x and \\x prefixes, and runs of hex or binary with no separators. Auto-detection prefers the reading that is valid UTF-8, picks binary for 8-bit groups of 0 and 1, and says when it was unsure. Decoding locates invalid UTF-8, including overlong forms, surrogates and code points past U+10FFFF, and shows a labelled Latin-1 fallback instead of silent replacement characters.',
+  'number': 'Values are BigInt throughout, so any base from 2 to 36 converts exactly far past 2^53, up to 100,000 digits, and negatives also show two\'s complement at 8, 16, 32 and 64 bits. Byte sizes are read in SI (kB, MB) and IEC (KiB, MiB) units, a bare K or G is read as binary and flagged as ambiguous, and the GB versus GiB gap is shown as a percentage. Permissions accept octal with setuid, setgid and sticky bits or an ls -l string, telling s from S and t from T, and produce chmod in both forms.',
+  'unicode': 'Each code point is listed with its general category and UTF-8 bytes, and flagged ones are named: zero-width characters, Trojan Source bidi controls, smuggled tag characters, exotic spaces, lone surrogates and U+FFFD. The joiners inside an emoji ZWJ sequence or a subdivision flag are not flagged, since they belong there. Cleaning removes zero-width characters, a leading BOM, soft hyphens, bidi controls and tag characters, replaces exotic spaces with a plain space, and can then apply NFC or NFKC, normalising after removal so a deleted character cannot block composition.',
+  'escape': 'Each mode follows its own language: JavaScript escapes ${ in template literals but not a lone $, writes NUL as \\x00 before a digit where \\0 would read as octal, and escapes line separators; shell wraps in single quotes with the \'\\\'\' idiom; SQL doubles quotes in standard mode and uses backslashes for MySQL. Unescaping reports a bad escape, an unescaped quote or a raw line break by line and column, and the shell reader warns about globs and expansions. HTML unescaping applies the windows-1252 remap browsers use.',
+  'env': 'Nothing pasted here is saved, because .env files hold secrets, and a mask toggle hides every value in the preview while Copy still takes the real ones. The .env reader handles export prefixes, inline comments, literal single quotes and backticks, escapes and multi-line values in double quotes, and CRLF; a # glued to an unquoted value is kept with a warning about Node dotenv, and a duplicate key keeps the last value and warns. JSON number literals keep their exact digits, nested objects can flatten to A__B keys, and output uses the lightest safe quoting, POSIX single quotes for shell, or raw values for docker --env-file.',
+  'color': 'Input can be hex in 3, 4, 6 or 8 digits, rgb() or hsl() in comma or space syntax with every angle unit and none, oklch() with chroma as a number or a percentage of 0.4, or any of the 148 named colors, and an error points at the offending character. Out-of-range rgb() values are clamped as a browser does, with a note. An oklch() color outside sRGB is kept as written and flagged, and its sRGB fallback lowers only the chroma, so lightness and hue are preserved. Output is canonical CSS with no float noise and no -0.',
+  'contrast': 'Ratios use the WCAG relative-luminance formula and are printed rounded down, so 4.499 never displays as a passing 4.50; #777 on white comes out at 4.48 and fails AA. A translucent foreground is blended over the background before measuring, and a translucent background over white, with a note either way. When a pair fails, the suggested foreground moves only OKLCH lightness, keeping the hue, by just enough to reach the target, and the suggestion is checked after rounding to hex so it passes as pasted.',
+  'lorem': 'Counts are exact in every unit and format, capped at 50,000 words, 5,000 sentences or 1,000 paragraphs, and the tool says when a cap applies. A seed reproduces the same text, and one seed gives the same words whether the output is plain text, HTML with one 

per paragraph, or Markdown, which emphasises the odd word but never the first of a sentence. Opening with the classic "Lorem ipsum dolor sit amet" is optional, and a request for only a few words cuts it short without leaving a dangling comma.', + 'cron': 'Errors name their field and position, a pasted crontab line with a command on the end is recognised, and Quartz syntax (?, L, W, #) is refused by name instead of half-accepted. When day-of-month and day-of-week are both restricted they are ORed, as Vixie cron does, 7 means Sunday, and six-field schedules run to the second. Next runs handle daylight saving: a fixed-time job in the spring gap moves to the first instant after it and runs once in the autumn overlap, half-hour zones such as Adelaide work, and 30 February is reported as never firing.', + 'csv': 'Parsing follows RFC 4180, including quoted fields that hold delimiters, line breaks and doubled quotes, reads LF, CRLF and lone CR alike, and never invents a row from a trailing newline. The delimiter is sniffed among comma, tab, semicolon and pipe, ignoring any inside quotes, and European files with decimal commas resolve to semicolon. Ragged rows are reported with expected and actual cell counts and padded rather than dropped, an unterminated quote is reported at its position, and repeated column names are flagged. JSON output keeps every value as its exact string, and Markdown output escapes pipes and newlines.', + 'yaml-toml': 'YAML is read as 1.2 unless a document declares %YAML 1.1, so country: no stays a string (the Norway problem), with a note, and YAML output quotes such values so 1.1 readers agree. Anchors, aliases and merge keys are resolved, but an alias bomb is refused rather than expanded, duplicate keys are reported, and a multi-document stream becomes a JSON array. Integers beyond 2^53 stay exact between formats, TOML datetimes become ISO strings with a note, and a null, which TOML cannot hold, is refused with its key path. Parse errors carry a line and a caret.', + 'qr': 'Wi-Fi codes escape reserved characters in both the SSID and the password, add H:true only for a hidden network, and warn about a WPA password outside 8 to 63 characters or an SSID over 32 UTF-8 bytes. A bare host gets https:// added, while host:port is not mistaken for a scheme. The quiet zone defaults to 4 modules, with warnings for a thinner one, for version 20 and above, for low contrast, and for an inverted code some scanners cannot read. The SVG is a single path, and the PNG is snapped to whole pixels per module.', + 'sql': 'Formatting uses sql-formatter across 12 dialects, from standard SQL and PostgreSQL to BigQuery, T-SQL, Spark and Trino, and a parse or lexer error is reported at its line and column. Minifying is separate and dialect-aware: string literals, quoted identifiers and dollar-quoted bodies are left untouched, backslash escapes are read only where the dialect has them, # starts a comment only in MySQL-like dialects, and BigQuery triple-quoted strings are understood. Comments are kept by default and can be stripped, except optimizer hints and MySQL versioned comments.', + 'css': 'Pretty-printing uses Prettier with its PostCSS parser, so CSS, SCSS and Less are all handled, and a syntax error or an unclosed block is reported at its line and column. The CSS minifier keeps /*! licence comments, the spaces calc() needs around + and -, the space that makes a descendant pseudo-class, and the space that ends a hex escape, and it leaves strings and unquoted url(data:...) bodies verbatim. It is idempotent, even on malformed input, and it drops the last semicolon in each block and any empty declarations.', } From 996612bd4a878e2f520749fd3b23c3e57c47c089 Mon Sep 17 00:00:00 2001 From: fadeltd Date: Fri, 30 Oct 2026 01:20:50 +0700 Subject: [PATCH 03/14] List: share a pipeline as a #p= link A "Copy link" button under the steps copies a link that carries the pipeline's steps (and any set-operation list) but never the input text. It lives in the URL fragment, not a query string, so it is never sent to the server or its logs. The link is encoded ahead of the click so the clipboard write happens directly in the handler, as Safari requires; past 8000 encoded characters, or when a setting is out of the shareable bounds, the button is disabled and the visible hint says why. Opening a link (on load, or when pasted into an already-open List tab) replaces the steps, keeps the input, clears the fragment while keeping React Router's history state, and shows a dismissible notice. A damaged or newer-version link shows an error and changes nothing. On /list/ the tool moves to /list so the route kind cannot swap a loaded one-step pipeline. Core: list/share.ts adds encodePipeline, decodePipeline and a strict parseOp/parseOps/parseSharedSteps that rebuilds every op field by field (types, enums, string lengths, the LineTest union, and bounds on pad, chunk size and seed), drops unknown fields, and rejects the whole link on any bad value. Payload is versioned ({ v: 1, steps: [{ op, on }] }) inside the codec's own 1. prefix. Unit tests per kind plus property tests: valid pipelines round-trip, arbitrary JSON never makes the parser throw, arbitrary strings never make decodePipeline throw. Also clamps the filter step's line-number range loop to the document, so a range like 1-999999999999 no longer walks every number in it. --- packages/core/src/list/index.ts | 1 + packages/core/src/list/pipeline.ts | 7 +- packages/core/src/list/share.test.ts | 322 +++++++++++++++++++++++++++ packages/core/src/list/share.ts | 301 +++++++++++++++++++++++++ src/tools/list/ListTool.tsx | 263 +++++++++++++++++----- 5 files changed, 832 insertions(+), 62 deletions(-) create mode 100644 packages/core/src/list/share.test.ts create mode 100644 packages/core/src/list/share.ts diff --git a/packages/core/src/list/index.ts b/packages/core/src/list/index.ts index 0675841..4b1d15d 100644 --- a/packages/core/src/list/index.ts +++ b/packages/core/src/list/index.ts @@ -3,4 +3,5 @@ export * from './compare.ts' export * from './lines.ts' export * from './ops.ts' export * from './pipeline.ts' +export * from './share.ts' export * from './types.ts' diff --git a/packages/core/src/list/pipeline.ts b/packages/core/src/list/pipeline.ts index cde71f3..f44770f 100644 --- a/packages/core/src/list/pipeline.ts +++ b/packages/core/src/list/pipeline.ts @@ -45,9 +45,10 @@ function parseIndexRanges(spec: string, length: number): Set { if (m) { const a = Number(m[1]) const b = Number(m[2]) - for (let i = Math.min(a, b); i <= Math.max(a, b); i++) { - if (i >= 1 && i <= length) out.add(i - 1) - } + // Clamped to the document first: "1-999999999999" must not loop a + // trillion times to select ten lines. + const hi = Math.min(Math.max(a, b), length) + for (let i = Math.max(Math.min(a, b), 1); i <= hi; i++) out.add(i - 1) } else if (/^\d+$/.test(t)) { const i = Number(t) if (i >= 1 && i <= length) out.add(i - 1) diff --git a/packages/core/src/list/share.test.ts b/packages/core/src/list/share.test.ts new file mode 100644 index 0000000..f49bff1 --- /dev/null +++ b/packages/core/src/list/share.test.ts @@ -0,0 +1,322 @@ +import fc from 'fast-check' +import { describe, expect, it } from 'vitest' +import { encodeCompact } from '../share/compact.ts' +import { parseLines } from './lines.ts' +import { CASE_LABELS, defaultOp, OP_KINDS } from './ops.ts' +import { runPipeline } from './pipeline.ts' +import { + decodePipeline, + encodePipeline, + MAX_FIELD_CHARS, + MAX_PAD, + MAX_SHARED_STEPS, + parseOp, + parseOps, + parseSharedSteps, + PIPELINE_PARAM, + type SharedStep, +} from './share.ts' +import type { CaseTarget, LineTest, Op } from './types.ts' + +const text = fc.string({ maxLength: 40 }) +const bool = fc.boolean() +const num = fc.double({ noNaN: true, noDefaultInfinity: true }) + +const testArb: fc.Arbitrary = fc.oneof( + fc.record({ t: fc.constant('regex' as const), source: text, flags: fc.constantFrom('', 'i', 'gimsu') }), + fc.record({ t: fc.constant('contains' as const), needle: text, ci: bool }), + fc.constant({ t: 'blank' as const }), + fc.record({ t: fc.constant('index' as const), ranges: text }), + fc.constant({ t: 'duplicate' as const }), +) + +/** Every shape the `Op` union allows, within the share bounds. */ +const opArb: fc.Arbitrary = fc.oneof( + fc.record( + { + kind: fc.constant('sort' as const), + order: fc.constantFrom('asc' as const, 'desc' as const), + mode: fc.constantFrom('codepoint' as const, 'locale' as const, 'natural' as const, 'length' as const, 'random' as const), + caseSensitive: bool, + locale: fc.constantFrom('en', 'de-DE', 'tr'), + blanks: fc.constantFrom('keep' as const, 'first' as const, 'last' as const, 'drop' as const), + }, + { requiredKeys: ['kind', 'order', 'mode', 'caseSensitive', 'blanks'] }, + ), + fc.record({ + kind: fc.constant('dedupe' as const), + caseSensitive: bool, + trimKey: bool, + keep: fc.constantFrom('first' as const, 'last' as const), + output: fc.constantFrom('unique' as const, 'duplicates-only' as const, 'unique-only' as const, 'with-counts' as const), + }), + fc.record( + { + kind: fc.constant('shuffle' as const), + source: fc.constantFrom('crypto' as const, 'seeded' as const), + seed: fc.nat({ max: 0xffff_ffff }), + }, + { requiredKeys: ['kind', 'source'] }, + ), + fc.constant({ kind: 'reverse' as const }), + fc.record( + { + kind: fc.constant('case' as const), + target: fc.constantFrom(...(Object.keys(CASE_LABELS) as CaseTarget[])), + locale: fc.constantFrom('en', 'tr'), + }, + { requiredKeys: ['kind', 'target'] }, + ), + fc.record({ kind: fc.constant('affix' as const), prefix: text, suffix: text, skipBlank: bool }), + fc.record({ kind: fc.constant('filter' as const), action: fc.constantFrom('keep' as const, 'remove' as const), test: testArb }), + fc.record({ + kind: fc.constant('number' as const), + start: num, + step: num, + pad: fc.nat({ max: MAX_PAD }), + padChar: fc.constantFrom('', '0', ' ', '😀'), + separator: text, + position: fc.constantFrom('prefix' as const, 'suffix' as const), + skipBlank: bool, + }), + fc.record({ kind: fc.constant('wrap' as const), width: num, breakWords: bool, preserveIndent: bool }), + fc.record( + { kind: fc.constant('join' as const), separator: text, chunkSize: fc.integer({ min: 1, max: 1000 }) }, + { requiredKeys: ['kind', 'separator'] }, + ), + fc.record({ + kind: fc.constant('split' as const), + by: fc.oneof( + fc.record({ t: fc.constant('literal' as const), s: text }), + fc.record({ t: fc.constant('regex' as const), source: text, flags: fc.constantFrom('', 'u') }), + ), + }), + fc.record({ kind: fc.constant('trim' as const), leading: bool, trailing: bool, collapseInternal: bool }), + fc.record({ kind: fc.constant('normalize' as const), form: fc.constantFrom('NFC' as const, 'NFD' as const, 'NFKC' as const, 'NFKD' as const) }), + fc.record({ + kind: fc.constant('setop' as const), + op: fc.constantFrom('union' as const, 'intersection' as const, 'difference' as const, 'symmetric-difference' as const), + other: fc.string({ maxLength: 400 }), + caseSensitive: bool, + bag: bool, + }), + fc.record({ kind: fc.constant('slice' as const), from: num, to: num }, { requiredKeys: ['kind', 'from'] }), +) + +const stepArb: fc.Arbitrary = fc.record({ op: opArb, enabled: bool }) + +const filter = (test: unknown) => parseOp({ kind: 'filter', action: 'keep', test }) + +/** JSON can only carry what JSON.stringify keeps: -0 becomes 0. */ +const viaJson = (v: T): T => JSON.parse(JSON.stringify(v)) as T + +describe('parseOp', () => { + it.each(OP_KINDS)('accepts the default %s op unchanged', (kind) => { + expect(parseOp(defaultOp(kind))).toEqual(defaultOp(kind)) + }) + + it('accepts every valid op as itself', () => { + fc.assert( + fc.property(opArb, (op) => { + expect(parseOp(viaJson(op))).toEqual(viaJson(op)) + }), + ) + }) + + it('never throws on arbitrary JSON', () => { + fc.assert( + fc.property(fc.jsonValue(), (v) => { + parseOp(v) + parseOps(v) + parseSharedSteps(v) + }), + { numRuns: 500 }, + ) + }) + + it('never throws on near-miss ops', () => { + // A valid op with one field replaced by an arbitrary value: the shape that + // reaches the type checks rather than bouncing off the kind. + const nearMiss = opArb.chain((op) => + fc.tuple(fc.constantFrom(...Object.keys(op)), fc.jsonValue()).map(([k, v]) => ({ ...op, [k]: v })), + ) + fc.assert( + fc.property(nearMiss, (v) => { + const parsed = parseOp(v) + if (parsed !== null) expect(parseOp(parsed)).toEqual(parsed) + }), + { numRuns: 500 }, + ) + }) + + it('rejects non-objects and unknown kinds', () => { + for (const v of [null, 1, 'sort', [], [{ kind: 'sort' }], { kind: 'toString' }, { kind: 'eval' }, {}]) { + expect(parseOp(v)).toBeNull() + } + }) + + it('drops unknown fields', () => { + const hostile = JSON.parse('{"kind":"reverse","evil":"x","__proto__":{"polluted":true}}') as unknown + const parsed = parseOp(hostile) + expect(parsed).toEqual({ kind: 'reverse' }) + expect(Object.keys(parsed!)).toEqual(['kind']) + expect(Object.getPrototypeOf(parsed)).toBe(Object.prototype) + expect(parseOp({ ...defaultOp('trim'), extra: 1 })).toEqual(defaultOp('trim')) + }) + + it('rejects a missing required field', () => { + const { caseSensitive: _, ...sort } = defaultOp('sort') as Extract + expect(parseOp(sort)).toBeNull() + }) + + it('rejects wrong types', () => { + expect(parseOp({ ...defaultOp('sort'), caseSensitive: 'yes' })).toBeNull() + expect(parseOp({ ...defaultOp('affix'), prefix: 1 })).toBeNull() + expect(parseOp({ ...defaultOp('wrap'), width: '80' })).toBeNull() + }) + + it('rejects values outside an enum', () => { + expect(parseOp({ ...defaultOp('sort'), mode: 'bogus' })).toBeNull() + expect(parseOp({ ...defaultOp('sort'), blanks: 'middle' })).toBeNull() + expect(parseOp({ ...defaultOp('dedupe'), output: 'all' })).toBeNull() + expect(parseOp({ ...defaultOp('case'), target: 'toString' })).toBeNull() + expect(parseOp({ ...defaultOp('normalize'), form: 'NFX' })).toBeNull() + expect(parseOp({ ...defaultOp('setop'), op: 'xor' })).toBeNull() + expect(parseOp({ ...defaultOp('number'), position: 'middle' })).toBeNull() + expect(parseOp({ ...defaultOp('shuffle'), source: 'math' })).toBeNull() + }) + + it('rejects an invalid optional field rather than dropping it', () => { + expect(parseOp({ ...defaultOp('sort'), locale: 'not a locale!!' })).toBeNull() + expect(parseOp({ ...defaultOp('sort'), locale: 42 })).toBeNull() + expect(parseOp({ ...defaultOp('join'), chunkSize: 0 })).toBeNull() + expect(parseOp({ ...defaultOp('slice'), to: '10' })).toBeNull() + expect(parseOp({ kind: 'shuffle', source: 'seeded', seed: -1 })).toBeNull() + }) + + it('bounds the values that could hang or exhaust memory', () => { + expect(parseOp({ ...defaultOp('number'), pad: MAX_PAD + 1 })).toBeNull() + expect(parseOp({ ...defaultOp('number'), pad: 1.5 })).toBeNull() + expect(parseOp({ ...defaultOp('number'), padChar: 'ab' })).toBeNull() + expect(parseOp({ ...defaultOp('join'), chunkSize: 1e-9 })).toBeNull() + expect(parseOp({ ...defaultOp('affix'), prefix: 'x'.repeat(MAX_FIELD_CHARS + 1) })).toBeNull() + expect(parseOp({ ...defaultOp('affix'), prefix: 'x'.repeat(MAX_FIELD_CHARS) })).not.toBeNull() + }) + + it('checks every variant of the filter test', () => { + expect(filter({ t: 'regex', source: 'a+', flags: 'i' })).not.toBeNull() + expect(filter({ t: 'regex', source: 'a+' })).toBeNull() + expect(filter({ t: 'contains', needle: 'x', ci: 'no' })).toBeNull() + expect(filter({ t: 'blank', extra: 1 })).toEqual({ kind: 'filter', action: 'keep', test: { t: 'blank' } }) + expect(filter({ t: 'index', ranges: 5 })).toBeNull() + expect(filter({ t: 'duplicate' })).not.toBeNull() + expect(filter({ t: 'eval' })).toBeNull() + expect(filter('blank')).toBeNull() + expect(parseOp({ kind: 'filter', test: { t: 'blank' } })).toBeNull() + }) + + it('checks both split variants', () => { + expect(parseOp({ kind: 'split', by: { t: 'literal', s: ',' } })).not.toBeNull() + expect(parseOp({ kind: 'split', by: { t: 'literal' } })).toBeNull() + expect(parseOp({ kind: 'split', by: { t: 'regex', source: ',', flags: 'x'.repeat(17) } })).toBeNull() + expect(parseOp({ kind: 'split', by: { t: 'other', s: ',' } })).toBeNull() + expect(parseOp({ kind: 'split' })).toBeNull() + }) +}) + +describe('parseOps', () => { + it('rejects the whole array on one bad op', () => { + expect(parseOps([defaultOp('sort'), { kind: 'sort' }])).toBeNull() + expect(parseOps([defaultOp('sort'), defaultOp('reverse')])).toHaveLength(2) + }) + + it('caps the number of steps', () => { + expect(parseOps(Array.from({ length: MAX_SHARED_STEPS }, () => defaultOp('reverse')))).not.toBeNull() + expect(parseOps(Array.from({ length: MAX_SHARED_STEPS + 1 }, () => defaultOp('reverse')))).toBeNull() + }) +}) + +describe('encodePipeline / decodePipeline', () => { + it('round-trips any valid pipeline, enabled flags and all', async () => { + await fc.assert( + fc.asyncProperty(fc.array(stepArb, { minLength: 1, maxLength: 8 }), async (steps) => { + const encoded = await encodePipeline(steps) + expect(encoded).not.toBeNull() + expect(await decodePipeline(`#${PIPELINE_PARAM}=${encoded!}`)).toEqual(viaJson(steps)) + }), + { numRuns: 100 }, + ) + }) + + it('accepts the bare value as well as the fragment', async () => { + const encoded = (await encodePipeline([{ op: defaultOp('sort'), enabled: false }]))! + expect(await decodePipeline(encoded)).toEqual([{ op: defaultOp('sort'), enabled: false }]) + }) + + it('is URL-safe and carries no ids', async () => { + const encoded = (await encodePipeline([{ op: defaultOp('dedupe'), enabled: true }]))! + expect(encoded).toMatch(/^1\.[A-Za-z0-9_-]+$/) + const withId = { id: 'secret-id', op: defaultOp('dedupe'), enabled: true } + expect(await encodePipeline([withId])).toBe(encoded) + }) + + it('declines what a receiver would reject', async () => { + expect(await encodePipeline([])).toBeNull() + expect(await encodePipeline([{ op: { ...defaultOp('number'), pad: 1e9 } as Op, enabled: true }])).toBeNull() + }) + + it('returns null for arbitrary strings, never throws', async () => { + await fc.assert( + fc.asyncProperty(fc.string(), async (s) => { + expect(await decodePipeline(s)).toBeNull() + expect(await decodePipeline(`#p=${s}`)).toBeNull() + }), + { numRuns: 200 }, + ) + }) + + it('returns null for a valid encoding of the wrong payload', async () => { + const steps = [{ op: defaultOp('reverse'), on: true }] + expect(await decodePipeline(await encodeCompact({ v: 2, steps }))).toBeNull() + expect(await decodePipeline(await encodeCompact({ v: 1, steps: [] }))).toBeNull() + expect(await decodePipeline(await encodeCompact({ v: 1, steps: [{ op: defaultOp('reverse') }] }))).toBeNull() + expect(await decodePipeline(await encodeCompact([defaultOp('reverse')]))).toBeNull() + expect(await decodePipeline(await encodeCompact({ v: 1, steps }))).toEqual([ + { op: defaultOp('reverse'), enabled: true }, + ]) + }) + + it('returns null for arbitrary JSON payloads, never throws', async () => { + await fc.assert( + fc.asyncProperty(fc.jsonValue(), async (v) => { + const r = await decodePipeline(await encodeCompact(v)) + if (r !== null) expect(r.length).toBeGreaterThan(0) + }), + { numRuns: 200 }, + ) + }) + + it('refuses a payload that inflates past the cap', async () => { + // Valid steps, plus an unknown field that would otherwise just be dropped. + const huge = { v: 1, steps: [{ op: defaultOp('reverse'), on: true }], junk: 'x'.repeat(2_000_000) } + expect(await decodePipeline(await encodeCompact(huge))).toBeNull() + }) + + it('a decoded pipeline runs', async () => { + const steps: SharedStep[] = [ + { op: { kind: 'filter', action: 'remove', test: { t: 'blank' } }, enabled: true }, + { op: defaultOp('dedupe'), enabled: true }, + { op: { kind: 'affix', prefix: '- ', suffix: '', skipBlank: true }, enabled: false }, + ] + const back = (await decodePipeline((await encodePipeline(steps))!))! + const ops = back.filter((s) => s.enabled).map((s) => s.op) + expect(runPipeline(parseLines('b\n\nb\na'), ops).doc.lines).toEqual(['b', 'a']) + }) +}) + +describe('index ranges', () => { + it('selects within a huge range without walking all of it', () => { + const op: Op = { kind: 'filter', action: 'keep', test: { t: 'index', ranges: '2-999999999999999' } } + expect(runPipeline(parseLines('a\nb\nc'), [op]).doc.lines).toEqual(['b', 'c']) + }) +}) diff --git a/packages/core/src/list/share.ts b/packages/core/src/list/share.ts new file mode 100644 index 0000000..0d19c3d --- /dev/null +++ b/packages/core/src/list/share.ts @@ -0,0 +1,301 @@ +import { decodeCompact, encodeCompact, fragmentValue } from '../share/compact.ts' +import { CASE_LABELS, isOpKind, type Step } from './ops.ts' +import type { BlankPolicy, CaseTarget, LineTest, Op, SortMode } from './types.ts' + +/** + * Share links for a pipeline: the steps, compressed into the URL fragment + * (`#p=1.…`). Never the input text. + * + * A fragment, not a query string: a query string is sent to the server and + * lands in its logs, and a pipeline can carry user data -- a filter pattern, + * a whole set-operation list. + * + * A link is untrusted input. Everything in it goes through `parseOp`, which + * rebuilds each op field by field and rejects the whole link on the first bad + * value, so nothing unvalidated ever reaches `runPipeline`. The numeric bounds + * are the ones where a crafted value would hang or exhaust memory (a pad + * width of 1e9, a chunk size of 1e-9), not a second opinion on taste. + */ + +/** The fragment key: `#p=`. */ +export const PIPELINE_PARAM = 'p' + +/** Encoded length past which no link is offered. Same as diff: survives chat apps and trackers. */ +export const MAX_PIPELINE_CHARS = 8000 +/** A crafted link can inflate enormously; stop reading well before that hurts. */ +export const MAX_PIPELINE_DECODED_BYTES = 1_000_000 + +/** More steps than anyone builds by hand. */ +export const MAX_SHARED_STEPS = 64 +/** Patterns, needles, separators, prefixes. */ +export const MAX_FIELD_CHARS = 2000 +/** The set-operation list, the one field that is meant to hold data. */ +export const MAX_LIST_CHARS = 500_000 +/** `padStart` past this allocates per line for nothing anyone wants. */ +export const MAX_PAD = 1000 + +/** The payload version, independent of the codec's own `1.` prefix. */ +const PAYLOAD_VERSION = 1 + +/** A step as it travels in a link: no id, which the receiver assigns. */ +export type SharedStep = Pick + +type Obj = Record + +function isObj(v: unknown): v is Obj { + return typeof v === 'object' && v !== null && !Array.isArray(v) +} + +/** A string at most `max` UTF-16 units long, or undefined. */ +function str(v: unknown, max = MAX_FIELD_CHARS): string | undefined { + return typeof v === 'string' && v.length <= max ? v : undefined +} + +function bool(v: unknown): boolean | undefined { + return typeof v === 'boolean' ? v : undefined +} + +function finite(v: unknown): number | undefined { + return typeof v === 'number' && Number.isFinite(v) ? v : undefined +} + +function int(v: unknown, min: number, max: number): number | undefined { + return typeof v === 'number' && Number.isInteger(v) && v >= min && v <= max ? v : undefined +} + +function oneOf(v: unknown, allowed: readonly T[]): T | undefined { + return typeof v === 'string' && (allowed as readonly string[]).includes(v) ? (v as T) : undefined +} + +/** A BCP 47 tag `Intl` accepts. A bad one would only fail the step, but it never needs to travel. */ +function locale(v: unknown): string | undefined { + const s = str(v, 64) + if (s === undefined || s === '') return undefined + try { + return Intl.getCanonicalLocales(s).length === 1 ? s : undefined + } catch { + return undefined + } +} + +const SORT_MODES = ['codepoint', 'locale', 'natural', 'length', 'random'] as const satisfies readonly SortMode[] +const BLANK_POLICIES = ['keep', 'first', 'last', 'drop'] as const satisfies readonly BlankPolicy[] +const CASE_TARGETS = Object.keys(CASE_LABELS) as CaseTarget[] +const DEDUPE_OUTPUTS = ['unique', 'duplicates-only', 'unique-only', 'with-counts'] as const +const SET_OPS = ['union', 'intersection', 'difference', 'symmetric-difference'] as const +const FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'] as const +const MAX_INDEX = Number.MAX_SAFE_INTEGER + +/** + * The fields as given when every one parsed, else `null` -- each parser above + * returns `undefined` for a bad value. Saves a guard per field below. + */ +function all(fields: T): { [K in keyof T]: Exclude } | null { + for (const v of Object.values(fields)) if (v === undefined) return null + return fields as { [K in keyof T]: Exclude } +} + +function parseTest(v: unknown): LineTest | null { + if (!isObj(v)) return null + switch (v.t) { + case 'regex': + return all({ t: 'regex' as const, source: str(v.source), flags: str(v.flags, 16) }) + case 'contains': + return all({ t: 'contains' as const, needle: str(v.needle), ci: bool(v.ci) }) + case 'blank': + return { t: 'blank' } + case 'index': + return all({ t: 'index' as const, ranges: str(v.ranges) }) + case 'duplicate': + return { t: 'duplicate' } + default: + return null + } +} + +/** + * Rebuild one op from an untrusted value: every field checked, unknown fields + * dropped, `null` on anything malformed. Never throws. + */ +export function parseOp(v: unknown): Op | null { + if (!isObj(v) || typeof v.kind !== 'string' || !isOpKind(v.kind)) return null + + switch (v.kind) { + case 'sort': { + const op = all({ + kind: 'sort' as const, + order: oneOf(v.order, ['asc', 'desc']), + mode: oneOf(v.mode, SORT_MODES), + caseSensitive: bool(v.caseSensitive), + blanks: oneOf(v.blanks, BLANK_POLICIES), + }) + return op && withOptional(op, 'locale', v.locale, locale) + } + case 'dedupe': + return all({ + kind: 'dedupe' as const, + caseSensitive: bool(v.caseSensitive), + trimKey: bool(v.trimKey), + keep: oneOf(v.keep, ['first', 'last']), + output: oneOf(v.output, DEDUPE_OUTPUTS), + }) + case 'shuffle': { + const op = all({ kind: 'shuffle' as const, source: oneOf(v.source, ['crypto', 'seeded']) }) + return op && withOptional(op, 'seed', v.seed, (s) => int(s, 0, 0xffff_ffff)) + } + case 'reverse': + return { kind: 'reverse' } + case 'case': { + const op = all({ kind: 'case' as const, target: oneOf(v.target, CASE_TARGETS) }) + return op && withOptional(op, 'locale', v.locale, locale) + } + case 'affix': + return all({ + kind: 'affix' as const, + prefix: str(v.prefix), + suffix: str(v.suffix), + skipBlank: bool(v.skipBlank), + }) + case 'filter': { + const test = parseTest(v.test) + const op = all({ kind: 'filter' as const, action: oneOf(v.action, ['keep', 'remove']) }) + return op && test && { ...op, test } + } + case 'number': { + const padChar = str(v.padChar, 2) + return all({ + kind: 'number' as const, + start: finite(v.start), + step: finite(v.step), + pad: int(v.pad, 0, MAX_PAD), + // One code point (the UI keeps only the last one typed), or none. + padChar: padChar !== undefined && [...padChar].length <= 1 ? padChar : undefined, + separator: str(v.separator), + position: oneOf(v.position, ['prefix', 'suffix']), + skipBlank: bool(v.skipBlank), + }) + } + case 'wrap': + return all({ + kind: 'wrap' as const, + width: finite(v.width), + breakWords: bool(v.breakWords), + preserveIndent: bool(v.preserveIndent), + }) + case 'join': { + const op = all({ kind: 'join' as const, separator: str(v.separator) }) + // A fractional or tiny chunk size would step through the lines forever. + return op && withOptional(op, 'chunkSize', v.chunkSize, (n) => int(n, 1, MAX_INDEX)) + } + case 'split': { + const by = v.by + if (!isObj(by)) return null + if (by.t === 'literal') { + const s = str(by.s) + return s === undefined ? null : { kind: 'split', by: { t: 'literal', s } } + } + if (by.t === 'regex') { + const parsed = all({ t: 'regex' as const, source: str(by.source), flags: str(by.flags, 16) }) + return parsed && { kind: 'split', by: parsed } + } + return null + } + case 'trim': + return all({ + kind: 'trim' as const, + leading: bool(v.leading), + trailing: bool(v.trailing), + collapseInternal: bool(v.collapseInternal), + }) + case 'normalize': + return all({ kind: 'normalize' as const, form: oneOf(v.form, FORMS) }) + case 'setop': + return all({ + kind: 'setop' as const, + op: oneOf(v.op, SET_OPS), + other: str(v.other, MAX_LIST_CHARS), + caseSensitive: bool(v.caseSensitive), + bag: bool(v.bag), + }) + case 'slice': { + const op = all({ kind: 'slice' as const, from: finite(v.from) }) + return op && withOptional(op, 'to', v.to, finite) + } + } +} + +/** + * Add an optional field: absent stays absent, present must parse. A present + * but invalid value rejects the op rather than being silently dropped, so a + * link never runs as something other than what was shared. + */ +function withOptional( + op: T, + key: K, + raw: unknown, + parse: (v: unknown) => V | undefined, +): (T & { [P in K]?: V }) | null { + if (raw === undefined) return op + const value = parse(raw) + return value === undefined ? null : { ...op, [key]: value } +} + +/** Every op in an untrusted array, or `null` if the array or any op is malformed. */ +export function parseOps(value: unknown): Op[] | null { + if (!Array.isArray(value) || value.length > MAX_SHARED_STEPS) return null + const out: Op[] = [] + for (const item of value) { + const op = parseOp(item) + if (op === null) return null + out.push(op) + } + return out +} + +/** + * The steps inside a decoded payload (`{ v: 1, steps: [{ op, on }] }`), or + * `null` for anything else -- including a newer payload version and an empty + * pipeline, which is nothing to load. + */ +export function parseSharedSteps(value: unknown): SharedStep[] | null { + if (!isObj(value) || value.v !== PAYLOAD_VERSION) return null + const raw = value.steps + if (!Array.isArray(raw) || raw.length === 0 || raw.length > MAX_SHARED_STEPS) return null + const out: SharedStep[] = [] + for (const item of raw) { + if (!isObj(item)) return null + const op = parseOp(item.op) + const enabled = bool(item.on) + if (op === null || enabled === undefined) return null + out.push({ op, enabled }) + } + return out +} + +/** + * The fragment value (without `#p=`) for these steps, or `null` when they + * cannot travel: none at all, or a value past the bounds a receiver would + * reject. Encoding runs the same validator as decoding, so every link this + * produces is one `decodePipeline` accepts. + */ +export async function encodePipeline(steps: readonly SharedStep[]): Promise { + // Round-tripped through the parser so stray fields are dropped here too. + const checked = parseSharedSteps(toPayload(steps)) + return checked === null ? null : encodeCompact(toPayload(checked)) +} + +function toPayload(steps: readonly SharedStep[]) { + return { v: PAYLOAD_VERSION, steps: steps.map((s) => ({ op: s.op, on: s.enabled })) } +} + +/** + * Read a pipeline link from a location hash (`#p=…`, or the bare value). + * `null` for anything that is not an intact link this version wrote. Never + * throws. + */ +export async function decodePipeline(hash: string): Promise { + const parsed = await decodeCompact(fragmentValue(hash, PIPELINE_PARAM), { + maxDecodedBytes: MAX_PIPELINE_DECODED_BYTES, + }) + return parseSharedSteps(parsed) +} diff --git a/src/tools/list/ListTool.tsx b/src/tools/list/ListTool.tsx index 8807f4c..8323e2e 100644 --- a/src/tools/list/ListTool.tsx +++ b/src/tools/list/ListTool.tsx @@ -1,5 +1,6 @@ import type { Remote } from 'comlink' -import { useDeferredValue, useMemo } from 'react' +import { Check, Link } from 'lucide-react' +import { useDeferredValue, useEffect, useMemo, useState } from 'react' import { useLocation, useNavigate } from 'react-router' import { ToolFrame } from '@/components/layout/ToolFrame' import { TwoPane } from '@/components/layout/TwoPane' @@ -11,13 +12,18 @@ import { useToolState } from '@/lib/persist/useToolState' import { useShortcuts } from '@/lib/keys/useShortcuts' import { useToolUsageTracker } from '@/lib/prefs' import { copyText } from '@/lib/util/clipboard' -import { formatCount } from '@/lib/util/bytes' +import { cn } from '@/lib/util/cn' +import { formatBytes, formatCount } from '@/lib/util/bytes' import { countStats } from '@fadeltd/devtools-core/text' import { useWorkerJob, type JobStatus } from '@/lib/worker/useWorkerJob' import { parseLines, + decodePipeline, defaultOp, + encodePipeline, isOpKind, + MAX_PIPELINE_CHARS, + PIPELINE_PARAM, withRouteKind, type Step, type Op, @@ -54,6 +60,34 @@ function runInWorker(api: Remote, input: Input): Promise('list', INITIAL) + const [notice, setNotice] = useState(null) + const [linkCopied, setLinkCopied] = useState(false) const routeSegment = location.pathname.split('/')[2] ?? '' const routeKind = isOpKind(routeSegment) ? routeSegment : undefined + + // Opened from a pipeline link: replace the steps, never the input text. + // List state lives in localStorage and is read synchronously on the first + // render, so it is already in place before this resolves and cannot land on + // top of it; and setState marks the state dirty, so even an async (idb) + // hydration would leave the loaded steps alone -- as in Diff. + useEffect(() => { + let cancelled = false + function load() { + const hash = window.location.hash + if (!hash.startsWith(PIPELINE_PREFIX)) return + void decodePipeline(hash).then((shared) => { + // A newer link may have arrived meanwhile; only the latest one counts. + if (cancelled || window.location.hash !== hash) return + if (shared === null) { + // The fragment stays, so the broken link can still be copied and reported. + setNotice({ tone: 'error', text: 'That pipeline link is damaged or from a newer version.' }) + return + } + setState((p) => ({ ...p, steps: shared.map((s) => ({ ...s, id: crypto.randomUUID() })) })) + const n = shared.length + setNotice({ + tone: 'info', + text: `Loaded a shared pipeline (${n} ${n === 1 ? 'step' : 'steps'}). It replaced your previous steps; your input is unchanged.`, + }) + // Drop the fragment so a reload does not reapply it over later edits. + // On /list/ a single loaded step would be swapped for that kind, + // so leave the deep link too; navigate() drops the fragment by itself. + if (routeKind !== undefined) void navigate('/list', { replace: true }) + // history.state is kept: React Router stores its index there. + else window.history.replaceState(window.history.state, '', window.location.pathname + window.location.search) + }) + } + load() + // A link pasted into the address bar of an open List tab is a same-page + // fragment change, not a load. + window.addEventListener('hashchange', load) + return () => { + cancelled = true + window.removeEventListener('hashchange', load) + } + }, [setState, navigate, routeKind]) + // Memoised: a fresh array here would re-key the worker input on every render, // and every result would then trigger another run. const steps = useMemo(() => withRouteKind(state.steps, routeKind), [state.steps, routeKind]) @@ -109,6 +188,40 @@ export default function ListTool() { } const ops = useMemo(() => steps.filter((s) => s.enabled).map((s) => s.op), [steps]) + + // Built ahead of the click: compression is async, and Safari refuses a + // clipboard write that happens after an await. + const shareSteps = useDeferredValue(steps) + const [encoded, setEncoded] = useState<{ steps: readonly Step[]; value: string | null } | null>(null) + useEffect(() => { + if (shareSteps.length === 0) return + let cancelled = false + void encodePipeline(shareSteps).then((value) => { + if (!cancelled) setEncoded({ steps: shareSteps, value }) + }) + return () => { + cancelled = true + } + }, [shareSteps]) + + let share: ShareLink + if (steps.length === 0) share = { problem: 'empty' } + else if (encoded?.steps !== steps) share = { problem: 'pending' } + else if (encoded.value === null) share = { problem: 'invalid' } + else if (encoded.value.length > MAX_PIPELINE_CHARS) share = { problem: 'tooLarge', chars: encoded.value.length } + // Always /list: on /list/ a one-step link would be swapped for that kind. + else share = { url: `${window.location.origin}/list${PIPELINE_PREFIX}${encoded.value}` } + const carriesList = steps.some((s) => s.op.kind === 'setop' && s.op.other !== '') + + function copyLink() { + if (!('url' in share)) return + // Called straight from the click: Safari refuses a later clipboard write. + void copyText(share.url).then((ok) => { + if (!ok) return + setLinkCopied(true) + setTimeout(() => setLinkCopied(false), 1200) + }) + } // Keeps typing responsive: the worker runs against a deferred snapshot. const deferredText = useDeferredValue(state.text) const input = useMemo( @@ -169,65 +282,97 @@ export default function ListTool() { } > - - setState((p) => ({ ...p, text: e.target.value }))} - placeholder={'Paste your list here, one item per line…'} - /> -

- {formatCount(inputLines)} lines in - {inputDoc.eol === 'mixed' && ( - - mixed line endings - - )} - {inputDoc.bom && BOM} -
-
- } - middle={ -
-
- setState((p) => ({ ...p, shuffleNonce: p.shuffleNonce + 1 }))} - /> -
- {/* On a phone the output footer is on another tab; Cancel must be - reachable from where the regex is being typed. */} - {(job.status === 'slow' || job.status === 'cancelled' || job.status === 'error') && ( -
- -
+
+ {notice !== null && ( +
+ {notice.text} +
- } - right={ -
- -
- - {formatCount(inputLines)} → {formatCount(outputLines)} lines - - {formatCount(counts.words)} words - {formatCount(counts.graphemes)} chars - {formatCount(counts.bytes)} bytes - {result !== undefined && result.collisions > 0 && ( - - {result.collisions} differ only by Unicode form - - )} - -
-
- } - /> + )} +
+ + setState((p) => ({ ...p, text: e.target.value }))} + placeholder={'Paste your list here, one item per line…'} + /> +
+ {formatCount(inputLines)} lines in + {inputDoc.eol === 'mixed' && ( + + mixed line endings + + )} + {inputDoc.bom && BOM} +
+
+ } + middle={ +
+
+ setState((p) => ({ ...p, shuffleNonce: p.shuffleNonce + 1 }))} + /> +
+
+ + + {shareHint(share, carriesList)} + +
+ {/* On a phone the output footer is on another tab; Cancel must be + reachable from where the regex is being typed. */} + {(job.status === 'slow' || job.status === 'cancelled' || job.status === 'error') && ( +
+ +
+ )} +
+ } + right={ +
+ +
+ + {formatCount(inputLines)} → {formatCount(outputLines)} lines + + {formatCount(counts.words)} words + {formatCount(counts.graphemes)} chars + {formatCount(counts.bytes)} bytes + {result !== undefined && result.collisions > 0 && ( + + {result.collisions} differ only by Unicode form + + )} + +
+
+ } + /> +
+
) } From 97b53c2f4b2cda971663d4571b7f2f6010b03bdf Mon Sep 17 00:00:00 2001 From: fadeltd Date: Fri, 30 Oct 2026 01:23:38 +0700 Subject: [PATCH 04/14] Diff: sort-lines and JSON-normalise transforms; refuse oversize input before CodeMirror sees it The size guard now runs on the live text instead of a deferred copy, so a 5 MB single-line file that is picked, dropped, restored from IndexedDB or opened from a share link is refused in the same render and never reaches CodeMirror. assessSize scans lines with indexOf (5 MB single line: 14.8 ms to 0.1 ms) and its thresholds are exported as MAX_DIFF_CHARS, MAX_DIFF_LINE_CHARS and LINE_ONLY_CHARS. MergePane gets a transaction filter that refuses an edit (typically a paste) that would push a side past those limits, with a notice, so the editor never lays out the oversize line first. MergePane no longer echoes text pushed in from React back through the change callbacks, tracks the last text per side by reference instead of comparing doc.toString() on every change (which also stops CRLF input from being re-pushed into the other pane on each keystroke), keeps the unified view's original document in sync with the left side, and accepts a readOnly prop. New core transformForDiff(text, 'sort-lines' | 'json'): sort-lines uses the List tool's natural sort with a code-point tie-break, so the output depends only on the multiset of lines, and keeps CRLF, BOM and the final newline; json accepts comments and trailing commas, deep-sorts keys, prints with two-space indent, keeps big integers losslessly where JSON.rawJSON exists and otherwise says how many numbers were rounded, and reports invalid input with a line and column. The Diff tool gets a "Compare as" select (As written / Sorted lines / Key-sorted JSON). While a transform is active both panes show the rewritten text read-only under a persistent banner with a Show original button and per-side JSON errors; stats and the copied patch use the shown text and the button says so; the share link keeps the original text; the size guard applies to the rewritten text. --- packages/core/src/diff/index.ts | 1 + packages/core/src/diff/stats.test.ts | 41 +++++ packages/core/src/diff/stats.ts | 28 ++- packages/core/src/diff/transform.test.ts | 208 +++++++++++++++++++++++ packages/core/src/diff/transform.ts | 141 +++++++++++++++ src/tools/diff/DiffTool.tsx | 92 ++++++++-- src/tools/diff/MergePane.tsx | 150 ++++++++++++---- src/tools/diff/view.ts | 77 +++++++++ 8 files changed, 681 insertions(+), 57 deletions(-) create mode 100644 packages/core/src/diff/stats.test.ts create mode 100644 packages/core/src/diff/transform.test.ts create mode 100644 packages/core/src/diff/transform.ts create mode 100644 src/tools/diff/view.ts diff --git a/packages/core/src/diff/index.ts b/packages/core/src/diff/index.ts index 4f4d5ed..7911432 100644 --- a/packages/core/src/diff/index.ts +++ b/packages/core/src/diff/index.ts @@ -4,3 +4,4 @@ export * from './patch.ts' export * from './share.ts' export * from './stats.ts' export * from './tokens.ts' +export * from './transform.ts' diff --git a/packages/core/src/diff/stats.test.ts b/packages/core/src/diff/stats.test.ts new file mode 100644 index 0000000..63e2a71 --- /dev/null +++ b/packages/core/src/diff/stats.test.ts @@ -0,0 +1,41 @@ +import fc from 'fast-check' +import { describe, expect, it } from 'vitest' +import { LINE_ONLY_CHARS, MAX_DIFF_CHARS, MAX_DIFF_LINE_CHARS, assessSize, longestLineOf } from './stats.ts' + +describe('longestLineOf', () => { + it('measures lines without their terminator', () => { + expect(longestLineOf('')).toBe(0) + expect(longestLineOf('abc')).toBe(3) + expect(longestLineOf('abc\n')).toBe(3) + expect(longestLineOf('a\nbcd\nef')).toBe(3) + expect(longestLineOf('\n\n')).toBe(0) + }) + + it('agrees with splitting on \\n', () => { + fc.assert( + fc.property(fc.string({ unit: fc.constantFrom('a', 'b', '\n', '\r') }), (s) => { + expect(longestLineOf(s)).toBe(Math.max(...s.split('\n').map((l) => l.length))) + }), + ) + }) +}) + +describe('assessSize thresholds', () => { + it('refuses exactly past the longest-line limit', () => { + expect(assessSize('x'.repeat(MAX_DIFF_LINE_CHARS), '').tier).toBe('ok') + expect(assessSize('', 'x'.repeat(MAX_DIFF_LINE_CHARS + 1)).tier).toBe('refuse') + }) + + it('refuses a 5 MB single line, the case that hangs CodeMirror', () => { + const v = assessSize('a'.repeat(5_000_000), 'short') + expect(v.tier).toBe('refuse') + expect(v.longestLine).toBe(5_000_000) + }) + + it('turns highlighting off past the line-only size, and refuses past the hard cap', () => { + const line = 'x'.repeat(99) + '\n' + const over = (n: number) => line.repeat(Math.ceil((n + 1) / line.length)) + expect(assessSize(over(LINE_ONLY_CHARS), '').tier).toBe('line-only') + expect(assessSize(over(MAX_DIFF_CHARS), '').tier).toBe('refuse') + }) +}) diff --git a/packages/core/src/diff/stats.ts b/packages/core/src/diff/stats.ts index e8ef65f..6db19fe 100644 --- a/packages/core/src/diff/stats.ts +++ b/packages/core/src/diff/stats.ts @@ -106,31 +106,42 @@ export interface SizeVerdict { longestLine: number } +/** Past this many characters on either side, the diff is refused outright. */ +export const MAX_DIFF_CHARS = 8_000_000 +/** Past this many characters on one line, the diff is refused: CodeMirror cannot lay it out. */ +export const MAX_DIFF_LINE_CHARS = 200_000 +/** Past this many characters on either side, word-level highlighting is turned off. */ +export const LINE_ONLY_CHARS = 2_000_000 + /** * Guardrails, so a pathological input degrades visibly instead of hanging. * * The single-enormous-line case matters as much as total size: CodeMirror * handles a 5MB document fine but not a 5MB document that is one line. + * + * Linear and cheap (the line scan is `indexOf`, not a character loop), so the + * UI can run it on the live text before anything reaches the editor, rather + * than on a deferred copy that lags a render behind. */ export function assessSize(a: string, b: string): SizeVerdict { const longestLine = Math.max(longestLineOf(a), longestLineOf(b)) const bytes = Math.max(a.length, b.length) - if (bytes > 8_000_000) { + if (bytes > MAX_DIFF_CHARS) { return { tier: 'refuse', reason: 'Each side must be under 8 MB. Use `git diff` or `diff` on the command line.', longestLine, } } - if (longestLine > 200_000) { + if (longestLine > MAX_DIFF_LINE_CHARS) { return { tier: 'refuse', reason: `One line is ${longestLine.toLocaleString('en-US')} characters long, which cannot be displayed usefully.`, longestLine, } } - if (bytes > 2_000_000) { + if (bytes > LINE_ONLY_CHARS) { return { tier: 'line-only', reason: 'Over 2 MB — word-level highlighting is off to keep the view responsive.', @@ -140,14 +151,13 @@ export function assessSize(a: string, b: string): SizeVerdict { return { tier: 'ok', reason: null, longestLine } } -function longestLineOf(text: string): number { +/** Length of the longest `\n`-separated line, not counting the terminator. */ +export function longestLineOf(text: string): number { let longest = 0 let start = 0 - for (let i = 0; i < text.length; i++) { - if (text[i] === '\n') { - if (i - start > longest) longest = i - start - start = i + 1 - } + for (let nl = text.indexOf('\n'); nl !== -1; nl = text.indexOf('\n', start)) { + if (nl - start > longest) longest = nl - start + start = nl + 1 } return Math.max(longest, text.length - start) } diff --git a/packages/core/src/diff/transform.test.ts b/packages/core/src/diff/transform.test.ts new file mode 100644 index 0000000..a789983 --- /dev/null +++ b/packages/core/src/diff/transform.test.ts @@ -0,0 +1,208 @@ +import fc from 'fast-check' +import { describe, expect, it } from 'vitest' +import { sortKeysDeep, withSortedKeys } from '../json/format.ts' +import { losslessSupported } from '../json/rawjson.ts' +import { parseLines } from '../list/lines.ts' +import { transformForDiff, type TransformResult } from './transform.ts' + +function textOf(r: TransformResult): string { + if (!r.ok) throw new Error(`expected ok, got: ${r.error}`) + return r.text +} + +const collate = new Intl.Collator(undefined, { numeric: true, sensitivity: 'variant' }).compare +const keyRef = (a: string, b: string) => collate(a, b) || (a < b ? -1 : a > b ? 1 : 0) + +const sorted = (s: string) => textOf(transformForDiff(s, 'sort-lines')) +const json = (s: string, lossless = true) => textOf(transformForDiff(s, 'json', { lossless })) + +describe('sort-lines', () => { + it('sorts in natural order', () => { + expect(sorted('item10\nitem9\nitem1\n')).toBe('item1\nitem9\nitem10\n') + }) + + it('ignores case for ordering, but orders case variants deterministically', () => { + expect(sorted('b\nA\na\nB\n')).toBe(sorted('B\na\nA\nb\n')) + expect(sorted('b\na\n')).toBe('a\nb\n') + }) + + it('does not invent a phantom blank line from the trailing newline', () => { + expect(sorted('b\na\n')).toBe('a\nb\n') + expect(parseLines(sorted('b\na\n')).lines).toEqual(['a', 'b']) + }) + + it('keeps the presence or absence of a final newline', () => { + expect(sorted('b\na')).toBe('a\nb') + expect(sorted('b\na\n')).toBe('a\nb\n') + }) + + it('keeps CRLF and never leaves a \\r glued to a line', () => { + expect(sorted('b\r\na\r\n')).toBe('a\r\nb\r\n') + expect(sorted('b\ra\r')).toBe('a\rb\r') + }) + + it('normalises mixed endings to LF and says so', () => { + const r = transformForDiff('b\r\na\n', 'sort-lines') + expect(r).toEqual({ ok: true, text: 'a\nb\n', notes: ['Mixed line endings were normalised to LF.'] }) + }) + + it('keeps a byte-order mark at the front', () => { + expect(sorted('b\na\n')).toBe('a\nb\n') + }) + + it('handles empty input and a lone newline', () => { + expect(sorted('')).toBe('') + expect(sorted('\n')).toBe('\n') + }) + + it('sorts blank lines with everything else', () => { + expect(sorted('b\n\na\n')).toBe('\na\nb\n') + }) + + it('is idempotent', () => { + fc.assert( + fc.property(fc.string(), (s) => { + const once = sorted(s) + expect(sorted(once)).toBe(once) + }), + ) + }) + + it('preserves the multiset of lines', () => { + fc.assert( + fc.property(fc.array(fc.string({ unit: 'grapheme' }).map((l) => l.replace(/[\r\n]/g, ''))), (lines) => { + const out = parseLines(sorted(lines.join('\n'))).lines + const before = parseLines(lines.join('\n')).lines + expect(out.toSorted()).toEqual(before.toSorted()) + }), + ) + }) + + it('depends only on the multiset, not the input order', () => { + const line = fc.string({ unit: 'grapheme-ascii' }).map((l) => l.replace(/[\r\n]/g, '')) + fc.assert( + fc.property(fc.array(line, { minLength: 1 }), fc.integer(), (lines, seed) => { + const shuffled = lines + .map((l, i) => ({ l, k: Math.sin(seed + i) })) + .toSorted((a, b) => a.k - b.k) + .map((x) => x.l) + expect(sorted(shuffled.join('\n') + '\n')).toBe(sorted(lines.join('\n') + '\n')) + }), + ) + }) + + it('never reports a failure', () => { + fc.assert( + fc.property(fc.string({ unit: 'binary' }), (s) => { + expect(transformForDiff(s, 'sort-lines').ok).toBe(true) + }), + ) + }) +}) + +describe('json', () => { + it('sorts keys deeply and indents by two spaces', () => { + expect(json('{"b":1,"a":{"d":[3,1],"c":null}}')).toBe( + '{\n "a": {\n "c": null,\n "d": [\n 3,\n 1\n ]\n },\n "b": 1\n}\n', + ) + }) + + it('never reorders arrays', () => { + expect(JSON.parse(json('[3,1,2]'))).toEqual([3, 1, 2]) + }) + + it('makes key order and formatting irrelevant', () => { + expect(json('{"a":1,"b":[true,false]}')).toBe(json('{\n\t"b": [ true, false ],\n\t"a": 1\n}')) + }) + + it('leaves an empty side empty', () => { + expect(transformForDiff('', 'json')).toEqual({ ok: true, text: '', notes: [] }) + expect(transformForDiff(' \n', 'json')).toEqual({ ok: true, text: ' \n', notes: [] }) + }) + + it('accepts comments and trailing commas, and says they were removed', () => { + const r = transformForDiff('{\n // why\n "b": 1,\n "a": 2,\n}', 'json') + expect(r).toEqual({ + ok: true, + text: '{\n "a": 2,\n "b": 1\n}\n', + notes: ['Comments and trailing commas were removed.'], + }) + }) + + it('reports invalid JSON with a line and column', () => { + const r = transformForDiff('{\n "a": 1\n "b": 2\n}', 'json') + expect(r.ok).toBe(false) + if (r.ok) return + expect(r.error).toMatch(/^Expected a comma between items at line 3, column 3$/) + }) + + it('points at an unquoted key', () => { + const r = transformForDiff('{a: 1}', 'json') + expect(r.ok).toBe(false) + if (!r.ok) expect(r.error).toContain('line 1, column 2') + }) + + it('recognises NDJSON as not a single document', () => { + const r = transformForDiff('{"a":1}\n{"a":2}\n', 'json') + expect(r.ok).toBe(false) + if (!r.ok) expect(r.error).toContain('NDJSON') + }) + + it.runIf(losslessSupported)('keeps big integers exactly where the platform allows', () => { + const r = transformForDiff('{"id":12345678901234567890,"n":1}', 'json') + expect(r).toEqual({ ok: true, text: '{\n "id": 12345678901234567890,\n "n": 1\n}\n', notes: [] }) + }) + + it('without lossless parsing, says how many numbers were rounded or reprinted', () => { + const r = transformForDiff('[12345678901234567890, 98765432109876543210, -9223372036854775808]', 'json', { + lossless: false, + }) + expect(r.ok).toBe(true) + if (!r.ok) return + expect(r.notes).toEqual([ + '2 numbers are too large for this browser to keep exactly and may have been rounded.', + '1 number is printed differently, with the same value.', + ]) + }) + + it('is idempotent on arbitrary JSON', () => { + fc.assert( + fc.property(fc.jsonValue(), fc.boolean(), (v, lossless) => { + const once = json(JSON.stringify(v), lossless) + expect(json(once, lossless)).toBe(once) + }), + ) + }) + + it('is idempotent with big integers', () => { + const big = fc.bigInt({ min: -(10n ** 30n), max: 10n ** 30n }).map(String) + fc.assert( + fc.property(fc.array(big), fc.boolean(), (nums, lossless) => { + const once = json(`{"z":[${nums.join(',')}],"a":0}`, lossless) + expect(json(once, lossless)).toBe(once) + }), + ) + }) + + it('re-parses to the key-sorted input value', () => { + fc.assert( + fc.property(fc.jsonValue(), (v) => { + const input = JSON.stringify(v) + const out = json(input) + // Stringify both sides so key order is compared, not just structure. + expect(JSON.stringify(JSON.parse(out))).toBe(JSON.stringify(sortKeysDeep(JSON.parse(input), keyRef))) + // Which is the JSON tool's key sort, up to the tie-break. + expect(JSON.parse(out)).toEqual(withSortedKeys(JSON.parse(input))) + }), + ) + }) + + it('makes any key order produce the same text', () => { + fc.assert( + fc.property(fc.dictionary(fc.string(), fc.jsonValue()), (obj) => { + const reversed = Object.fromEntries(Object.entries(obj).toReversed()) + expect(json(JSON.stringify(reversed))).toBe(json(JSON.stringify(obj))) + }), + ) + }) +}) diff --git a/packages/core/src/diff/transform.ts b/packages/core/src/diff/transform.ts new file mode 100644 index 0000000..05079fb --- /dev/null +++ b/packages/core/src/diff/transform.ts @@ -0,0 +1,141 @@ +import { analyze, stripJsonc } from '../json/errors.ts' +import { findNumberIssues, formatValue, sortKeysDeep } from '../json/format.ts' +import { losslessSupported, parseLossless } from '../json/rawjson.ts' +import { comparatorFor } from '../list/compare.ts' +import { parseLines, serializeLines, withLines } from '../list/lines.ts' +import { defaultOp } from '../list/ops.ts' + +/** + * Rewrites applied to each side before diffing, so that two documents which + * differ only in line order, or only in JSON key order and formatting, compare + * as identical. + * + * These are transforms, not diff options: they change the text that is shown, + * which is why the UI makes both panes read-only while one is active. + */ +export type DiffTransform = 'sort-lines' | 'json' + +export interface TransformOptions { + /** + * Keep integers a double cannot hold as their literal digits. On by default + * where the platform supports it (`JSON.rawJSON`); ignored where it does not. + */ + lossless?: boolean +} + +export type TransformResult = + | { + ok: true + text: string + /** Things the reader should know about the rewritten text, e.g. rounded numbers. */ + notes: string[] + } + | { ok: false; error: string } + +export function transformForDiff( + text: string, + kind: DiffTransform, + options: TransformOptions = {}, +): TransformResult { + switch (kind) { + case 'sort-lines': + return sortLines(text) + case 'json': + return normalizeJson(text, (options.lossless ?? true) && losslessSupported) + } +} + +type Compare = (a: string, b: string) => number + +let lineOrder: Compare | null = null + +/** + * The List tool's default sort (natural, case-insensitive), with a code-point + * tie-break. The collator calls `a` and `A`, or `1` and `01`, equal, and a + * stable sort then keeps them in input order -- so the same lines in a + * different order would sort to different text and show a spurious change. + * The tie-break makes the output depend only on the multiset of lines. + * + * Built on first use: constructing a collator is not free, and the package is + * side-effect free. + */ +function compareLines(): Compare { + if (lineOrder !== null) return lineOrder + const op = defaultOp('sort') + if (op.kind !== 'sort') throw new Error('defaultOp("sort") is not a sort') + const natural = comparatorFor(op) + lineOrder = (a, b) => natural(a, b) || (a < b ? -1 : a > b ? 1 : 0) + return lineOrder +} + +let keyOrder: Compare | null = null + +/** + * The JSON tool's key order (`withSortedKeys`: natural, case-sensitive), with + * the same code-point tie-break as lines: numeric collation calls `"02"` and + * `"2"` equal, and their input order must not leak into the output. + */ +function compareKeys(): Compare { + if (keyOrder !== null) return keyOrder + const natural = new Intl.Collator(undefined, { numeric: true, sensitivity: 'variant' }).compare + keyOrder = (a, b) => natural(a, b) || (a < b ? -1 : a > b ? 1 : 0) + return keyOrder +} + +function sortLines(text: string): TransformResult { + const doc = parseLines(text) + const notes: string[] = [] + // There is no single faithful ending for mixed input; serializeLines uses LF. + if (doc.eol === 'mixed') notes.push('Mixed line endings were normalised to LF.') + const sorted = withLines(doc, doc.lines.toSorted(compareLines())) + return { ok: true, text: serializeLines(sorted), notes } +} + +function plural(n: number, one: string, many: string): string { + return `${n.toLocaleString('en-US')} ${n === 1 ? one : many}` +} + +function normalizeJson(text: string, lossless: boolean): TransformResult { + // An empty side stays empty: comparing a document against nothing is normal. + if (text.trim() === '') return { ok: true, text, notes: [] } + + // Lenient, so a config file with comments (tsconfig.json) still compares. + const analysis = analyze(text, { lenient: true }) + if (analysis.value === undefined) { + const issue = analysis.issues[0] + const where = issue === undefined ? '' : ` at line ${issue.line}, column ${issue.column}` + const message = issue?.message ?? 'Could not parse JSON' + const ndjson = analysis.ndjson === null ? '' : ' (it looks like NDJSON: one value per line)' + return { ok: false, error: `${message}${where}${ndjson}` } + } + + const notes: string[] = [] + const strict = analysis.flavor === 'jsonc' ? stripJsonc(text) : text + if (analysis.flavor === 'jsonc') notes.push('Comments and trailing commas were removed.') + + // Same approach as the JSON tool: only when the cheap number scan trips, + // re-parse keeping those literals as written. + let value: unknown = analysis.value + const issues = findNumberIssues(strict, Number.POSITIVE_INFINITY) + if (issues.length > 0) { + if (lossless) { + value = parseLossless(strict) + } else { + const rounded = issues.filter((n) => n.kind === 'precision').length + const reprinted = issues.length - rounded + if (rounded > 0) { + notes.push( + `${plural(rounded, 'number is', 'numbers are')} too large for this browser to keep exactly and may have been rounded.`, + ) + } + if (reprinted > 0) { + notes.push(`${plural(reprinted, 'number is', 'numbers are')} printed differently, with the same value.`) + } + } + } + + // A final newline, as a file would have, so a copied patch does not end in + // "\ No newline at end of file". + const canonical = sortKeysDeep(value, compareKeys()) + return { ok: true, text: formatValue(canonical, { indent: 2, sortKeys: false }) + '\n', notes } +} diff --git a/src/tools/diff/DiffTool.tsx b/src/tools/diff/DiffTool.tsx index 34e1824..3983c03 100644 --- a/src/tools/diff/DiffTool.tsx +++ b/src/tools/diff/DiffTool.tsx @@ -3,7 +3,7 @@ import { ArrowLeftRight, Check, ChevronDown, ChevronUp, FileDiff, Link, Upload } import { ToolFrame } from '@/components/layout/ToolFrame' import { Badge } from '@/components/ui/Badge' import { Button, Segmented } from '@/components/ui/Button' -import { Toggle } from '@/components/ui/Select' +import { Select, Toggle } from '@/components/ui/Select' import { useToolState } from '@/lib/persist/useToolState' import { useShortcuts } from '@/lib/keys/useShortcuts' import { useToolUsageTracker } from '@/lib/prefs' @@ -15,7 +15,6 @@ import { ignoreLabel, type IgnoreOptions, toUnifiedPatch, - assessSize, diffStats, MAX_SHARE_CHARS, SHARE_PARAM, @@ -23,6 +22,7 @@ import { encodeShare, } from '@fadeltd/devtools-core/diff' import { diffFor } from './engine' +import { prepareView, TRANSFORM_BANNERS, TRANSFORM_LABELS, type Transform } from './view' interface State { left: string @@ -34,6 +34,8 @@ interface State { ignoreWhitespace: boolean ignoreCase: boolean trim: boolean + /** A rewrite of both sides before diffing. The input itself is never changed. */ + transform: Transform } const INITIAL: State = { @@ -44,6 +46,19 @@ const INITIAL: State = { ignoreWhitespace: false, ignoreCase: false, trim: false, + transform: 'none', +} + +const TRANSFORMS: readonly Transform[] = ['none', 'sort-lines', 'json'] + +function isTransform(v: string): v is Transform { + return (TRANSFORMS as readonly string[]).includes(v) +} + +const PATCH_LABELS: Record = { + none: 'Patch', + 'sort-lines': 'Patch (sorted view)', + json: 'Patch (JSON view)', } function ignoreOf(s: State): IgnoreOptions { @@ -121,14 +136,22 @@ export default function DiffTool() { ? { tooLarge: encoded.value.length } : { url: window.location.origin + window.location.pathname + SHARE_PREFIX + encoded.value } } - const size = useMemo(() => assessSize(deferred.left, deferred.right), [deferred]) + // What the panes show, and whether they may show it at all, decided on the + // live text: see prepareView. Only the stats below run on a deferred copy. + const { left, right, transform } = state + const view = useMemo(() => prepareView(left, right, transform), [left, right, transform]) + const size = view.size + const transforming = transform !== 'none' + + const deferredView = useDeferredValue(view) const stats = useMemo(() => { - if (size.tier === 'refuse') return null - if (deferred.left === '' && deferred.right === '') return null - const settings = { ignore: ignoreOf(deferred), refine: size.tier === 'ok' } - return diffStats(deferred.left, deferred.right, diffFor(deferred.left, deferred.right, settings).lines) - }, [deferred, size.tier]) + const { left: a, right: b, size: verdict } = deferredView + if (verdict.tier === 'refuse') return null + if (a === '' && b === '') return null + const settings = { ignore: ignoreOf(deferred), refine: verdict.tier === 'ok' } + return diffStats(a, b, diffFor(a, b, settings).lines) + }, [deferredView, deferred]) const ignoring = ignoreLabel(ignoreOf(state)) @@ -144,7 +167,8 @@ export default function DiffTool() { function copyPatch() { // Built and written inside the click handler: Safari refuses a clipboard // write that happens anywhere else. - const patch = toUnifiedPatch(state.left, state.right) + // Of what is shown: in a transform, the sorted text, as the label says. + const patch = toUnifiedPatch(view.left, view.right) if (patch === null) { setError('These inputs are too different to build a patch in time. Use `diff -u` on the command line.') return @@ -262,6 +286,21 @@ export default function DiffTool() { setState((p) => ({ ...p, trim: v }))}> Trim + {stats !== null && ( @@ -305,15 +344,19 @@ export default function DiffTool() {
)} + {transforming && ( + // Not dismissible: while it is up, the panes are not your text. +
+
+ {TRANSFORM_BANNERS[transform]} Both sides are read-only; your text is unchanged. + +
+ {view.problems.map((problem) => ( +
+ {problem} +
+ ))} + {view.notes.map((note) => ( +
+ {note} +
+ ))} +
+ )} {size.reason !== null && (
{size.reason} @@ -363,14 +425,16 @@ export default function DiffTool() {
setState((p) => ({ ...p, left: value }))} onChangeRight={(value) => setState((p) => ({ ...p, right: value }))} + onRejectEdit={setError} />
)} diff --git a/src/tools/diff/MergePane.tsx b/src/tools/diff/MergePane.tsx index 8302fa0..ed4788b 100644 --- a/src/tools/diff/MergePane.tsx +++ b/src/tools/diff/MergePane.tsx @@ -1,9 +1,16 @@ import { useEffect, useRef } from 'react' -import { EditorState, type Extension } from '@codemirror/state' +import { Annotation, ChangeSet, EditorState, type Extension, type Transaction } from '@codemirror/state' import { EditorView, keymap, lineNumbers } from '@codemirror/view' -import { MergeView, goToNextChunk, goToPreviousChunk, unifiedMergeView } from '@codemirror/merge' +import { + MergeView, + getOriginalDoc, + goToNextChunk, + goToPreviousChunk, + originalDocChangeEffect, + unifiedMergeView, +} from '@codemirror/merge' import { diffTheme } from './theme' -import type { IgnoreOptions } from '@fadeltd/devtools-core/diff' +import { MAX_DIFF_CHARS, MAX_DIFF_LINE_CHARS, type IgnoreOptions } from '@fadeltd/devtools-core/diff' import { makeDiffConfig } from './engine' export interface DiffApi { @@ -20,8 +27,12 @@ export interface MergePaneProps { highlightChanges: boolean /** What counts as equal. Changing it rebuilds the view (see below). */ ignore: IgnoreOptions + /** Shows the text without accepting edits, e.g. a sorted view of the input. */ + readOnly: boolean onChangeLeft: (value: string) => void onChangeRight: (value: string) => void + /** An edit was refused because the result would be too large to display. */ + onRejectEdit: (reason: string) => void /** Called with a handle for chunk navigation on mount, and null on unmount. */ onReady?: (api: DiffApi | null) => void } @@ -33,12 +44,68 @@ const chunkKeymap = keymap.of([ { key: 'Shift-F8', run: (v) => goToPreviousChunk(v), preventDefault: true }, ]) -function baseExtensions(wrapLines: boolean): Extension[] { - const ext: Extension[] = [lineNumbers(), diffTheme, chunkKeymap, EditorView.editable.of(true)] +/** Marks text pushed in from React, which must not be echoed back as an edit. */ +const external = Annotation.define() + +/** + * Why an edit would push this side past what the diff tool will display, or + * null if it is fine. Only the lines the edit touched are measured, so typing + * stays O(edit) rather than O(document). + */ +function oversize(tr: Transaction): string | null { + const doc = tr.newDoc + if (doc.length > MAX_DIFF_CHARS) { + return 'That edit was not applied: each side must stay under 8 MB.' + } + let longest = 0 + tr.changes.iterChangedRanges((_fromA, _toA, fromB, toB) => { + for (let pos = fromB; ; ) { + const line = doc.lineAt(pos) + if (line.length > longest) longest = line.length + if (line.to >= toB) break + pos = line.to + 1 + } + }, true) + if (longest <= MAX_DIFF_LINE_CHARS) return null + return `That edit was not applied: it would make one line ${longest.toLocaleString('en-US')} characters long, over the ${MAX_DIFF_LINE_CHARS.toLocaleString('en-US')} that can be displayed.` +} + +function baseExtensions( + wrapLines: boolean, + readOnly: boolean, + onReject: (reason: string) => void, +): Extension[] { + const ext: Extension[] = [ + lineNumbers(), + diffTheme, + chunkKeymap, + // Still editable (focusable, selectable, navigable by chunk) when read-only; + // readOnly is what blocks input. + EditorView.editable.of(true), + EditorState.readOnly.of(readOnly), + // DiffTool refuses oversize text before it reaches the view, but a paste + // lands here first. Without this, CodeMirror would lay out a 5 MB line on + // the main thread before the refusal could unmount it. + EditorState.transactionFilter.of((tr) => { + if (!tr.docChanged || tr.annotation(external)) return tr + const reason = oversize(tr) + if (reason === null) return tr + onReject(reason) + return [] + }), + ] if (wrapLines) ext.push(EditorView.lineWrapping) return ext } +/** Calls `onChange` for the user's edits only, not for text pushed in from React. */ +function changeListener(onChange: (value: string) => void): Extension { + return EditorView.updateListener.of((u) => { + if (!u.docChanged || u.transactions.some((tr) => tr.annotation(external))) return + onChange(u.state.doc.toString()) + }) +} + /** * Imperative wrapper around CodeMirror's MergeView. * @@ -48,12 +115,18 @@ function baseExtensions(wrapLines: boolean): Extension[] { * keystroke would lose the cursor and the scroll position. */ export function MergePane(props: MergePaneProps) { - const { mode, wrapLines, highlightChanges, left, right, onReady } = props + const { mode, wrapLines, highlightChanges, readOnly, left, right, onReady } = props // Primitives, so a fresh `ignore` object each render does not rebuild the view. const { whitespace, case: ignoreCase, trim } = props.ignore const host = useRef(null) const mergeRef = useRef(null) const unifiedRef = useRef(null) + // The text each side last held, whether pushed in or typed. Compared by + // reference instead of `doc.toString()`, which would build two copies of a + // multi-MB document on every keystroke, and which never equals CRLF input + // (CodeMirror normalises line breaks), so the other pane would be replaced + // on each edit. + const seen = useRef({ left: '', right: '' }) // Callbacks and latest values via a ref, so the view is not rebuilt on every // render -- only when the structural options below actually change. Assigned @@ -75,6 +148,16 @@ export function MergePane(props: MergePaneProps) { ignore: { whitespace, case: ignoreCase, trim }, refine: highlightChanges, }) + const base = baseExtensions(wrapLines, readOnly, (reason) => cb.current.onRejectEdit(reason)) + seen.current = { left: cb.current.left, right: cb.current.right } + const onLeft = (value: string) => { + seen.current.left = value + cb.current.onChangeLeft(value) + } + const onRight = (value: string) => { + seen.current.right = value + cb.current.onChangeRight(value) + } if (mode === 'split') { const view = new MergeView({ @@ -87,21 +170,11 @@ export function MergePane(props: MergePaneProps) { // Deliberately no revertControls: you merge in git, not in a browser tab. a: { doc: cb.current.left, - extensions: [ - ...baseExtensions(wrapLines), - EditorView.updateListener.of((u) => { - if (u.docChanged) cb.current.onChangeLeft(u.state.doc.toString()) - }), - ], + extensions: [...base, changeListener(onLeft)], }, b: { doc: cb.current.right, - extensions: [ - ...baseExtensions(wrapLines), - EditorView.updateListener.of((u) => { - if (u.docChanged) cb.current.onChangeRight(u.state.doc.toString()) - }), - ], + extensions: [...base, changeListener(onRight)], }, }) mergeRef.current = view @@ -124,10 +197,8 @@ export function MergePane(props: MergePaneProps) { diffConfig, mergeControls: false, }), - ...baseExtensions(wrapLines), - EditorView.updateListener.of((u) => { - if (u.docChanged) cb.current.onChangeRight(u.state.doc.toString()) - }), + ...base, + changeListener(onRight), ], }), }) @@ -136,24 +207,35 @@ export function MergePane(props: MergePaneProps) { view.destroy() unifiedRef.current = null } - }, [mode, wrapLines, highlightChanges, whitespace, ignoreCase, trim]) + // readOnly rebuilds too: it flips only when a transform swaps in different + // text, which replaces the whole document anyway. + }, [mode, wrapLines, highlightChanges, readOnly, whitespace, ignoreCase, trim]) // Push external text changes in without clobbering an in-progress edit. + // Annotated, so the change listener does not write the text back to state: + // in a transform it is the transformed text, and would overwrite the input. useEffect(() => { const merge = mergeRef.current - if (merge) { - if (merge.a.state.doc.toString() !== left) { - merge.a.dispatch({ changes: { from: 0, to: merge.a.state.doc.length, insert: left } }) - } - if (merge.b.state.doc.toString() !== right) { - merge.b.dispatch({ changes: { from: 0, to: merge.b.state.doc.length, insert: right } }) - } - return + const unified = unifiedRef.current + const replace = (view: EditorView, text: string) => { + view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: text }, annotations: external.of(true) }) } - const unified = unifiedRef.current - if (unified && unified.state.doc.toString() !== right) { - unified.dispatch({ changes: { from: 0, to: unified.state.doc.length, insert: right } }) + if (left !== seen.current.left) { + seen.current.left = left + if (merge) replace(merge.a, left) + else if (unified) { + // The unified view keeps the left side as its "original" document, + // outside the editable doc, so it has its own update path. + const original = getOriginalDoc(unified.state) + const changes = ChangeSet.of({ from: 0, to: original.length, insert: left }, original.length) + unified.dispatch({ effects: originalDocChangeEffect(unified.state, changes), annotations: external.of(true) }) + } + } + if (right !== seen.current.right) { + seen.current.right = right + const view = merge?.b ?? unified + if (view) replace(view, right) } }, [left, right]) diff --git a/src/tools/diff/view.ts b/src/tools/diff/view.ts new file mode 100644 index 0000000..15702a2 --- /dev/null +++ b/src/tools/diff/view.ts @@ -0,0 +1,77 @@ +import { + assessSize, + transformForDiff, + MAX_DIFF_CHARS, + type DiffTransform, + type SizeVerdict, + type TransformResult, +} from '@fadeltd/devtools-core/diff' + +export type Transform = 'none' | DiffTransform + +/** What the panes, the stats and the patch are built from. */ +export interface DiffView { + left: string + right: string + /** Judged on the text actually shown, so a transform's output is guarded too. */ + size: SizeVerdict + /** A side that could not be transformed, and is shown as written instead. */ + problems: string[] + notes: string[] +} + +// One entry per side is enough: in a transform the panes are read-only, so a +// side changes only when a file replaces it, and the other side should not be +// re-parsed or re-sorted when that happens. +const cache: Array<{ text: string; kind: DiffTransform; result: TransformResult }> = [] + +function transformCached(text: string, kind: DiffTransform): TransformResult { + const hit = cache.find((e) => e.kind === kind && e.text === text) + if (hit) return hit.result + const result = transformForDiff(text, kind) + cache.unshift({ text, kind, result }) + cache.length = Math.min(cache.length, 2) + return result +} + +/** + * Decide what reaches CodeMirror, from the live text. + * + * Runs on the current state rather than a deferred copy: a deferred verdict + * lags a render behind, which is exactly long enough for a dropped 5 MB + * single-line file to be laid out on the main thread before it is refused. + * `assessSize` is a couple of `indexOf` scans, so this is cheap per keystroke. + */ +export function prepareView(left: string, right: string, transform: Transform): DiffView { + // Past the hard cap there is nothing to show, so skip parsing or sorting it. + if (transform === 'none' || Math.max(left.length, right.length) > MAX_DIFF_CHARS) { + return { left, right, size: assessSize(left, right), problems: [], notes: [] } + } + + const problems: string[] = [] + const notes: string[] = [] + const side = (text: string, name: 'Left' | 'Right'): string => { + const r = transformCached(text, transform) + if (!r.ok) { + problems.push(`${name} side is not valid JSON: ${r.error}. Showing it as written.`) + return text + } + for (const note of r.notes) notes.push(`${name}: ${note}`) + return r.text + } + + const shownLeft = side(left, 'Left') + const shownRight = side(right, 'Right') + return { left: shownLeft, right: shownRight, size: assessSize(shownLeft, shownRight), problems, notes } +} + +export const TRANSFORM_LABELS: Record = { + none: 'As written', + 'sort-lines': 'Sorted lines', + json: 'Key-sorted JSON', +} + +export const TRANSFORM_BANNERS: Record = { + 'sort-lines': 'Showing line-sorted view.', + json: 'Showing key-sorted JSON view.', +} From cf264ff9cc5539c3c156010fa4437d0b0c9d6baf Mon Sep 17 00:00:00 2001 From: fadeltd Date: Fri, 30 Oct 2026 01:25:04 +0700 Subject: [PATCH 05/14] Base64: SVG inspector with a strip report, and a virtualised hex view with SHA-256 A decoded SVG is valid UTF-8, so it used to show only as markup. Decode mode now previews it through above the text, and every SVG (encode-mode file, decoded text, or a decoded non-UTF-8 SVG) gets an inspect panel: pixel size and viewBox, plus a strip report from running DOMPurify (svg + svgFilters profiles) on the source text, e.g. "Stripped 1