From 8bf985bec02e89402d126b66dd6614c458815a8e Mon Sep 17 00:00:00 2001 From: pearmini Date: Sun, 29 Sep 2024 14:30:51 -0400 Subject: [PATCH] Code for example star --- .eslintrc.json | 13 + .gitattributes | 1 + .github/workflows/test.yml | 23 + .gitignore | 6 + .prettierignore | 1 + .prettierrc | 4 + LICENSE | 13 + README.md | 21 +- index.html | 32 + package.json | 63 ++ pnpm-lock.yaml | 1900 ++++++++++++++++++++++++++++++++++++ rollup.config.js | 40 + rust/Cargo.lock | 143 +++ rust/Cargo.toml | 16 + rust/src/attributes.rs | 42 + rust/src/globals.rs | 32 + rust/src/lib.rs | 7 + rust/src/matrix3.rs | 76 ++ rust/src/pipeline.rs | 263 +++++ rust/src/primitives.rs | 94 ++ rust/src/renderer.rs | 64 ++ rust/src/transform.rs | 38 + src/context/cols.js | 3 + src/context/constants.js | 3 + src/context/index.js | 28 + src/context/init.js | 15 + src/context/node.js | 3 + src/context/point.js | 4 + src/context/rows.js | 3 + src/context/run.js | 40 + src/context/setup.js | 3 + src/context/stroke.js | 21 + src/index.js | 2 + src/platform.js | 8 + src/render.js | 10 + src/terminal.js | 151 +++ src/wide.js | 6 + test/apps/index.js | 1 + test/apps/star.js | 17 + test/index.html | 54 + test/snapshot.spec.js | 3 + vite.config.js | 17 + 42 files changed, 3274 insertions(+), 10 deletions(-) create mode 100644 .eslintrc.json create mode 100644 .gitattributes create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 LICENSE create mode 100644 index.html create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 rollup.config.js create mode 100644 rust/Cargo.lock create mode 100644 rust/Cargo.toml create mode 100644 rust/src/attributes.rs create mode 100644 rust/src/globals.rs create mode 100644 rust/src/lib.rs create mode 100644 rust/src/matrix3.rs create mode 100644 rust/src/pipeline.rs create mode 100644 rust/src/primitives.rs create mode 100644 rust/src/renderer.rs create mode 100644 rust/src/transform.rs create mode 100644 src/context/cols.js create mode 100644 src/context/constants.js create mode 100644 src/context/index.js create mode 100644 src/context/init.js create mode 100644 src/context/node.js create mode 100644 src/context/point.js create mode 100644 src/context/rows.js create mode 100644 src/context/run.js create mode 100644 src/context/setup.js create mode 100644 src/context/stroke.js create mode 100644 src/index.js create mode 100644 src/platform.js create mode 100644 src/render.js create mode 100644 src/terminal.js create mode 100644 src/wide.js create mode 100644 test/apps/index.js create mode 100644 test/apps/star.js create mode 100644 test/index.html create mode 100644 test/snapshot.spec.js create mode 100644 vite.config.js diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..687ae9e --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,13 @@ +{ + "extends": ["eslint:recommended", "prettier"], + "env": { + "es2020": true, + "node": true, + "browser": true + }, + "parserOptions": { + "sourceType": "module", + "ecmaVersion": "latest" + }, + "ignorePatterns": ["src/wasm/*"] +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..71fb5ba --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +**/*.txt text=auto eol=lf \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..4715f0b --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Test + +on: + push: + branches: [next] + pull_request: + branches: [next] + +jobs: + build: + runs-on: macOS-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 16 + - uses: dtolnay/rust-toolchain@stable + - uses: jetli/wasm-pack-action@v0.4.0 + with: + version: "latest" + - run: npm install + - run: npm run test + - run: npm run build diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d34e6b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +.DS_Store +**/target +src/wasm +dist +**/*-actual.txt \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..a940f11 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +src/wasm/* \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0edba09 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "printWidth": 120, + "bracketSpacing": false +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..da9c4d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright 2024-present Bairui SU https://github.com/pearmini + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/README.md b/README.md index 95ce996..6e0409f 100644 --- a/README.md +++ b/README.md @@ -3,20 +3,21 @@ ```js import * as Cell from "@charming-art/cell"; -function App(ctx) { - let x = 0; +function Star(ctx) { return { + mode: "double", + width: 520, + height: 520, setup() { - ctx.size(30, 20); - }, - draw() { - ctx.background(" "); - ctx.fill("@", "red", "yellow"); - ctx.rect(x, 0, 10, 5); - x += 1; + for (let t = 0; t <= Math.PI * 2; t += Math.PI / 120) { + const x = ctx.cols() / 2 + 12 * Math.cos(t) * Math.cos(t * 3); + const y = ctx.rows() / 2 + 12 * Math.sin(t) * Math.cos(t * 3); + ctx.stroke(Cell.wide("๐ŸŒŸ")); + ctx.point(x, y); + } }, }; } -document.body.append(await Cell.render(App)); +document.body.append(await Cell.render(Star)); ``` diff --git a/index.html b/index.html new file mode 100644 index 0000000..351d25f --- /dev/null +++ b/index.html @@ -0,0 +1,32 @@ + + + + + + Preview + + + + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..849471a --- /dev/null +++ b/package.json @@ -0,0 +1,63 @@ +{ + "name": "@charming-art/cell", + "description": "The creative coding language for ASCII Art.", + "version": "0.0.1", + "author": { + "name": "pearmini", + "url": "https://github.com/pearmini" + }, + "type": "module", + "license": "ISC", + "main": "dist/es/index.js", + "module": "dist/es/index.js", + "jsdelivr": "dist/cell.umd.min.js", + "unpkg": "dist/cell.umd.min.js", + "exports": { + "umd": "./dist/cell.umd.min.js", + "default": "./src/index.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/charming-art/cell.git" + }, + "files": [ + "dist/**/*.js", + "dist/**/*.wasm" + ], + "scripts": { + "dev": "npm run build:rust && vite", + "test": "npm run test:rust && npm run test:js && npm run test:lint && npm run test:format", + "test:js": "npm run build:rust && vitest", + "test:rust": "cargo test --manifest-path ./rust/Cargo.toml", + "test:lint": "eslint src test", + "test:format": "prettier --check src test && cargo fmt --manifest-path ./rust/Cargo.toml --check", + "build": "npm run build:rust && npm run build:js", + "build:js": "rm -rf dist && rollup -c", + "build:rust": "rm -rf ./src/wasm && wasm-pack build ./rust --out-dir ../src/wasm --out-name index --target web", + "preview": "npm run build && vite preview", + "prepublishOnly": "npm run build" + }, + "sideEffects": false, + "devDependencies": { + "@rollup/plugin-node-resolve": "^15.1.0", + "@rollup/plugin-terser": "^0.4.3", + "@rollup/plugin-wasm": "^6.1.3", + "eslint": "^8.44.0", + "eslint-config-prettier": "^8.8.0", + "prettier": "^2.8.8", + "rollup": "^3.26.0", + "vite": "^4.3.9", + "vitest": "^0.32.1", + "wasm-pack": "^0.13.0" + }, + "dependencies": { + "d3-color": "^3.1.0", + "d3-timer": "^3.0.1" + }, + "engines": { + "node": ">=14.18" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..4f32dfc --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1900 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + d3-color: + specifier: ^3.1.0 + version: 3.1.0 + d3-timer: + specifier: ^3.0.1 + version: 3.0.1 + devDependencies: + '@rollup/plugin-node-resolve': + specifier: ^15.1.0 + version: 15.3.0(rollup@3.29.5) + '@rollup/plugin-terser': + specifier: ^0.4.3 + version: 0.4.4(rollup@3.29.5) + '@rollup/plugin-wasm': + specifier: ^6.1.3 + version: 6.2.2(rollup@3.29.5) + eslint: + specifier: ^8.44.0 + version: 8.57.1 + eslint-config-prettier: + specifier: ^8.8.0 + version: 8.10.0(eslint@8.57.1) + playwright: + specifier: ^1.35.0 + version: 1.47.2 + prettier: + specifier: ^2.8.8 + version: 2.8.8 + rollup: + specifier: ^3.26.0 + version: 3.29.5 + vite: + specifier: ^4.3.9 + version: 4.5.5(@types/node@22.7.4)(terser@5.34.1) + vitest: + specifier: ^0.32.1 + version: 0.32.4(playwright@1.47.2)(terser@5.34.1) + wasm-pack: + specifier: ^0.13.0 + version: 0.13.0 + +packages: + + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.11.1': + resolution: {integrity: sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@rollup/plugin-node-resolve@15.3.0': + resolution: {integrity: sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-terser@0.4.4': + resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-wasm@6.2.2': + resolution: {integrity: sha512-gpC4R1G9Ni92ZIRTexqbhX7U+9estZrbhP+9SRb0DW9xpB9g7j34r+J2hqrcW/lRI7dJaU84MxZM0Rt82tqYPQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.1.2': + resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@types/chai-subset@1.3.5': + resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} + + '@types/chai@4.3.20': + resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==} + + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@types/node@22.7.4': + resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==} + + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + '@vitest/expect@0.32.4': + resolution: {integrity: sha512-m7EPUqmGIwIeoU763N+ivkFjTzbaBn0n9evsTOcde03ugy2avPs3kZbYmw3DkcH1j5mxhMhdamJkLQ6dM1bk/A==} + + '@vitest/runner@0.32.4': + resolution: {integrity: sha512-cHOVCkiRazobgdKLnczmz2oaKK9GJOw6ZyRcaPdssO1ej+wzHVIkWiCiNacb3TTYPdzMddYkCgMjZ4r8C0JFCw==} + + '@vitest/snapshot@0.32.4': + resolution: {integrity: sha512-IRpyqn9t14uqsFlVI2d7DFMImGMs1Q9218of40bdQQgMePwVdmix33yMNnebXcTzDU5eiV3eUsoxxH5v0x/IQA==} + + '@vitest/spy@0.32.4': + resolution: {integrity: sha512-oA7rCOqVOOpE6rEoXuCOADX7Lla1LIa4hljI2MSccbpec54q+oifhziZIJXxlE/CvI2E+ElhBHzVu0VEvJGQKQ==} + + '@vitest/utils@0.32.4': + resolution: {integrity: sha512-Gwnl8dhd1uJ+HXrYyV0eRqfmk9ek1ASE/LWfTCuWMw+d07ogHqp4hEAV28NiecimK6UY9DpSEPh+pXBA5gtTBg==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + + axios@0.26.1: + resolution: {integrity: sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + binary-install@1.1.0: + resolution: {integrity: sha512-rkwNGW+3aQVSZoD0/o3mfPN6Yxh3Id0R/xzTVBVVpGNlVz8EGwusksxRlbk/A5iKTZt9zkMn3qIqmAt3vpfbzg==} + engines: {node: '>=10'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@8.10.0: + resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.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-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + 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==} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + + magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + 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==} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pkg-types@1.2.0: + resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==} + + playwright-core@1.47.2: + resolution: {integrity: sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.47.2: + resolution: {integrity: sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==} + engines: {node: '>=18'} + hasBin: true + + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@3.29.5: + resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + 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'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + smob@1.5.0: + resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} + + source-map-js@1.2.1: + 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'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + terser@5.34.1: + resolution: {integrity: sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==} + engines: {node: '>=10'} + hasBin: true + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinypool@0.5.0: + resolution: {integrity: sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==} + engines: {node: '>=14.0.0'} + + tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + vite-node@0.32.4: + resolution: {integrity: sha512-L2gIw+dCxO0LK14QnUMoqSYpa9XRGnTTTDjW2h19Mr+GR0EFj4vx52W41gFXfMLqpA00eK4ZjOVYo1Xk//LFEw==} + engines: {node: '>=v14.18.0'} + hasBin: true + + vite@4.5.5: + resolution: {integrity: sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@0.32.4: + resolution: {integrity: sha512-3czFm8RnrsWwIzVDu/Ca48Y/M+qh3vOnF16czJm98Q/AN1y3B6PBsyV8Re91Ty5s7txKNjEhpgtGPcfdbh2MZg==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + + wasm-pack@0.13.0: + resolution: {integrity: sha512-AmboGZEnZoIcVCzSlkLEmNFEqJN+IwgshJ5S7pi30uNUTce4LvWkifQzsQRxnWj47G8gkqZxlyGlyQplsnIS7w==} + hasBin: true + + 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 + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + +snapshots: + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-x64@0.18.20': + optional: true + + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.11.1': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.3.7 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.7 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@rollup/plugin-node-resolve@15.3.0(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@3.29.5) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.8 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-terser@0.4.4(rollup@3.29.5)': + dependencies: + serialize-javascript: 6.0.2 + smob: 1.5.0 + terser: 5.34.1 + optionalDependencies: + rollup: 3.29.5 + + '@rollup/plugin-wasm@6.2.2(rollup@3.29.5)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@3.29.5) + optionalDependencies: + rollup: 3.29.5 + + '@rollup/pluginutils@5.1.2(rollup@3.29.5)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 3.29.5 + + '@sinclair/typebox@0.27.8': {} + + '@types/chai-subset@1.3.5': + dependencies: + '@types/chai': 4.3.20 + + '@types/chai@4.3.20': {} + + '@types/estree@1.0.6': {} + + '@types/node@22.7.4': + dependencies: + undici-types: 6.19.8 + + '@types/resolve@1.20.2': {} + + '@ungap/structured-clone@1.2.0': {} + + '@vitest/expect@0.32.4': + dependencies: + '@vitest/spy': 0.32.4 + '@vitest/utils': 0.32.4 + chai: 4.5.0 + + '@vitest/runner@0.32.4': + dependencies: + '@vitest/utils': 0.32.4 + p-limit: 4.0.0 + pathe: 1.1.2 + + '@vitest/snapshot@0.32.4': + dependencies: + magic-string: 0.30.11 + pathe: 1.1.2 + pretty-format: 29.7.0 + + '@vitest/spy@0.32.4': + dependencies: + tinyspy: 2.2.1 + + '@vitest/utils@0.32.4': + dependencies: + diff-sequences: 29.6.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.12.1 + + acorn@8.12.1: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + argparse@2.0.1: {} + + assertion-error@1.1.0: {} + + axios@0.26.1: + dependencies: + follow-redirects: 1.15.9 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + binary-install@1.1.0: + dependencies: + axios: 0.26.1 + rimraf: 3.0.2 + tar: 6.2.1 + transitivePeerDependencies: + - debug + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + buffer-from@1.1.2: {} + + cac@6.7.14: {} + + callsites@3.1.0: {} + + chai@4.5.0: + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.4 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.1.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + check-error@1.0.3: + dependencies: + get-func-name: 2.0.2 + + chownr@2.0.0: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@2.20.3: {} + + concat-map@0.0.1: {} + + confbox@0.1.7: {} + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + d3-color@3.1.0: {} + + d3-timer@3.0.1: {} + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + deep-eql@4.1.4: + dependencies: + type-detect: 4.1.0 + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + diff-sequences@29.6.3: {} + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@8.10.0(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) + '@eslint-community/regexpp': 4.11.1 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.2.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.7 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) + eslint-visitor-keys: 3.4.3 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.1 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.1: {} + + follow-redirects@1.15.9: {} + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs.realpath@1.0.0: {} + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-func-name@2.0.2: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + graphemer@1.4.0: {} + + has-flag@4.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + ignore@5.3.2: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-module@1.0.0: {} + + is-path-inside@3.0.3: {} + + isexe@2.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + local-pkg@0.4.3: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loupe@2.3.7: + dependencies: + get-func-name: 2.0.2 + + magic-string@0.30.11: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + mkdirp@1.0.4: {} + + mlly@1.7.1: + dependencies: + acorn: 8.12.1 + pathe: 1.1.2 + pkg-types: 1.2.0 + ufo: 1.5.4 + + ms@2.1.3: {} + + nanoid@3.3.7: {} + + natural-compare@1.4.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.1.1 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + pathe@1.1.2: {} + + pathval@1.1.1: {} + + picocolors@1.1.0: {} + + picomatch@2.3.1: {} + + pkg-types@1.2.0: + dependencies: + confbox: 0.1.7 + mlly: 1.7.1 + pathe: 1.1.2 + + playwright-core@1.47.2: {} + + playwright@1.47.2: + dependencies: + playwright-core: 1.47.2 + optionalDependencies: + fsevents: 2.3.2 + + postcss@8.4.47: + dependencies: + nanoid: 3.3.7 + picocolors: 1.1.0 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier@2.8.8: {} + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + react-is@18.3.1: {} + + resolve-from@4.0.0: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.0.4: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rollup@3.29.5: + optionalDependencies: + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-buffer@5.2.1: {} + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + smob@1.5.0: {} + + 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: {} + + stackback@0.0.2: {} + + std-env@3.7.0: {} + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-json-comments@3.1.1: {} + + strip-literal@1.3.0: + dependencies: + acorn: 8.12.1 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + terser@5.34.1: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.12.1 + commander: 2.20.3 + source-map-support: 0.5.21 + + text-table@0.2.0: {} + + tinybench@2.9.0: {} + + tinypool@0.5.0: {} + + tinyspy@2.2.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.1.0: {} + + type-fest@0.20.2: {} + + ufo@1.5.4: {} + + undici-types@6.19.8: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vite-node@0.32.4(@types/node@22.7.4)(terser@5.34.1): + dependencies: + cac: 6.7.14 + debug: 4.3.7 + mlly: 1.7.1 + pathe: 1.1.2 + picocolors: 1.1.0 + vite: 4.5.5(@types/node@22.7.4)(terser@5.34.1) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + + vite@4.5.5(@types/node@22.7.4)(terser@5.34.1): + dependencies: + esbuild: 0.18.20 + postcss: 8.4.47 + rollup: 3.29.5 + optionalDependencies: + '@types/node': 22.7.4 + fsevents: 2.3.3 + terser: 5.34.1 + + vitest@0.32.4(playwright@1.47.2)(terser@5.34.1): + dependencies: + '@types/chai': 4.3.20 + '@types/chai-subset': 1.3.5 + '@types/node': 22.7.4 + '@vitest/expect': 0.32.4 + '@vitest/runner': 0.32.4 + '@vitest/snapshot': 0.32.4 + '@vitest/spy': 0.32.4 + '@vitest/utils': 0.32.4 + acorn: 8.12.1 + acorn-walk: 8.3.4 + cac: 6.7.14 + chai: 4.5.0 + debug: 4.3.7 + local-pkg: 0.4.3 + magic-string: 0.30.11 + pathe: 1.1.2 + picocolors: 1.1.0 + std-env: 3.7.0 + strip-literal: 1.3.0 + tinybench: 2.9.0 + tinypool: 0.5.0 + vite: 4.5.5(@types/node@22.7.4)(terser@5.34.1) + vite-node: 0.32.4(@types/node@22.7.4)(terser@5.34.1) + why-is-node-running: 2.3.0 + optionalDependencies: + playwright: 1.47.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + + wasm-pack@0.13.0: + dependencies: + binary-install: 1.1.0 + transitivePeerDependencies: + - debug + + 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 + + word-wrap@1.2.5: {} + + wrappy@1.0.2: {} + + yallist@4.0.0: {} + + yocto-queue@0.1.0: {} + + yocto-queue@1.1.1: {} diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..1ba8236 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,40 @@ +import node from "@rollup/plugin-node-resolve"; +import {wasm} from "@rollup/plugin-wasm"; +import terser from "@rollup/plugin-terser"; + +const umd = { + input: "src/index.js", + output: { + format: "umd", + name: "Cell", + }, + plugins: [wasm({targetEnv: "auto-inline"}), node()], +}; + +export default [ + { + input: "src/index.js", + output: { + format: "es", + dir: "dist/es", + preserveModules: true, + }, + external: [/node_modules/], + plugins: [wasm(), node()], + }, + { + ...umd, + output: { + ...umd.output, + file: "dist/cell.umd.js", + }, + }, + { + ...umd, + output: { + ...umd.output, + file: "dist/cell.umd.min.js", + }, + plugins: [...umd.plugins, terser()], + }, +]; diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..7d50bd5 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,143 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "syn" +version = "2.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..17fab84 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wasm-bindgen = "0.2" + +[dependencies.web-sys] +version = "0.3.4" +features = [ + "console", +] \ No newline at end of file diff --git a/rust/src/attributes.rs b/rust/src/attributes.rs new file mode 100644 index 0000000..0053f81 --- /dev/null +++ b/rust/src/attributes.rs @@ -0,0 +1,42 @@ +extern crate wasm_bindgen; +use crate::renderer::Renderer; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +impl Renderer { + pub fn stroke(&mut self, ch: u32, ch1: u32, fg: u32, bg: u32) { + self.has_stroke = true; + self.stroke_color = [ch, ch1, fg, bg]; + } + pub fn fill(&mut self, ch: u32, ch1: u32, fg: u32, bg: u32) { + self.has_fill = true; + self.fill_color = [ch, ch1, fg, bg]; + } + pub fn background(&mut self, ch: u32, ch1: u32, fg: u32, bg: u32) { + self.has_background = true; + self.background_color = [ch, ch1, fg, bg]; + } + #[wasm_bindgen(js_name = "noStroke")] + pub fn no_stroke(&mut self) { + self.has_stroke = false; + } + #[wasm_bindgen(js_name = "noFill")] + pub fn no_fill(&mut self) { + self.has_fill = false; + } +} + +#[cfg(test)] +mod tests { + use super::Renderer; + + #[test] + fn should_set_stroke() { + let mut renderer: Renderer = Renderer::new(10, 10); + renderer.stroke(0, 0, 0, 0); + assert_eq!(renderer.stroke_color[0], 0); + assert_eq!(renderer.stroke_color[1], 0); + assert_eq!(renderer.stroke_color[2], 0); + assert_eq!(renderer.stroke_color[3], 0); + } +} diff --git a/rust/src/globals.rs b/rust/src/globals.rs new file mode 100644 index 0000000..c985f32 --- /dev/null +++ b/rust/src/globals.rs @@ -0,0 +1,32 @@ +pub const CELL_SIZE: usize = 4; + +pub const NULL_VALUE: u32 = 0xFFFFFFFF; + +pub struct Vertex { + pub(crate) color: Color, + pub(crate) x: f64, + pub(crate) y: f64, +} + +pub struct Point { + pub(crate) color: Color, + pub(crate) x: isize, + pub(crate) y: isize, +} + +pub type Edge<'a> = [&'a Point; 2]; + +pub type Color = [u32; CELL_SIZE]; + +pub struct Shape { + pub(crate) vertices: Vec, + pub(crate) matrix: Matrix3, + pub(crate) is_closed: bool, + pub(crate) has_stroke: bool, + pub(crate) has_fill: bool, + pub(crate) fill_color: Color, +} + +pub type Matrix3 = [f64; 9]; + +pub type Vector3 = [f64; 3]; diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..41c17ec --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,7 @@ +mod attributes; +mod globals; +mod matrix3; +mod pipeline; +mod primitives; +pub mod renderer; +mod transform; diff --git a/rust/src/matrix3.rs b/rust/src/matrix3.rs new file mode 100644 index 0000000..f98ff26 --- /dev/null +++ b/rust/src/matrix3.rs @@ -0,0 +1,76 @@ +use crate::globals::{Matrix3, Vector3}; + +pub fn matrix3_identity() -> Matrix3 { + [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0] +} + +pub fn matrix3_zero() -> Matrix3 { + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] +} + +// x' a b c x +// y' = d e f * y +// z' g h i z +pub fn matrix3_transform(v: &Vector3, m: &Matrix3) -> Vector3 { + let mut out: Vector3 = [0.0, 0.0, 0.0]; + let x: f64 = v[0]; + let y: f64 = v[1]; + let z: f64 = v[2]; + out[0] = x * m[0] + y * m[1] + z * m[2]; + out[1] = x * m[3] + y * m[4] + z * m[5]; + out[2] = x * m[6] + y * m[7] + z * m[8]; + out +} + +// x' a b c 1 0 x x +// y' = d e f * 0 1 y * y +// 1 g h i 0 0 1 1 +pub fn matrix3_translate(a: &Matrix3, x: f64, y: f64) -> Matrix3 { + let mut out: Matrix3 = matrix3_zero(); + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[0] * x + a[1] * y + a[2]; + out[3] = a[3]; + out[4] = a[4]; + out[5] = a[3] * x + a[4] * y + a[5]; + out[6] = a[6]; + out[7] = a[7]; + out[8] = a[6] * x + a[7] * y + a[8]; + out +} + +// x' a b c x 0 0 x0 +// y' = d e f * 0 y 0 * y0 +// 1 g h i 0 0 1 1 +pub fn matrix3_scale(a: &Matrix3, x: f64, y: f64) -> Matrix3 { + let mut out: Matrix3 = matrix3_zero(); + out[0] = x * a[0]; + out[1] = y * a[1]; + out[2] = a[2]; + out[3] = x * a[3]; + out[4] = y * a[4]; + out[5] = a[5]; + out[6] = x * a[6]; + out[7] = y * a[7]; + out[8] = a[8]; + out +} + +// x' a b c cosx -sinx 0 x +// y'= d e f * sinx cosx 0 * y +// 1 g h i 0 0 1 1 +pub fn matrix3_rotate(a: &Matrix3, rad: f64) -> Matrix3 { + let mut out: Matrix3 = matrix3_zero(); + let cos: f64 = rad.cos(); + let sin: f64 = rad.sin(); + out[0] = a[0] * cos + a[1] * sin; + out[1] = -a[0] * sin + a[1] * cos; + out[2] = a[2]; + out[3] = a[3] * cos + a[4] * sin; + out[4] = -a[3] * sin + a[4] * cos; + out[5] = a[5]; + out[6] = a[6] * cos + a[7] * sin; + out[7] = -a[6] * sin + a[7] * cos; + out[8] = a[8]; + out +} diff --git a/rust/src/pipeline.rs b/rust/src/pipeline.rs new file mode 100644 index 0000000..cfe1bcf --- /dev/null +++ b/rust/src/pipeline.rs @@ -0,0 +1,263 @@ +extern crate wasm_bindgen; +use crate::{ + globals::{Color, Edge, Matrix3, Point, Vector3, Vertex, CELL_SIZE, NULL_VALUE}, + matrix3::{matrix3_identity, matrix3_transform}, + renderer::Renderer, +}; +use std::{cmp, ptr}; +use wasm_bindgen::prelude::*; + +fn vertex_processing(vertices: &Vec, m: &Matrix3) -> Vec { + let mut transformed: Vec = vec![]; + for vertex in vertices { + let v: Vector3 = [vertex.x, vertex.y, 1.0]; + let out: Vector3 = matrix3_transform(&v, &m); + transformed.push(Point { + color: vertex.color, + x: out[0].round() as isize, + y: out[1].round() as isize, + }) + } + transformed +} + +fn primitive_assembly(vertices: &Vec, is_closed: bool) -> Vec { + if vertices.len() == 0 { + vec![] + } else if vertices.len() == 1 { + let edge: Edge = [&vertices[0], &vertices[0]]; + vec![edge] + } else { + let mut edges: Vec = vec![]; + let len: usize = vertices.len(); + let end: usize = if is_closed { len + 1 } else { len }; + for i in 1..end { + let from: &Point = &vertices[i - 1]; + let to: &Point = &vertices[i % len]; + let edge: Edge = [from, to]; + edges.push(edge); + } + edges + } +} + +fn rasterization( + edges: &Vec, + fill_color: Color, + has_stroke: bool, + has_fill: bool, +) -> Vec { + let mut vertices: Vec = vec![]; + if has_fill { + rasterization_stroke(&mut vertices, edges, fill_color, true); + rasterization_fill(&mut vertices, fill_color); + } + if has_stroke { + rasterization_stroke(&mut vertices, edges, fill_color, false); + } + vertices +} + +fn rasterization_stroke( + vertices: &mut Vec, + edges: &Vec, + fill_color: Color, + use_fill: bool, +) { + for i in 0..edges.len() { + let edge: Edge = edges[i]; + let from: &Point = edge[0]; + let to: &Point = edge[1]; + if ptr::eq(from, to) { + vertices.push(Point { + color: from.color, + x: from.x, + y: from.y, + }) + } else { + let next: Edge = edges[(i + 1) % edges.len()]; + let next_from: &Point = next[0]; + let color: Color = if use_fill { fill_color } else { from.color }; + let line: &mut Vec = &mut rasterization_line(from, to, color); + if next_from.x == to.x && next_from.y == to.y && line.len() >= 2 { + line.pop(); + vertices.append(line); + } else { + vertices.append(line); + } + } + } +} + +fn rasterization_fill(vertices: &mut Vec, color: Color) { + let mut y0: isize = isize::MAX; + let mut y1: isize = isize::MIN; + for vertex in &mut *vertices { + y0 = cmp::min(y0, vertex.y); + y1 = cmp::max(y1, vertex.y); + } + + let mut lookup: Vec> = vec![vec![]; (y1 - y0) as usize + 1]; + for vertex in &mut *vertices { + let index: usize = (vertex.y - y0) as usize; + lookup[index].push(vertex.x as isize) + } + + for i in 0..lookup.len() { + let line: &mut Vec = &mut lookup[i]; + line.sort(); + let y: isize = (i as isize) + y0; + let mut in_polygon: bool = false; + for i in 0..line.len() { + if in_polygon { + let x0: isize = line[i - 1]; + let x1: isize = line[i]; + for x in (x0 + 1)..x1 { + vertices.push(Point { color, x, y }) + } + } + in_polygon = !in_polygon; + } + } +} + +// @see https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm +fn rasterization_line(from: &Point, to: &Point, color: Color) -> Vec { + let dx: isize = (to.x - from.x).abs(); + let dy: isize = -(to.y - from.y).abs(); + let sx: isize = if from.x < to.x { 1 } else { -1 }; + let sy: isize = if from.y < to.y { 1 } else { -1 }; + let mut vertices: Vec = vec![]; + let mut x: isize = from.x; + let mut y: isize = from.y; + let mut error: isize = dx + dy; + + loop { + vertices.push(Point { color, x, y }); + if x == to.x && y == to.y { + break; + }; + let error2: isize = error * 2; + if error2 >= dy { + if x == to.x { + break; + } + error = error + dy; + x = x + sx; + } + if error2 <= dx { + if y == to.y { + break; + } + error = error + dx; + y = y + sy; + } + } + + vertices +} + +fn clipping(vertices: &Vec, cols: isize, rows: isize) -> Vec { + let mut clipped: Vec = vec![]; + for i in 0..vertices.len() { + let vertex: &Point = &vertices[i]; + let x: isize = vertex.x; + let y: isize = vertex.y; + if x >= 0 && x < cols && y >= 0 && y < rows { + clipped.push(i); + } + } + clipped +} + +fn fragment_processing( + vertices: &Vec, + clipped: &Vec, + buffer: &mut Vec, + cols: isize, +) { + for i in clipped { + let vertex: &Point = &vertices[*i]; + let x: isize = vertex.x; + let y: isize = vertex.y; + let index: usize = ((x + y * cols) as usize) * CELL_SIZE; + buffer[index] = vertex.color[0]; + buffer[index + 1] = vertex.color[1]; + buffer[index + 2] = vertex.color[2]; + buffer[index + 3] = vertex.color[3]; + } +} + +fn background_processing( + has_background: bool, + color: Color, + buffer: &mut Vec, + rows: isize, + cols: isize, +) { + if has_background { + for x in 0..cols { + for y in 0..rows { + let index: usize = ((x + y * cols) as usize) * CELL_SIZE; + buffer[index] = color[0]; + buffer[index + 1] = color[1]; + buffer[index + 2] = color[2]; + buffer[index + 3] = color[3]; + } + } + } else { + buffer.fill(NULL_VALUE); + } +} + +#[wasm_bindgen] +impl Renderer { + pub fn render(&mut self) -> *const u32 { + background_processing( + self.has_background, + self.background_color, + &mut self.buffer, + self.rows as isize, + self.cols as isize, + ); + for shape in &self.shapes { + let transformed: Vec = vertex_processing(&shape.vertices, &shape.matrix); + let primitive: Vec = primitive_assembly(&transformed, shape.is_closed); + let fragment: Vec = rasterization( + &primitive, + shape.fill_color, + shape.has_stroke, + shape.has_fill, + ); + let clipped: Vec = clipping(&fragment, self.cols as isize, self.rows as isize); + fragment_processing(&fragment, &clipped, &mut self.buffer, self.cols as isize); + } + self.has_background = false; + self.has_stroke = true; + self.has_fill = false; + self.shapes.clear(); + self.stacks.clear(); + self.mode_view = matrix3_identity(); + self.buffer.as_ptr() + } +} + +#[cfg(test)] +mod tests { + use super::Renderer; + + #[test] + fn should_clear_reset() { + let mut renderer: Renderer = Renderer::new(10, 10); + renderer.point(0.0, 0.0); + renderer.render(); + assert_eq!(renderer.shapes.len(), 0); + assert_eq!(renderer.stacks.len(), 0); + assert_eq!(renderer.mode_view[0], 1.0); + assert_eq!(renderer.mode_view[4], 1.0); + assert_eq!(renderer.mode_view[8], 1.0); + assert_eq!(renderer.has_background, false); + assert_eq!(renderer.has_stroke, true); + assert_eq!(renderer.has_fill, false); + } +} diff --git a/rust/src/primitives.rs b/rust/src/primitives.rs new file mode 100644 index 0000000..0effc4d --- /dev/null +++ b/rust/src/primitives.rs @@ -0,0 +1,94 @@ +extern crate wasm_bindgen; +use crate::{ + globals::{Shape, Vertex}, + renderer::Renderer, +}; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +impl Renderer { + pub fn point(&mut self, x: f64, y: f64) { + let v: Vertex = Vertex { + color: self.stroke_color, + x, + y, + }; + self.shapes.push(Shape { + vertices: vec![v], + matrix: self.mode_view, + is_closed: false, + has_fill: false, + has_stroke: self.has_stroke, + fill_color: self.fill_color, + }); + } + pub fn line(&mut self, x: f64, y: f64, x1: f64, y1: f64) { + let v: Vertex = Vertex { + color: self.stroke_color, + x, + y, + }; + let v1: Vertex = Vertex { + color: self.stroke_color, + x: x1, + y: y1, + }; + self.shapes.push(Shape { + vertices: vec![v, v1], + matrix: self.mode_view, + is_closed: false, + has_fill: false, + has_stroke: self.has_stroke, + fill_color: self.fill_color, + }) + } + pub fn rect(&mut self, x: f64, y: f64, width: f64, height: f64) { + if width == 0.0 || height == 0.0 { + return; + } + let dx: f64 = width - 1.0; + let dy: f64 = height - 1.0; + let v: Vertex = Vertex { + color: self.stroke_color, + x, + y, + }; + let v1: Vertex = Vertex { + color: self.stroke_color, + x: x + dx, + y, + }; + let v2: Vertex = Vertex { + color: self.stroke_color, + x: x + dx, + y: y + dy, + }; + let v3: Vertex = Vertex { + color: self.stroke_color, + x, + y: y + dy, + }; + self.shapes.push(Shape { + vertices: vec![v, v1, v2, v3], + matrix: self.mode_view, + is_closed: true, + has_fill: self.has_fill, + has_stroke: self.has_stroke, + fill_color: self.fill_color, + }) + } +} + +#[cfg(test)] +mod tests { + + use super::Renderer; + + #[test] + fn should_add_point() { + let mut renderer: Renderer = Renderer::new(10, 10); + renderer.point(0.0, 0.0); + assert_eq!(renderer.shapes[0].vertices[0].x, 0.0); + assert_eq!(renderer.shapes[0].vertices[0].y, 0.0); + } +} diff --git a/rust/src/renderer.rs b/rust/src/renderer.rs new file mode 100644 index 0000000..984cdbe --- /dev/null +++ b/rust/src/renderer.rs @@ -0,0 +1,64 @@ +extern crate wasm_bindgen; +use crate::{ + globals::{Color, Matrix3, Shape, Vector3, CELL_SIZE, NULL_VALUE}, + matrix3::matrix3_identity, +}; +use std::vec; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub struct Renderer { + pub(crate) cols: usize, + pub(crate) rows: usize, + pub(crate) stroke_color: Color, + pub(crate) fill_color: Color, + pub(crate) has_stroke: bool, + pub(crate) has_fill: bool, + pub(crate) has_background: bool, + pub(crate) background_color: Color, + pub(crate) buffer: Vec, + pub(crate) shapes: Vec, + pub(crate) mode_view: Matrix3, + pub(crate) out: Vector3, + pub(crate) stacks: Vec, +} + +#[wasm_bindgen] +impl Renderer { + pub fn new(cols: usize, rows: usize) -> Renderer { + let buffer: Vec = vec![NULL_VALUE; cols * rows * CELL_SIZE]; + Renderer { + cols, + rows, + buffer, + stroke_color: [NULL_VALUE, NULL_VALUE, NULL_VALUE, NULL_VALUE], + fill_color: [NULL_VALUE, NULL_VALUE, NULL_VALUE, NULL_VALUE], + has_fill: false, + has_stroke: true, + has_background: false, + background_color: [NULL_VALUE, NULL_VALUE, NULL_VALUE, NULL_VALUE], + mode_view: matrix3_identity(), + stacks: vec![], + shapes: vec![], + out: [0.0, 0.0, 0.0], + } + } +} + +#[cfg(test)] +mod tests { + use super::Renderer; + + #[test] + fn should_have_expected_defaults() { + let renderer: Renderer = Renderer::new(10, 10); + assert_eq!(renderer.cols, 10); + assert_eq!(renderer.rows, 10); + assert_eq!(renderer.buffer.len(), 400); + assert_eq!(renderer.shapes.len(), 0); + assert_eq!(renderer.stacks.len(), 0); + assert_eq!(renderer.has_stroke, true); + assert_eq!(renderer.has_background, false); + assert_eq!(renderer.has_fill, false); + } +} diff --git a/rust/src/transform.rs b/rust/src/transform.rs new file mode 100644 index 0000000..bd32a05 --- /dev/null +++ b/rust/src/transform.rs @@ -0,0 +1,38 @@ +extern crate wasm_bindgen; +use crate::{ + matrix3::{matrix3_rotate, matrix3_scale, matrix3_transform, matrix3_translate}, + renderer::Renderer, +}; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +impl Renderer { + pub fn translate(&mut self, x: f64, y: f64) { + self.mode_view = matrix3_translate(&self.mode_view, x, y); + } + + pub fn scale(&mut self, x: f64, y: f64) { + self.mode_view = matrix3_scale(&self.mode_view, x, y); + } + + pub fn rotate(&mut self, rad: f64) { + self.mode_view = matrix3_rotate(&self.mode_view, rad); + } + + #[wasm_bindgen(js_name = "pushMatrix")] + pub fn push_matrix(&mut self) { + self.stacks.push(self.mode_view); + } + + #[wasm_bindgen(js_name = "popMatrix")] + pub fn pop_matrix(&mut self) { + if let Some(last) = self.stacks.pop() { + self.mode_view = last; + } + } + + pub fn transform(&mut self, x: f64, y: f64) -> *const f64 { + self.out = matrix3_transform(&[x, y, 1.0], &self.mode_view); + self.out.as_ptr() + } +} diff --git a/src/context/cols.js b/src/context/cols.js new file mode 100644 index 0000000..06da312 --- /dev/null +++ b/src/context/cols.js @@ -0,0 +1,3 @@ +export function context_cols() { + return this._terminal._cols; +} diff --git a/src/context/constants.js b/src/context/constants.js new file mode 100644 index 0000000..38766f9 --- /dev/null +++ b/src/context/constants.js @@ -0,0 +1,3 @@ +export const NULL_VALUE = 0xffffffff; + +export const CELL_SIZE = 4; diff --git a/src/context/index.js b/src/context/index.js new file mode 100644 index 0000000..0ec498f --- /dev/null +++ b/src/context/index.js @@ -0,0 +1,28 @@ +import {context_init} from "./init.js"; +import {context_run} from "./run.js"; +import {context_stroke} from "./stroke.js"; +import {context_point} from "./point.js"; +import {context_setup} from "./setup.js"; +import {context_rows} from "./rows.js"; +import {context_cols} from "./cols.js"; +import {context_node} from "./node.js"; + +export function Context() { + Object.defineProperties(this, { + _memory: {value: null, writable: true}, + _terminal: {value: null, writable: true}, + _renderer: {value: null, writable: true}, + _setup: {value: null, writable: true}, + }); +} + +Object.defineProperties(Context.prototype, { + setup: {value: context_setup, writable: true, configurable: true}, + init: {value: context_init, writable: true, configurable: true}, + run: {value: context_run, writable: true, configurable: true}, + stroke: {value: context_stroke, writable: true, configurable: true}, + point: {value: context_point, writable: true, configurable: true}, + rows: {value: context_rows, writable: true, configurable: true}, + cols: {value: context_cols, writable: true, configurable: true}, + node: {value: context_node, writable: true, configurable: true}, +}); diff --git a/src/context/init.js b/src/context/init.js new file mode 100644 index 0000000..a8e5c61 --- /dev/null +++ b/src/context/init.js @@ -0,0 +1,15 @@ +import {Terminal} from "../terminal.js"; +import {Renderer} from "../wasm/index.js"; +import init from "../wasm/index.js"; +import wasm from "../wasm/index_bg.wasm"; + +export async function context_init(options = {}) { + const module = await init(typeof wasm === "function" ? await wasm() : undefined); + const {memory} = module; + const terminal = new Terminal(options); + const renderer = Renderer.new(terminal._cols, terminal._rows); + this._memory = memory; + this._terminal = terminal; + this._renderer = renderer; + this._terminal.background("#000"); +} diff --git a/src/context/node.js b/src/context/node.js new file mode 100644 index 0000000..0895dbf --- /dev/null +++ b/src/context/node.js @@ -0,0 +1,3 @@ +export function context_node() { + return this._terminal.node(); +} diff --git a/src/context/point.js b/src/context/point.js new file mode 100644 index 0000000..d7d1738 --- /dev/null +++ b/src/context/point.js @@ -0,0 +1,4 @@ +export function context_point(x, y) { + this._renderer.point(x, y); + return this; +} diff --git a/src/context/rows.js b/src/context/rows.js new file mode 100644 index 0000000..f0f4eaa --- /dev/null +++ b/src/context/rows.js @@ -0,0 +1,3 @@ +export function context_rows() { + return this._terminal._rows; +} diff --git a/src/context/run.js b/src/context/run.js new file mode 100644 index 0000000..99546a0 --- /dev/null +++ b/src/context/run.js @@ -0,0 +1,40 @@ +import {NULL_VALUE, CELL_SIZE} from "./constants.js"; + +function decodeColor(color) { + if (color === NULL_VALUE) return undefined; + const r = (color & 0xff0000) >> 16; + const g = (color & 0x00ff00) >> 8; + const b = color & 0x0000ff; + return `rgb(${r}, ${g}, ${b})`; +} + +function decodeChar(n) { + if (n === NULL_VALUE) return ["", 0]; + const first = n >> 31; + const n1 = n & 0x0fffffff; + const w = first === 0 ? 1 : 2; + return [String.fromCodePoint(n1), w]; +} + +function draw() { + const bufferPtr = this._renderer.render(); + const buffer = new Uint32Array(this._memory.buffer, bufferPtr, this.cols() * this.rows() * CELL_SIZE); + for (let i = 0; i < this.rows(); i++) { + for (let j = 0; j < this.cols(); j++) { + const index = (this.cols() * i + j) * CELL_SIZE; + const [ch, wch] = decodeChar(buffer[index]); + const [ch1, wch1] = decodeChar(buffer[index + 1]); + const fg = decodeColor(buffer[index + 2]); + const bg = decodeColor(buffer[index + 3]); + const wide = wch + wch1 >= 2; + const ch2 = ch + ch1; + if (ch2 || fg) this._terminal.char(ch2, j, i, fg, bg, wide); + } + } +} + +export function context_run() { + this._setup?.(this); + draw.call(this); + return this._terminal.node(); +} diff --git a/src/context/setup.js b/src/context/setup.js new file mode 100644 index 0000000..cb8387b --- /dev/null +++ b/src/context/setup.js @@ -0,0 +1,3 @@ +export function context_setup(hook) { + this._setup = hook; +} diff --git a/src/context/stroke.js b/src/context/stroke.js new file mode 100644 index 0000000..65720aa --- /dev/null +++ b/src/context/stroke.js @@ -0,0 +1,21 @@ +import {NULL_VALUE} from "./constants.js"; +import {color as rgb} from "d3-color"; + +function encodeColor(color) { + if (color === NULL_VALUE || color === null) return NULL_VALUE; + const {r, g, b} = rgb(color); + return b + (g << 8) + (r << 16); +} + +function encodeChar(ch) { + if (Array.isArray(ch)) return ch; + return Array.from(ch) + .slice(0, 2) + .map((ch) => ch.codePointAt(0)); +} + +export function context_stroke(ch, fg = "#ffffff", bg = null) { + const [n, n1 = NULL_VALUE] = encodeChar(ch); + this._renderer.stroke(n, n1, encodeColor(fg), encodeColor(bg)); + return this; +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..f7774c9 --- /dev/null +++ b/src/index.js @@ -0,0 +1,2 @@ +export {render} from "./render.js"; +export {wide} from "./wide.js"; diff --git a/src/platform.js b/src/platform.js new file mode 100644 index 0000000..2919150 --- /dev/null +++ b/src/platform.js @@ -0,0 +1,8 @@ +// For vite env. +export const isNode = typeof navigator === "undefined" ? true : false; + +const userAgent = isNode ? "node" : navigator.userAgent; + +export const isFireFox = userAgent.includes("Firefox"); + +export const isLegacyEdge = userAgent.includes("Edge"); diff --git a/src/render.js b/src/render.js new file mode 100644 index 0000000..6b68035 --- /dev/null +++ b/src/render.js @@ -0,0 +1,10 @@ +import {Context} from "./context/index.js"; + +export async function render(App) { + const ctx = new Context(); + const {setup, ...options} = App(ctx); + await ctx.init(options); + ctx.setup(setup); + ctx.run(); + return ctx.node(); +} diff --git a/src/terminal.js b/src/terminal.js new file mode 100644 index 0000000..f777980 --- /dev/null +++ b/src/terminal.js @@ -0,0 +1,151 @@ +import {isFireFox, isLegacyEdge} from "./platform.js"; + +// https://github.com/xtermjs/xterm.js/blob/096fe171356fc9519e0a6b737a98ca82d0587e91/src/browser/renderer/shared/Constants.ts#LL14C1-L14C1 +export const TEXT_BASELINE = isFireFox || isLegacyEdge ? "bottom" : "ideographic"; + +export const CELL_SIZE = 3; + +export const TERMINAL_CLASS = "cell-terminal"; + +// Default options from: https://github.com/xtermjs/xterm.js/blob/ac0207bf2e8a923d0cff95cc383f6f3e36a2e923/src/common/services/OptionsService.ts#LL12C1-L12C1 +export function Terminal({ + document = window.document, + mode = "single", + cols = mode === "single" ? 80 : 40, + rows = 24, + fontFamily = "courier-new, courier, monospace", + fontSize = 15, + fontWeight = "normal", + width, + height, +} = {}) { + const {width: tw, height: th} = measureText("W", { + fontSize, + fontFamily, + fontWeight, + }); + const cellWidth = mode === "double" ? tw * 2 : tw; + const cellHeight = th; + const computedCols = dimensionOf(width, cols, cellWidth); + const computedRows = dimensionOf(height, rows, cellHeight); + const computedWidth = computedCols * cellWidth; + const computedHeight = computedRows * cellHeight; + const context = createContext(document, computedWidth, computedHeight); + const buffer = Array.from({length: computedCols * computedRows}, () => null); + context.canvas.classList.add(TERMINAL_CLASS); + Object.defineProperties(this, { + _mode: {value: mode}, + _fontSize: {value: fontSize}, + _fontFamily: {value: fontFamily}, + _fontWeight: {value: fontWeight}, + _cellWidth: {value: cellWidth}, + _cellHeight: {value: cellHeight}, + _cols: {value: computedCols}, + _rows: {value: computedRows}, + _width: {value: computedWidth}, + _height: {value: computedHeight}, + _context: {value: context}, + _buffer: {value: buffer}, + }); +} + +Object.defineProperties(Terminal.prototype, { + background: {value: terminal$background, writable: true, configurable: true}, + char: {value: terminal$char, writable: true, configurable: true}, + toString: {value: terminal$toString, writable: true, configurable: true}, + node: {value: terminal$node, writable: true, configurable: true}, +}); + +function terminal$background(color) { + this._context.fillStyle = color; + this._context.fillRect(0, 0, this._width, this._height); + this._buffer.fill(null); + return this; +} + +function terminal$char(char, i, j, fg, bg, wide = false) { + const x = this._cellWidth * i; + const y = this._cellHeight * j; + const index = (this._cols * j + i) * CELL_SIZE; + + if (bg) { + this._context.fillStyle = bg; + this._context.fillRect(x, y, this._cellWidth, this._cellHeight); + this._buffer[index + 2] = bg; + } + + if (fg) { + this._context.fillStyle = fg; + this._buffer[index + 1] = fg; + } + + if (!char) return; + this._context.font = `${this._fontWeight} ${this._fontSize}px ${this._fontFamily}`; + this._context.textBaseline = TEXT_BASELINE; + this._context.fillText(char, x, y + this._cellHeight); + this._buffer[index] = char; + + if (this._mode !== "double" || wide) return; + this._context.fillText(char, x + this._cellWidth / 2, y + this._cellHeight); + this._buffer[index] += char; + + return this; +} + +function terminal$toString() { + let string = ""; + for (let j = 0; j < this._rows; j++) { + if (j !== 0) string += "\n"; + for (let i = 0; i < this._cols; i++) { + const index = (this._cols * j + i) * CELL_SIZE; + const empty = this._mode === "double" ? "ยทยท" : "ยท"; + const char = this._buffer[index] || empty; + string += char; + } + } + return string; +} + +function terminal$node() { + return this._context.canvas; +} + +function createContext(document, width = 640, height = 480, dpi = null) { + if (dpi == null) dpi = devicePixelRatio; + const canvas = document.createElement("canvas"); + canvas.width = width * dpi; + canvas.height = height * dpi; + canvas.style.width = width + "px"; + canvas.style.height = height + "px"; + const context = canvas.getContext("2d"); + context.scale(dpi, dpi); + return context; +} + +function measureText(text, styles) { + const span = document.createElement("span"); + + // Hide span. + span.style.visibility = "hidden"; + span.style.position = "absolute"; + span.style.display = "inline-block"; + span.style.left = "-9999em"; + span.style.top = "0"; + span.style.lineHeight = "normal"; + span.setAttribute("aria-hidden", true); + + // Font attributes. + span.style.fontSize = `${styles.fontSize}px`; + span.style.fontFamily = styles.fontFamily; + + span.innerHTML = text; + document.body.appendChild(span); + + const bbox = span.getBoundingClientRect(); + return {width: bbox.width, height: Math.ceil(bbox.height)}; +} + +function dimensionOf(pixel, count, unit) { + if (pixel === undefined) return count; + return (pixel / unit) | 0; +} diff --git a/src/wide.js b/src/wide.js new file mode 100644 index 0000000..af920bd --- /dev/null +++ b/src/wide.js @@ -0,0 +1,6 @@ +import {NULL_VALUE} from "./context/constants.js"; + +export function wide(string) { + const code = string.codePointAt(0); + return [code + 0xf0000000, NULL_VALUE]; +} diff --git a/test/apps/index.js b/test/apps/index.js new file mode 100644 index 0000000..065d86d --- /dev/null +++ b/test/apps/index.js @@ -0,0 +1 @@ +export {Star} from "./star.js"; diff --git a/test/apps/star.js b/test/apps/star.js new file mode 100644 index 0000000..7a97d75 --- /dev/null +++ b/test/apps/star.js @@ -0,0 +1,17 @@ +import * as Cell from "@charming-art/cell"; + +export function Star(ctx) { + return { + mode: "double", + width: 520, + height: 520, + setup() { + for (let t = 0; t <= Math.PI * 2; t += Math.PI / 120) { + const x = ctx.cols() / 2 + 12 * Math.cos(t) * Math.cos(t * 3); + const y = ctx.rows() / 2 + 12 * Math.sin(t) * Math.cos(t * 3); + ctx.stroke(Cell.wide("๐ŸŒŸ")); + ctx.point(x, y); + } + }, + }; +} diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..c8c0797 --- /dev/null +++ b/test/index.html @@ -0,0 +1,54 @@ + + + + + diff --git a/test/snapshot.spec.js b/test/snapshot.spec.js new file mode 100644 index 0000000..413ca26 --- /dev/null +++ b/test/snapshot.spec.js @@ -0,0 +1,3 @@ +import {describe} from "vitest"; + +describe.todo("Snapshot testing"); diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..da6ee82 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,17 @@ +import path from "path"; +import {defineConfig} from "vite"; + +export default defineConfig({ + root: path.resolve("./test"), + server: { + port: 8080, + open: "/", + }, + resolve: { + alias: { + "@charming-art/cell": path.resolve("./src/index.js"), + "../wasm/index_bg.wasm": "../wasm/index_bg.wasm?url", + }, + }, + build: {outDir: "../"}, +});