diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml deleted file mode 100644 index 870e083..0000000 --- a/.github/workflows/deno.yml +++ /dev/null @@ -1,41 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# This workflow will install Deno then run `deno lint` and `deno test`. -# For more information see: https://github.com/denoland/setup-deno - -name: Deno - -on: - push: - branches: ["main"] - pull_request: - branches: ["main"] - -permissions: - contents: read - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - name: Setup repo - uses: actions/checkout@v4 - - - name: Setup Deno - uses: denoland/setup-deno@v1.1.4 - with: - deno-version: v1.x - - # Uncomment this step to verify the use of 'deno fmt' on each commit. - - name: Verify formatting - run: deno fmt --check - - - name: Run linter - run: deno lint - - - name: Run tests - run: deno test -A diff --git a/.github/workflows/publish_jsr.yml b/.github/workflows/publish_jsr.yml index 0ee3654..3925558 100644 --- a/.github/workflows/publish_jsr.yml +++ b/.github/workflows/publish_jsr.yml @@ -1,7 +1,7 @@ name: Publish on JSR on: workflow_run: - workflows: ["Deno"] + workflows: ["Test"] types: [completed] branches: - "main" @@ -9,6 +9,7 @@ on: jobs: publish: runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} permissions: contents: read @@ -17,5 +18,36 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Publish package - run: npx jsr publish + - name: Setup repo + uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + run_install: false + - uses: actions/setup-node@v4 + with: + node-version: "24.x" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + # This step removes JSR dependencies from package.json before publishing, + # Because apparently now while major npm compatible package managers + # support JSR dependencies in package.json, JSR itself does not. + - name: Remove JSR deps from package.json + run: | + node -e " + const fs = require('fs'); + const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8')); + for (const dep in pkg.dependencies ?? {}) { + if (pkg.dependencies[dep].startsWith('jsr:')) { + delete pkg.dependencies[dep]; + } + } + fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2)); + " + + - name: Publish to JSR + # Have to use --allow-dirty because we modified package.json in the previous step + run: pnpm dlx jsr publish --allow-dirty diff --git a/.github/workflows/publish_npm.yml b/.github/workflows/publish_npm.yml index 7746542..1101e8c 100644 --- a/.github/workflows/publish_npm.yml +++ b/.github/workflows/publish_npm.yml @@ -9,13 +9,21 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v4 - - name: Setup Deno - uses: denoland/setup-deno@v1.1.4 + - name: Setup repo + uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 with: - deno-version: v1.x - - run: deno run -A scripts/build_npm.ts - - run: cd npm && npm ci - - run: cd npm && npm publish --provenance --access public + run_install: false + # Setup .npmrc file to publish to npm + - uses: actions/setup-node@v4 + with: + node-version: "24.x" + cache: "pnpm" + registry-url: "https://registry.npmjs.org" + - run: pnpm install + - run: pnpm build + - run: pnpm test + # --no-git-checks because of https://github.com/pnpm/pnpm/issues/5894. + - run: pnpm publish --provenance --access public --no-git-checks env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..8600ce4 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,32 @@ +name: Test + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Setup repo + uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + with: + run_install: false + - uses: actions/setup-node@v4 + with: + node-version: "24.x" + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Run tests + run: pnpm test diff --git a/.gitignore b/.gitignore index bad76e5..8d67a86 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -npm .DS_Store +node_modules +dist diff --git a/.zed/settings.json b/.zed/settings.json index 14b3761..9425503 100644 --- a/.zed/settings.json +++ b/.zed/settings.json @@ -3,25 +3,11 @@ // For a full list of overridable settings, and general information on folder-specific settings, // see the documentation: https://zed.dev/docs/configuring-zed#folder-specific-settings { + "formatter": "prettier", + "format_on_save": "on", "languages": { "TypeScript": { - "language_servers": ["deno", "!typescript-language-server", "!eslint"], - "formatter": { - "external": { - "command": "deno", - "arguments": ["fmt", "-"] - } - } - }, - "TSX": { - "language_servers": ["deno", "!typescript-language-server", "!eslint"], - "formatter": { - "external": { - "command": "deno", - "arguments": ["fmt", "-"] - } - } + "language_servers": ["vtsls", "!deno"] } - }, - "format_on_save": "on" + } } diff --git a/.zed/tasks.json b/.zed/tasks.json index 4868825..0698fbd 100644 --- a/.zed/tasks.json +++ b/.zed/tasks.json @@ -1,9 +1,6 @@ -// Static tasks configuration. -// -// Example: [ { "label": "Test", - "command": "deno test" + "command": "pnpm test" } ] diff --git a/LICENSE b/LICENSE index 80f95df..538e18d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ The MIT License (MIT) -Copyright © 2024 Shibo Lyu +Copyright © 2025 Shibo Lyu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/build.config.ts b/build.config.ts new file mode 100644 index 0000000..0e77cd2 --- /dev/null +++ b/build.config.ts @@ -0,0 +1,5 @@ +import { defineBuildConfig } from "obuild/config"; + +export default defineBuildConfig({ + entries: ["src/mod.ts"], +}) as ReturnType; diff --git a/deno.json b/deno.json index f399119..44f593d 100644 --- a/deno.json +++ b/deno.json @@ -1,8 +1,11 @@ { "name": "@textplace/core", - "version": "0.1.5", - "exports": "./mod.ts", + "version": "0.6.1", + "exports": "./src/mod.ts", "imports": { - "@deno/dnt": "jsr:@deno/dnt@^0.41.1" + "@std/cli": "jsr:@std/cli@1" + }, + "publish": { + "include": ["src/**/*.ts", "LICENSE", "README.md"] } } diff --git a/deno.lock b/deno.lock deleted file mode 100644 index cb732d5..0000000 --- a/deno.lock +++ /dev/null @@ -1,256 +0,0 @@ -{ - "version": "3", - "packages": { - "specifiers": { - "jsr:@deno/cache-dir@^0.8.0": "jsr:@deno/cache-dir@0.8.0", - "jsr:@deno/dnt@^0.41.1": "jsr:@deno/dnt@0.41.1", - "jsr:@deno/graph@^0.69.7": "jsr:@deno/graph@0.69.10", - "jsr:@std/assert@^0.218.2": "jsr:@std/assert@0.218.2", - "jsr:@std/bytes@^0.218.2": "jsr:@std/bytes@0.218.2", - "jsr:@std/fmt@^0.218.2": "jsr:@std/fmt@0.218.2", - "jsr:@std/fs@^0.218.2": "jsr:@std/fs@0.218.2", - "jsr:@std/io@^0.218.2": "jsr:@std/io@0.218.2", - "jsr:@std/path@^0.218.2": "jsr:@std/path@0.218.2", - "npm:@ts-morph/bootstrap@0.22": "npm:@ts-morph/bootstrap@0.22.0", - "npm:code-block-writer@^13.0.1": "npm:code-block-writer@13.0.1" - }, - "jsr": { - "@deno/cache-dir@0.8.0": { - "integrity": "e87e80a404958f6350d903e6238b72afb92468378b0b32111f7a1e4916ac7fe7", - "dependencies": [ - "jsr:@deno/graph@^0.69.7", - "jsr:@std/fs@^0.218.2", - "jsr:@std/io@^0.218.2" - ] - }, - "@deno/dnt@0.41.1": { - "integrity": "8746a773e031ae19ef43d0eece850217b76cf1d0118fdd8e059652d7023d4aff", - "dependencies": [ - "jsr:@deno/cache-dir@^0.8.0", - "jsr:@std/fmt@^0.218.2", - "jsr:@std/fs@^0.218.2", - "jsr:@std/path@^0.218.2", - "npm:@ts-morph/bootstrap@0.22", - "npm:code-block-writer@^13.0.1" - ] - }, - "@deno/graph@0.69.10": { - "integrity": "38fe22ac5686f6ece5daeec5a4df65c6314d7d32adcc33f77917a13cfaffa26f" - }, - "@std/assert@0.218.2": { - "integrity": "7f0a5a1a8cf86607cd6c2c030584096e1ffad27fc9271429a8cb48cfbdee5eaf" - }, - "@std/bytes@0.218.2": { - "integrity": "91fe54b232dcca73856b79a817247f4a651dbb60d51baafafb6408c137241670" - }, - "@std/fmt@0.218.2": { - "integrity": "99526449d2505aa758b6cbef81e7dd471d8b28ec0dcb1491d122b284c548788a" - }, - "@std/fs@0.218.2": { - "integrity": "dd9431453f7282e8c577cc22c9e6d036055a9a980b5549f887d6012969fabcca", - "dependencies": [ - "jsr:@std/assert@^0.218.2", - "jsr:@std/path@^0.218.2" - ] - }, - "@std/io@0.218.2": { - "integrity": "c64fbfa087b7c9d4d386c5672f291f607d88cb7d44fc299c20c713e345f2785f", - "dependencies": [ - "jsr:@std/bytes@^0.218.2" - ] - }, - "@std/path@0.218.2": { - "integrity": "b568fd923d9e53ad76d17c513e7310bda8e755a3e825e6289a0ce536404e2662", - "dependencies": [ - "jsr:@std/assert@^0.218.2" - ] - } - }, - "npm": { - "@nodelib/fs.scandir@2.1.5": { - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "@nodelib/fs.stat@2.0.5", - "run-parallel": "run-parallel@1.2.0" - } - }, - "@nodelib/fs.stat@2.0.5": { - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dependencies": {} - }, - "@nodelib/fs.walk@1.2.8": { - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "@nodelib/fs.scandir@2.1.5", - "fastq": "fastq@1.17.1" - } - }, - "@ts-morph/bootstrap@0.22.0": { - "integrity": "sha512-MI5q7pid4swAlE2lcHwHRa6rcjoIMyT6fy8uuZm8BGg7DHGi/H5bQ0GMZzbk3N0r/LfStMdOYPkl+3IwvfIQ2g==", - "dependencies": { - "@ts-morph/common": "@ts-morph/common@0.22.0" - } - }, - "@ts-morph/common@0.22.0": { - "integrity": "sha512-HqNBuV/oIlMKdkLshXd1zKBqNQCsuPEsgQOkfFQ/eUKjRlwndXW1AjN9LVkBEIukm00gGXSRmfkl0Wv5VXLnlw==", - "dependencies": { - "fast-glob": "fast-glob@3.3.2", - "minimatch": "minimatch@9.0.4", - "mkdirp": "mkdirp@3.0.1", - "path-browserify": "path-browserify@1.0.1" - } - }, - "balanced-match@1.0.2": { - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dependencies": {} - }, - "brace-expansion@2.0.1": { - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "balanced-match@1.0.2" - } - }, - "braces@3.0.2": { - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "fill-range@7.0.1" - } - }, - "code-block-writer@13.0.1": { - "integrity": "sha512-c5or4P6erEA69TxaxTNcHUNcIn+oyxSRTOWV+pSYF+z4epXqNvwvJ70XPGjPNgue83oAFAPBRQYwpAJ/Hpe/Sg==", - "dependencies": {} - }, - "fast-glob@3.3.2": { - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dependencies": { - "@nodelib/fs.stat": "@nodelib/fs.stat@2.0.5", - "@nodelib/fs.walk": "@nodelib/fs.walk@1.2.8", - "glob-parent": "glob-parent@5.1.2", - "merge2": "merge2@1.4.1", - "micromatch": "micromatch@4.0.5" - } - }, - "fastq@1.17.1": { - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dependencies": { - "reusify": "reusify@1.0.4" - } - }, - "fill-range@7.0.1": { - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "to-regex-range@5.0.1" - } - }, - "glob-parent@5.1.2": { - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "is-glob@4.0.3" - } - }, - "is-extglob@2.1.1": { - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dependencies": {} - }, - "is-glob@4.0.3": { - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "is-extglob@2.1.1" - } - }, - "is-number@7.0.0": { - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dependencies": {} - }, - "merge2@1.4.1": { - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dependencies": {} - }, - "micromatch@4.0.5": { - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "braces@3.0.2", - "picomatch": "picomatch@2.3.1" - } - }, - "minimatch@9.0.4": { - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", - "dependencies": { - "brace-expansion": "brace-expansion@2.0.1" - } - }, - "mkdirp@3.0.1": { - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dependencies": {} - }, - "path-browserify@1.0.1": { - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dependencies": {} - }, - "picomatch@2.3.1": { - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dependencies": {} - }, - "queue-microtask@1.2.3": { - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dependencies": {} - }, - "reusify@1.0.4": { - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dependencies": {} - }, - "run-parallel@1.2.0": { - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dependencies": { - "queue-microtask": "queue-microtask@1.2.3" - } - }, - "to-regex-range@5.0.1": { - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "is-number@7.0.0" - } - } - } - }, - "remote": { - "https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", - "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", - "https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293", - "https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7", - "https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74", - "https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd", - "https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff", - "https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46", - "https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b", - "https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c", - "https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491", - "https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68", - "https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3", - "https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7", - "https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29", - "https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a", - "https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a", - "https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8", - "https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693", - "https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31", - "https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5", - "https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8", - "https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb", - "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917", - "https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47", - "https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68", - "https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3", - "https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73", - "https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19", - "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5", - "https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6", - "https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2", - "https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e" - }, - "workspace": { - "dependencies": [ - "jsr:@deno/dnt@^0.41.1" - ] - } -} diff --git a/logic/board.ts b/logic/board.ts deleted file mode 100644 index 3503de7..0000000 --- a/logic/board.ts +++ /dev/null @@ -1,93 +0,0 @@ -import type { SectionData, SectionPosition } from "../types/section.ts"; -import type { - BoardConfig, - BoardData, - CharacterPosition, - FullBoard, -} from "../types/board.ts"; -import { applyChange, createSection } from "./section.ts"; -import type { BoardChange } from "../types/change.ts"; - -export function createBoard(config: BoardConfig): BoardData { - const sections: SectionData[][] = Array(config.ySections) - .fill(0) - .map((_, sy) => - Array(config.xSections) - .fill(0) - .map((_, sx) => createSection({ sx, sy }, config)) - ); - - return { config, sections }; -} - -export function locateSection( - { x, y }: CharacterPosition, - config: BoardConfig, -): SectionPosition { - return { - sx: Math.floor(x / config.sectionWidth), - sy: Math.floor(y / config.sectionHeight), - }; -} - -export function applyChangeOnBoard(change: BoardChange, board: BoardData) { - const { sx, sy } = locateSection(change, board.config); - const section = board.sections[sy][sx]; - applyChange(change, section); -} - -export function renderFullBoard(data: BoardData): FullBoard { - const totalLineCount = data.config.sectionHeight * data.config.ySections; - const lineLength = data.config.sectionWidth * data.config.xSections; - - const chLines: string[] = Array(totalLineCount); - const colorLines: string[] = Array(totalLineCount); - const bgColorLines: string[] = Array(totalLineCount); - const widthLines: string[] = Array(totalLineCount); - - for (let y = 0; y < totalLineCount; y++) { - let chLine = ""; - let colorLine = ""; - let bgColorLine = ""; - let widthLine = ""; - - let charsToSkip = 0; - - for (let x = 0; x < lineLength; x++) { - if (charsToSkip > 0) { - charsToSkip--; - continue; - } - - const { sx, sy } = locateSection({ x, y }, data.config); - const section = data.sections[sy][sx]; - const xInSection = x % data.config.sectionWidth; - const yInSection = y % data.config.sectionHeight; - - const cCh = section.ch[yInSection][xInSection]; - const cCo = section.color[yInSection][xInSection]; - const cBg = section.bgColor[yInSection][xInSection]; - const cWd = section.width[yInSection][xInSection]; - - chLine += cCh; - colorLine += cCo; - bgColorLine += cBg; - widthLine += cWd.toString(); - charsToSkip += cWd - 1; - } - - chLines[y] = chLine; - colorLines[y] = colorLine; - bgColorLines[y] = bgColorLine; - widthLines[y] = widthLine; - } - - return { - w: lineLength, - h: totalLineCount, - ch: chLines.join("\n"), - color: colorLines.join("\n"), - bg_color: bgColorLines.join("\n"), - width: widthLines.join("\n"), - }; -} diff --git a/logic/character.ts b/logic/character.ts deleted file mode 100644 index d1b9f69..0000000 --- a/logic/character.ts +++ /dev/null @@ -1,21 +0,0 @@ -const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" }); -const cjkRegex = - /[\p{Unified_Ideograph}\u30A0-\u30FF\u3040-\u309F\u31F0-\u31FF]/u; -const printableASCIIRegex = /^[\x20-\x7E]$/; - -export function getCharacterWidth(ch: string): number { - const segments = [...segmenter.segment(ch)]; - if (segments.length !== 1) { - throw new Error( - `Expected exactly one grapheme cluster, got ${segments.length}.`, - ); - } - - const matchesASCII = ch.match(printableASCIIRegex); - const matchesCJK = ch.match(cjkRegex); - - if (!matchesASCII && !matchesCJK) throw new Error(`Invalid character: ${ch}`); - - // TODO: Support Emojis. - return matchesCJK ? 2 : 1; -} diff --git a/logic/section.ts b/logic/section.ts deleted file mode 100644 index d935dc3..0000000 --- a/logic/section.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { getCharacterWidth } from "../mod.ts"; -import type { BoardConfig } from "../types/board.ts"; -import type { BoardChange } from "../types/change.ts"; -import type { SectionData, SectionPosition } from "../types/section.ts"; - -export function createSection( - { sx, sy }: SectionPosition, - boardConfig: BoardConfig, -): SectionData { - if (boardConfig.sectionWidth % 2 !== 0) { - throw new Error( - "sectionWidth must be multiple of 2 (least common multiples of all character widths)", - ); - } - - const offsetX = sx * boardConfig.sectionWidth; - const offsetY = sy * boardConfig.sectionHeight; - - const ch: string[][] = Array(boardConfig.sectionHeight).fill( - Array(boardConfig.sectionWidth).fill(boardConfig.defaultCh), - ); - const color: string[][] = Array(boardConfig.sectionHeight).fill( - Array(boardConfig.sectionWidth).fill(boardConfig.defaultColor), - ); - const bgColor: string[][] = Array(boardConfig.sectionHeight).fill( - Array(boardConfig.sectionWidth).fill(boardConfig.defaultBgColor), - ); - const width: number[][] = Array(boardConfig.sectionHeight).fill( - Array(boardConfig.sectionWidth).fill(boardConfig.defaultWidth), - ); - - return { offsetX, offsetY, ch, color, bgColor, width }; -} - -export function applyChange(change: BoardChange, section: SectionData) { - const xInSection = change.x - section.offsetX; - const yInSection = change.y - section.offsetY; - - const validX = xInSection >= 0 && xInSection < section.ch[0].length; - const validY = yInSection >= 0 && yInSection < section.ch.length; - if (!validX || !validY) { - throw new Error("Change does not belong to this section"); - } - - if (change.ch) { - const chWidth = getCharacterWidth(change.ch); - const xCharacterOffset = xInSection % 2; - const offsetAdjustedXInSection = xInSection - xCharacterOffset; - section.ch[yInSection][offsetAdjustedXInSection] = change.ch; - section.width[yInSection][offsetAdjustedXInSection] = chWidth; - } - if (change.color) { - section.color[yInSection][xInSection] = change.color; - } - if (change.bg_color) { - section.bgColor[yInSection][xInSection] = change.bg_color; - } -} diff --git a/mod.ts b/mod.ts deleted file mode 100644 index 7b01c8a..0000000 --- a/mod.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from "./types/board.ts"; -export * from "./types/section.ts"; -export * from "./types/change.ts"; - -export * from "./logic/board.ts"; -export * from "./logic/section.ts"; -export * from "./logic/character.ts"; diff --git a/package.json b/package.json new file mode 100644 index 0000000..c572c3f --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "@textplace/core", + "version": "0.6.1", + "description": "The core logic of TextPlace.", + "license": "MIT", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/TextPlace/CoreTextPlace" + }, + "bugs": { + "url": "https://github.com/TextPlace/CoreTextPlace/issues" + }, + "scripts": { + "test": "vitest", + "build": "obuild", + "typecheck": "tsc --noEmit" + }, + "files": [ + "dist" + ], + "exports": { + ".": { + "types": "./dist/mod.d.ts", + "import": "./dist/mod.mjs" + } + }, + "dependencies": { + "@std/cli": "jsr:^1.0.24" + }, + "devDependencies": { + "obuild": "^0.4.3", + "prettier": "^3.7.2", + "typescript": "^5.9.3", + "vitest": "^4.0.14" + }, + "packageManager": "pnpm@10.25.0" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..070deb8 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1897 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@std/cli': + specifier: jsr:^1.0.24 + version: '@jsr/std__cli@1.0.24' + devDependencies: + obuild: + specifier: ^0.4.3 + version: 0.4.8(typescript@5.9.3) + prettier: + specifier: ^3.7.2 + version: 3.7.4 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.0.14 + version: 4.0.15(jiti@2.6.1) + +packages: + + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} + + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@esbuild/aix-ppc64@0.27.1': + resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.1': + resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.1': + resolution: {integrity: sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.1': + resolution: {integrity: sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.1': + resolution: {integrity: sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.1': + resolution: {integrity: sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.1': + resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.1': + resolution: {integrity: sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.1': + resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.1': + resolution: {integrity: sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.1': + resolution: {integrity: sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.1': + resolution: {integrity: sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.1': + resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.1': + resolution: {integrity: sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.1': + resolution: {integrity: sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.1': + resolution: {integrity: sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.1': + resolution: {integrity: sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.1': + resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.1': + resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.1': + resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.1': + resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.1': + resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.1': + resolution: {integrity: sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.1': + resolution: {integrity: sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.1': + resolution: {integrity: sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.1': + resolution: {integrity: sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@jsr/std__cli@1.0.24': + resolution: {integrity: sha512-lGBHY8KwqhSR3oh2Hk9U7Sy9v76/KZx0OOkiIVTMr8GW2yMrvhJUVTdteNxvKEYT1eKZKWPZl+3WWpC/9oPBeQ==, tarball: https://npm.jsr.io/~/11/@jsr/std__cli/1.0.24.tgz} + + '@jsr/std__internal@1.0.12': + resolution: {integrity: sha512-6xReMW9p+paJgqoFRpOE2nogJFvzPfaLHLIlyADYjKMUcwDyjKZxryIbgcU+gxiTygn8yCjld1HoI0ET4/iZeA==, tarball: https://npm.jsr.io/~/11/@jsr/std__internal/1.0.12.tgz} + + '@napi-rs/wasm-runtime@1.1.0': + resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} + + '@oxc-minify/binding-android-arm64@0.102.0': + resolution: {integrity: sha512-pknM+ttJTwRr7ezn1v5K+o2P4RRjLAzKI10bjVDPybwWQ544AZW6jxm7/YDgF2yUbWEV9o7cAQPkIUOmCiW8vg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-minify/binding-darwin-arm64@0.102.0': + resolution: {integrity: sha512-BDLiH41ZctNND38+GCEL3ZxFn9j7qMZJLrr6SLWMt8xlG4Sl64xTkZ0zeUy4RdVEatKKZdrRIhFZ2e5wPDQT6Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-minify/binding-darwin-x64@0.102.0': + resolution: {integrity: sha512-AcB8ZZ711w4hTDhMfMHNjT2d+hekTQ2XmNSUBqJdXB+a2bJbE50UCRq/nxXl44zkjaQTit3lcQbFvhk2wwKcpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-minify/binding-freebsd-x64@0.102.0': + resolution: {integrity: sha512-UlLEN9mR5QaviYVMWZQsN9DgAH3qyV67XUXDEzSrbVMLsqHsVHhFU8ZIeO0fxWTQW/cgpvldvKp9/+RdrggqWw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-minify/binding-linux-arm-gnueabihf@0.102.0': + resolution: {integrity: sha512-CWyCwedZrUt47n56/RwHSwKXxVI3p98hB0ntLaBNeH5qjjBujs9uOh4bQ0aAlzUWunT77b3/Y+xcQnmV42HN4A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-minify/binding-linux-arm64-gnu@0.102.0': + resolution: {integrity: sha512-W/DCw+Ys8rXj4j38ylJ2l6Kvp6SV+eO5SUWA11imz7yCWntNL001KJyGQ9PJNUFHg0jbxe3yqm4M50v6miWzeA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-minify/binding-linux-arm64-musl@0.102.0': + resolution: {integrity: sha512-DyH/t/zSZHuX4Nn239oBteeMC4OP7B13EyXWX18Qg8aJoZ+lZo90WPGOvhP04zII33jJ7di+vrtAUhsX64lp+A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-minify/binding-linux-riscv64-gnu@0.102.0': + resolution: {integrity: sha512-CMvzrmOg+Gs44E7TRK/IgrHYp+wwVJxVV8niUrDR2b3SsrCO3NQz5LI+7bM1qDbWnuu5Cl1aiitoMfjRY61dSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-minify/binding-linux-s390x-gnu@0.102.0': + resolution: {integrity: sha512-tZWr6j2s0ddm9MTfWTI3myaAArg9GDy4UgvpF00kMQAjLcGUNhEEQbB9Bd9KtCvDQzaan8HQs0GVWUp+DWrymw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxc-minify/binding-linux-x64-gnu@0.102.0': + resolution: {integrity: sha512-0YEKmAIun1bS+Iy5Shx6WOTSj3GuilVuctJjc5/vP8/EMTZ/RI8j0eq0Mu3UFPoT/bMULL3MBXuHuEIXmq7Ddg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-minify/binding-linux-x64-musl@0.102.0': + resolution: {integrity: sha512-Ew4QDpEsXoV+pG5+bJpheEy3GH436GBe6ASPB0X27Hh9cQ2gb1NVZ7cY7xJj68+fizwS/PtT8GHoG3uxyH17Pg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-minify/binding-openharmony-arm64@0.102.0': + resolution: {integrity: sha512-wYPXS8IOu/sXiP3CGHJNPzZo4hfPAwJKevcFH2syvU2zyqUxym7hx6smfcK/mgJBiX7VchwArdGRwrEQKcBSaQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-minify/binding-wasm32-wasi@0.102.0': + resolution: {integrity: sha512-52SepCb9e+8cVisGa9S/F14K8PxW0AnbV1j4KEYi8uwfkUIxeDNKRHVHzPoBXNrr0yxW0EHLn/3i8J7a2YCpWw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-minify/binding-win32-arm64-msvc@0.102.0': + resolution: {integrity: sha512-kLs6H1y6sDBKcIimkNwu5th28SLkyvFpHNxdLtCChda0KIGeIXNSiupy5BqEutY+VlWJivKT1OV3Ev3KC5Euzg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-minify/binding-win32-x64-msvc@0.102.0': + resolution: {integrity: sha512-XdyJZdSMN8rbBXH10CrFuU+Q9jIP2+MnxHmNzjK4+bldbTI1UxqwjUMS9bKVC5VCaIEZhh8IE8x4Vf8gmCgrKQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-parser/binding-android-arm64@0.102.0': + resolution: {integrity: sha512-pD2if3w3cxPvYbsBSTbhxAYGDaG6WVwnqYG0mYRQ142D6SJ6BpNs7YVQrqpRA2AJQCmzaPP5TRp/koFLebagfQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.102.0': + resolution: {integrity: sha512-RzMN6f6MrjjpQC2Dandyod3iOscofYBpHaTecmoRRbC5sJMwsurkqUMHzoJX9F6IM87kn8m/JcClnoOfx5Sesw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.102.0': + resolution: {integrity: sha512-Sr2/3K6GEcejY+HgWp5HaxRPzW5XHe9IfGKVn9OhLt8fzVLnXbK5/GjXj7JjMCNKI3G3ZPZDG2Dgm6CX3MaHCA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.102.0': + resolution: {integrity: sha512-s9F2N0KJCGEpuBW6ChpFfR06m2Id9ReaHSl8DCca4HvFNt8SJFPp8fq42n2PZy68rtkremQasM0JDrK2BoBeBQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.102.0': + resolution: {integrity: sha512-zRCIOWzLbqhfY4g8KIZDyYfO2Fl5ltxdQI1v2GlePj66vFWRl8cf4qcBGzxKfsH3wCZHAhmWd1Ht59mnrfH/UQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.102.0': + resolution: {integrity: sha512-5n5RbHgfjulRhKB0pW5p0X/NkQeOpI4uI9WHgIZbORUDATGFC8yeyPA6xYGEs+S3MyEAFxl4v544UEIWwqAgsA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-arm64-musl@0.102.0': + resolution: {integrity: sha512-/XWcmglH/VJ4yKAGTLRgPKSSikh3xciNxkwGiURt8dS30b+3pwc4ZZmudMu0tQ3mjSu0o7V9APZLMpbHK8Bp5w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-parser/binding-linux-riscv64-gnu@0.102.0': + resolution: {integrity: sha512-2jtIq4nswvy6xdqv1ndWyvVlaRpS0yqomLCvvHdCFx3pFXo5Aoq4RZ39kgvFWrbAtpeYSYeAGFnwgnqjx9ftdw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-parser/binding-linux-s390x-gnu@0.102.0': + resolution: {integrity: sha512-Yp6HX/574mvYryiqj0jNvNTJqo4pdAsNP2LPBTxlDQ1cU3lPd7DUA4MQZadaeLI8+AGB2Pn50mPuPyEwFIxeFg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxc-parser/binding-linux-x64-gnu@0.102.0': + resolution: {integrity: sha512-R4b0xZpDRhoNB2XZy0kLTSYm0ZmWeKjTii9fcv1Mk3/SIGPrrglwt4U6zEtwK54Dfi4Bve5JnQYduigR/gyDzw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-linux-x64-musl@0.102.0': + resolution: {integrity: sha512-xM5A+03Ti3jvWYZoqaBRS3lusvnvIQjA46Fc9aBE/MHgvKgHSkrGEluLWg/33QEwBwxupkH25Pxc1yu97oZCtg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-parser/binding-openharmony-arm64@0.102.0': + resolution: {integrity: sha512-AieLlsliblyaTFq7Iw9Nc618tgwV02JT4fQ6VIUd/3ZzbluHIHfPjIXa6Sds+04krw5TvCS8lsegtDYAyzcyhg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.102.0': + resolution: {integrity: sha512-w6HRyArs1PBb9rDsQSHlooe31buUlUI2iY8sBzp62jZ1tmvaJo9EIVTQlRNDkwJmk9DF9uEyIJ82EkZcCZTs9A==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.102.0': + resolution: {integrity: sha512-pqP5UuLiiFONQxqGiUFMdsfybaK1EOK4AXiPlvOvacLaatSEPObZGpyCkAcj9aZcvvNwYdeY9cxGM9IT3togaA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.102.0': + resolution: {integrity: sha512-ntMcL35wuLR1A145rLSmm7m7j8JBZGkROoB9Du0KFIFcfi/w1qk75BdCeiTl3HAKrreAnuhW3QOGs6mJhntowA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.102.0': + resolution: {integrity: sha512-8Skrw405g+/UJPKWJ1twIk3BIH2nXdiVlVNtYT23AXVwpsd79es4K+KYt06Fbnkc5BaTvk/COT2JuCLYdwnCdA==} + + '@oxc-transform/binding-android-arm64@0.102.0': + resolution: {integrity: sha512-JLBT7EiExsGmB6LuBBnm6qTfg0rLSxBU+F7xjqy6UXYpL7zhqelGJL7IAq6Pu5UYFT55zVlXXmgzLOXQfpQjXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-transform/binding-darwin-arm64@0.102.0': + resolution: {integrity: sha512-xmsBCk/NwE0khy8h6wLEexiS5abCp1ZqJUNHsAovJdGgIW21oGwhiC3VYg1vNLbq+zEXwOHuphVuNEYfBwyNTw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-transform/binding-darwin-x64@0.102.0': + resolution: {integrity: sha512-EhBsiq8hSd5BRjlWACB9MxTUiZT2He1s1b3tRP8k3lB8ZTt6sXnDXIWhxRmmM0h//xe6IJ2HuMlbvjXPo/tATg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-transform/binding-freebsd-x64@0.102.0': + resolution: {integrity: sha512-eujvuYf0x7BFgKyFecbXUa2JBEXT4Ss6vmyrrhVdN07jaeJRiobaKAmeNXBkanoWL2KQLELJbSBgs1ykWYTkzg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-transform/binding-linux-arm-gnueabihf@0.102.0': + resolution: {integrity: sha512-2x7Ro356PHBVp1SS/dOsHBSnrfs5MlPYwhdKg35t6qixt2bv1kzEH0tDmn4TNEbdjOirmvOXoCTEWUvh8A4f4Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-transform/binding-linux-arm64-gnu@0.102.0': + resolution: {integrity: sha512-Rz/RbPvT4QwcHKIQ/cOt6Lwl4c7AhK2b6whZfyL6oJ7Uz8UiVl1BCwk8thedrB5h+FEykmaPHoriW1hmBev60g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-transform/binding-linux-arm64-musl@0.102.0': + resolution: {integrity: sha512-I08iWABrN7zakn3wuNIBWY3hALQGsDLPQbZT1mXws7tyiQqJNGe49uS0/O50QhX3KXj+mbRGsmjVXLXGJE1CVQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxc-transform/binding-linux-riscv64-gnu@0.102.0': + resolution: {integrity: sha512-9+SYW1ARAF6Oj/82ayoqKRe8SI7O1qvzs3Y0kijvhIqAaaZWcFRjI5DToyWRAbnzTtHlMcSllZLXNYdmxBjFxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxc-transform/binding-linux-s390x-gnu@0.102.0': + resolution: {integrity: sha512-HV9nTyQw0TTKYPu+gBhaJBioomiM9O4LcGXi+s5IylCGG6imP0/U13q/9xJnP267QFmiWWqnnSFcv0QAWCyh8A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxc-transform/binding-linux-x64-gnu@0.102.0': + resolution: {integrity: sha512-4wcZ08mmdFk8OjsnglyeYGu5PW3TDh87AmcMOi7tZJ3cpJjfzwDfY27KTEUx6G880OpjAiF36OFSPwdKTKgp2g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-transform/binding-linux-x64-musl@0.102.0': + resolution: {integrity: sha512-rUHZSZBw0FUnUgOhL/Rs7xJz9KjH2eFur/0df6Lwq/isgJc/ggtBtFoZ+y4Fb8ON87a3Y2gS2LT7SEctX0XdPQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxc-transform/binding-openharmony-arm64@0.102.0': + resolution: {integrity: sha512-98y4tccTQ/pA+r2KA/MEJIZ7J8TNTJ4aCT4rX8kWK4pGOko2YsfY3Ru9DVHlLDwmVj7wP8Z4JNxdBrAXRvK+0g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-transform/binding-wasm32-wasi@0.102.0': + resolution: {integrity: sha512-M6myOXxHty3L2TJEB1NlJPtQm0c0LmivAxcGv/+DSDadOoB/UnOUbjM8W2Utlh5IYS9ARSOjqHtBiPYLWJ15XA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-transform/binding-win32-arm64-msvc@0.102.0': + resolution: {integrity: sha512-jzaA1lLiMXiJs4r7E0BHRxTPiwAkpoCfSNRr8npK/SqL4UQE4cSz3WDTX5wJWRrN2U+xqsDGefeYzH4reI8sgw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-transform/binding-win32-x64-msvc@0.102.0': + resolution: {integrity: sha512-eYOm6mch+1cP9qlNkMdorfBFY8aEOxY/isqrreLmEWqF/hyXA0SbLKDigTbvh3JFKny/gXlHoCKckqfua4cwtg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/binding-android-arm64@1.0.0-beta.54': + resolution: {integrity: sha512-zZRx/ur3Fai3fxiEmVp48+6GCBR48PRWJR1X3TTMn9yiq2bBHlYPgBaQtDOYWXv5H3J5dXujeTyGnuoY+kdGCg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-beta.54': + resolution: {integrity: sha512-zMyFEJmbIs91x22HAA/eUvmZHgjX8tGsD3TJ+WC9aY4bCdl3w84H9vMZmChSHAF1dYvGNH4KQDI2IubeZaCYtg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-beta.54': + resolution: {integrity: sha512-Ex7QttdaVnEpmE/zroUT5Qm10e2+Vjd9q0LX9eXm59SitxDODMpC8GI1Rct5RrLf4GLU4DzdXBj6DGzuR+6g6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-beta.54': + resolution: {integrity: sha512-E1XO10ryM/Vxw3Q1wvs9s2mSpVBfbHtzkbJcdu26qh17ZmVwNWLiIoqEcbkXm028YwkReG4Gd2gCZ3NxgTQ28Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.54': + resolution: {integrity: sha512-oS73Uks8jczQR9pg0Bj718vap/x71exyJ5yuxu4X5V4MhwRQnky7ANSPm6ARUfraxOqt49IBfcMeGnw2rTSqdA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.54': + resolution: {integrity: sha512-pY8N2X5C+/ZQcy0eRdfOzOP//OFngP1TaIqDjFwfBPws2UNavKS8SpxhPEgUaYIaT0keVBd/TB+eVy9z+CIOtw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.54': + resolution: {integrity: sha512-cgTooAFm2MUmFriB7IYaWBNyqrGlRPKG+yaK2rGFl2rcdOcO24urY4p3eyB0ogqsRLvJbIxwjjYiWiIP7Eo1Cw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.54': + resolution: {integrity: sha512-nGyLT1Qau0W+kEL44V2jhHmvfS3wyJW08E4WEu2E6NuIy+uChKN1X0aoxzFIDi2owDsYaZYez/98/f268EupIQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.54': + resolution: {integrity: sha512-KH374P0TUjDXssROT/orvzaWrzGOptD13PTrltgKwbDprJTMknoLiYsOD6Ttz92O2VuAcCtFuJ1xbyFM2Uo/Xg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.54': + resolution: {integrity: sha512-oMAVO4wbfAbhpBxPsSp8R7ntL2DchpNfO+tGhN8/sI9jsbYwOv78uIW1fTwOBslhjTVFltGJ+l23mubNQcYNaQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.54': + resolution: {integrity: sha512-MYY/FmY+HehHiQkNx04W5oLy/Fqd1hXYqZmmorSDXvAHnxMbSgmdFicKsSYOg/sVGHBMEP1tTn6kV5sWrS45rA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.54': + resolution: {integrity: sha512-66o3uKxUmcYskT9exskxs3OVduXf5x0ndlMkYOjSpBgqzhLtkub136yDvZkNT1OkNDET0odSwcU7aWdpnwzAyg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.54': + resolution: {integrity: sha512-FbbbrboChLBXfeEsOfaypBGqzbdJ/CcSA2BPLCggojnIHy58Jo+AXV7HATY8opZk7194rRbokIT8AfPJtZAWtg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-beta.54': + resolution: {integrity: sha512-AHgcZ+w7RIRZ65ihSQL8YuoKcpD9Scew4sEeP1BBUT9QdTo6KjwHrZZXjID6nL10fhKessCH6OPany2QKwAwTQ==} + + '@rollup/rollup-android-arm-eabi@4.53.4': + resolution: {integrity: sha512-PWU3Y92H4DD0bOqorEPp1Y0tbzwAurFmIYpjcObv5axGVOtcTlB0b2UKMd2echo08MgN7jO8WQZSSysvfisFSQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.53.4': + resolution: {integrity: sha512-Gw0/DuVm3rGsqhMGYkSOXXIx20cC3kTlivZeuaGt4gEgILivykNyBWxeUV5Cf2tDA2nPLah26vq3emlRrWVbng==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.53.4': + resolution: {integrity: sha512-+w06QvXsgzKwdVg5qRLZpTHh1bigHZIqoIUPtiqh05ZiJVUQ6ymOxaPkXTvRPRLH88575ZCRSRM3PwIoNma01Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.53.4': + resolution: {integrity: sha512-EB4Na9G2GsrRNRNFPuxfwvDRDUwQEzJPpiK1vo2zMVhEeufZ1k7J1bKnT0JYDfnPC7RNZ2H5YNQhW6/p2QKATw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.53.4': + resolution: {integrity: sha512-bldA8XEqPcs6OYdknoTMaGhjytnwQ0NClSPpWpmufOuGPN5dDmvIa32FygC2gneKK4A1oSx86V1l55hyUWUYFQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.53.4': + resolution: {integrity: sha512-3T8GPjH6mixCd0YPn0bXtcuSXi1Lj+15Ujw2CEb7dd24j9thcKscCf88IV7n76WaAdorOzAgSSbuVRg4C8V8Qw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.53.4': + resolution: {integrity: sha512-UPMMNeC4LXW7ZSHxeP3Edv09aLsFUMaD1TSVW6n1CWMECnUIJMFFB7+XC2lZTdPtvB36tYC0cJWc86mzSsaviw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.53.4': + resolution: {integrity: sha512-H8uwlV0otHs5Q7WAMSoyvjV9DJPiy5nJ/xnHolY0QptLPjaSsuX7tw+SPIfiYH6cnVx3fe4EWFafo6gH6ekZKA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.53.4': + resolution: {integrity: sha512-BLRwSRwICXz0TXkbIbqJ1ibK+/dSBpTJqDClF61GWIrxTXZWQE78ROeIhgl5MjVs4B4gSLPCFeD4xML9vbzvCQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.53.4': + resolution: {integrity: sha512-6bySEjOTbmVcPJAywjpGLckK793A0TJWSbIa0sVwtVGfe/Nz6gOWHOwkshUIAp9j7wg2WKcA4Snu7Y1nUZyQew==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.4': + resolution: {integrity: sha512-U0ow3bXYJZ5MIbchVusxEycBw7bO6C2u5UvD31i5IMTrnt2p4Fh4ZbHSdc/31TScIJQYHwxbj05BpevB3201ug==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.53.4': + resolution: {integrity: sha512-iujDk07ZNwGLVn0YIWM80SFN039bHZHCdCCuX9nyx3Jsa2d9V/0Y32F+YadzwbvDxhSeVo9zefkoPnXEImnM5w==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.53.4': + resolution: {integrity: sha512-MUtAktiOUSu+AXBpx1fkuG/Bi5rhlorGs3lw5QeJ2X3ziEGAq7vFNdWVde6XGaVqi0LGSvugwjoxSNJfHFTC0g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.53.4': + resolution: {integrity: sha512-btm35eAbDfPtcFEgaXCI5l3c2WXyzwiE8pArhd66SDtoLWmgK5/M7CUxmUglkwtniPzwvWioBKKl6IXLbPf2sQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.53.4': + resolution: {integrity: sha512-uJlhKE9ccUTCUlK+HUz/80cVtx2RayadC5ldDrrDUFaJK0SNb8/cCmC9RhBhIWuZ71Nqj4Uoa9+xljKWRogdhA==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.53.4': + resolution: {integrity: sha512-jjEMkzvASQBbzzlzf4os7nzSBd/cvPrpqXCUOqoeCh1dQ4BP3RZCJk8XBeik4MUln3m+8LeTJcY54C/u8wb3DQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.53.4': + resolution: {integrity: sha512-lu90KG06NNH19shC5rBPkrh6mrTpq5kviFylPBXQVpdEu0yzb0mDgyxLr6XdcGdBIQTH/UAhDJnL+APZTBu1aQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.4': + resolution: {integrity: sha512-dFDcmLwsUzhAm/dn0+dMOQZoONVYBtgik0VuY/d5IJUUb787L3Ko/ibvTvddqhb3RaB7vFEozYevHN4ox22R/w==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.53.4': + resolution: {integrity: sha512-WvUpUAWmUxZKtRnQWpRKnLW2DEO8HB/l8z6oFFMNuHndMzFTJEXzaYJ5ZAmzNw0L21QQJZsUQFt2oPf3ykAD/w==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.53.4': + resolution: {integrity: sha512-JGbeF2/FDU0x2OLySw/jgvkwWUo05BSiJK0dtuI4LyuXbz3wKiC1xHhLB1Tqm5VU6ZZDmAorj45r/IgWNWku5g==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.4': + resolution: {integrity: sha512-zuuC7AyxLWLubP+mlUwEyR8M1ixW1ERNPHJfXm8x7eQNP4Pzkd7hS3qBuKBR70VRiQ04Kw8FNfRMF5TNxuZq2g==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.4': + resolution: {integrity: sha512-Sbx45u/Lbb5RyptSbX7/3deP+/lzEmZ0BTSHxwxN/IMOZDZf8S0AGo0hJD5n/LQssxb5Z3B4og4P2X6Dd8acCA==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@vitest/expect@4.0.15': + resolution: {integrity: sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==} + + '@vitest/mocker@4.0.15': + resolution: {integrity: sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.15': + resolution: {integrity: sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==} + + '@vitest/runner@4.0.15': + resolution: {integrity: sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==} + + '@vitest/snapshot@4.0.15': + resolution: {integrity: sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==} + + '@vitest/spy@4.0.15': + resolution: {integrity: sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==} + + '@vitest/utils@4.0.15': + resolution: {integrity: sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-kit@2.2.0: + resolution: {integrity: sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==} + engines: {node: '>=20.19.0'} + + birpc@3.0.0: + resolution: {integrity: sha512-by+04pHuxpCEQcucAXqzopqfhyI8TLK5Qg5MST0cB6MP+JhHna9ollrtK9moVh27aq6Q6MEJgebD0cVm//yBkg==} + + c12@3.3.2: + resolution: {integrity: sha512-QkikB2X5voO1okL3QsES0N690Sn/K9WokXqUsDQsWy5SnYb+psYQFGA10iy1bZHj3fjISKsI67Q90gruvWWM3A==} + peerDependencies: + magicast: '*' + peerDependenciesMeta: + magicast: + optional: true + + chai@6.2.1: + resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} + engines: {node: '>=18'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} + engines: {node: '>=12'} + + dts-resolver@2.1.3: + resolution: {integrity: sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==} + engines: {node: '>=20.19.0'} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esbuild@0.27.1: + resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==} + engines: {node: '>=18'} + hasBin: true + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + giget@2.0.0: + resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} + hasBin: true + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + nypm@0.6.2: + resolution: {integrity: sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: true + + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + obuild@0.4.8: + resolution: {integrity: sha512-gFr6JPaMZOZmYGAwBQuJUpCgb9vV3JGPQ8ufEIyb3DBQv9MPM5z3DWw4bBnmfUs2fSj3CH7wTXk6jlxWvqRZKw==} + hasBin: true + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + oxc-minify@0.102.0: + resolution: {integrity: sha512-FphAHDyTCNepQbiQTSyWFMbNc9zdUmj1WBsoLwvZhWm7rEe/IeIKYKRhy75lWOjwFsi5/i4Qucq43hgs3n2Exw==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-parser@0.102.0: + resolution: {integrity: sha512-xMiyHgr2FZsphQ12ZCsXRvSYzmKXCm1ejmyG4GDZIiKOmhyt5iKtWq0klOfFsEQ6jcgbwrUdwcCVYzr1F+h5og==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-transform@0.102.0: + resolution: {integrity: sha512-MR5ohiBS6/kvxRpmUZ3LIDTTJBEC4xLAEZXfYr7vrA0eP7WHewQaNQPFDgT4Bee89TdmVQ5ZKrifGwxLjSyHHw==} + engines: {node: ^20.19.0 || >=22.12.0} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + perfect-debounce@2.0.0: + resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pkg-types@2.3.0: + resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} + engines: {node: '>=14'} + hasBin: true + + pretty-bytes@7.1.0: + resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} + engines: {node: '>=20'} + + rc9@2.1.2: + resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + rolldown-plugin-dts@0.18.3: + resolution: {integrity: sha512-rd1LZ0Awwfyn89UndUF/HoFF4oH9a5j+2ZeuKSJYM80vmeN/p0gslYMnHTQHBEXPhUlvAlqGA3tVgXB/1qFNDg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@ts-macro/tsc': ^0.3.6 + '@typescript/native-preview': '>=7.0.0-dev.20250601.1' + rolldown: ^1.0.0-beta.51 + typescript: ^5.0.0 + vue-tsc: ~3.1.0 + peerDependenciesMeta: + '@ts-macro/tsc': + optional: true + '@typescript/native-preview': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + + rolldown@1.0.0-beta.54: + resolution: {integrity: sha512-3lIvjCWgjPL3gmiATUdV1NeVBGJZy6FdtwgLPol25tAkn46Q/MsVGfCSNswXwFOxGrxglPaN20IeALSIFuFyEg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rollup@4.53.4: + resolution: {integrity: sha512-YpXaaArg0MvrnJpvduEDYIp7uGOqKXbH9NsHGQ6SxKCOsNAjZF018MmxefFUulVP2KLtiGw1UvZbr+/ekjvlDg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + vite@7.3.0: + resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.0.15: + resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.15 + '@vitest/browser-preview': 4.0.15 + '@vitest/browser-webdriverio': 4.0.15 + '@vitest/ui': 4.0.15 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + +snapshots: + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@emnapi/core@1.7.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.7.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.27.1': + optional: true + + '@esbuild/android-arm64@0.27.1': + optional: true + + '@esbuild/android-arm@0.27.1': + optional: true + + '@esbuild/android-x64@0.27.1': + optional: true + + '@esbuild/darwin-arm64@0.27.1': + optional: true + + '@esbuild/darwin-x64@0.27.1': + optional: true + + '@esbuild/freebsd-arm64@0.27.1': + optional: true + + '@esbuild/freebsd-x64@0.27.1': + optional: true + + '@esbuild/linux-arm64@0.27.1': + optional: true + + '@esbuild/linux-arm@0.27.1': + optional: true + + '@esbuild/linux-ia32@0.27.1': + optional: true + + '@esbuild/linux-loong64@0.27.1': + optional: true + + '@esbuild/linux-mips64el@0.27.1': + optional: true + + '@esbuild/linux-ppc64@0.27.1': + optional: true + + '@esbuild/linux-riscv64@0.27.1': + optional: true + + '@esbuild/linux-s390x@0.27.1': + optional: true + + '@esbuild/linux-x64@0.27.1': + optional: true + + '@esbuild/netbsd-arm64@0.27.1': + optional: true + + '@esbuild/netbsd-x64@0.27.1': + optional: true + + '@esbuild/openbsd-arm64@0.27.1': + optional: true + + '@esbuild/openbsd-x64@0.27.1': + optional: true + + '@esbuild/openharmony-arm64@0.27.1': + optional: true + + '@esbuild/sunos-x64@0.27.1': + optional: true + + '@esbuild/win32-arm64@0.27.1': + optional: true + + '@esbuild/win32-ia32@0.27.1': + optional: true + + '@esbuild/win32-x64@0.27.1': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jsr/std__cli@1.0.24': + dependencies: + '@jsr/std__internal': 1.0.12 + + '@jsr/std__internal@1.0.12': {} + + '@napi-rs/wasm-runtime@1.1.0': + dependencies: + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@oxc-minify/binding-android-arm64@0.102.0': + optional: true + + '@oxc-minify/binding-darwin-arm64@0.102.0': + optional: true + + '@oxc-minify/binding-darwin-x64@0.102.0': + optional: true + + '@oxc-minify/binding-freebsd-x64@0.102.0': + optional: true + + '@oxc-minify/binding-linux-arm-gnueabihf@0.102.0': + optional: true + + '@oxc-minify/binding-linux-arm64-gnu@0.102.0': + optional: true + + '@oxc-minify/binding-linux-arm64-musl@0.102.0': + optional: true + + '@oxc-minify/binding-linux-riscv64-gnu@0.102.0': + optional: true + + '@oxc-minify/binding-linux-s390x-gnu@0.102.0': + optional: true + + '@oxc-minify/binding-linux-x64-gnu@0.102.0': + optional: true + + '@oxc-minify/binding-linux-x64-musl@0.102.0': + optional: true + + '@oxc-minify/binding-openharmony-arm64@0.102.0': + optional: true + + '@oxc-minify/binding-wasm32-wasi@0.102.0': + dependencies: + '@napi-rs/wasm-runtime': 1.1.0 + optional: true + + '@oxc-minify/binding-win32-arm64-msvc@0.102.0': + optional: true + + '@oxc-minify/binding-win32-x64-msvc@0.102.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.102.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.102.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.102.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.102.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.102.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.102.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.102.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.102.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.102.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.102.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.102.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.102.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.102.0': + dependencies: + '@napi-rs/wasm-runtime': 1.1.0 + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.102.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.102.0': + optional: true + + '@oxc-project/types@0.102.0': {} + + '@oxc-transform/binding-android-arm64@0.102.0': + optional: true + + '@oxc-transform/binding-darwin-arm64@0.102.0': + optional: true + + '@oxc-transform/binding-darwin-x64@0.102.0': + optional: true + + '@oxc-transform/binding-freebsd-x64@0.102.0': + optional: true + + '@oxc-transform/binding-linux-arm-gnueabihf@0.102.0': + optional: true + + '@oxc-transform/binding-linux-arm64-gnu@0.102.0': + optional: true + + '@oxc-transform/binding-linux-arm64-musl@0.102.0': + optional: true + + '@oxc-transform/binding-linux-riscv64-gnu@0.102.0': + optional: true + + '@oxc-transform/binding-linux-s390x-gnu@0.102.0': + optional: true + + '@oxc-transform/binding-linux-x64-gnu@0.102.0': + optional: true + + '@oxc-transform/binding-linux-x64-musl@0.102.0': + optional: true + + '@oxc-transform/binding-openharmony-arm64@0.102.0': + optional: true + + '@oxc-transform/binding-wasm32-wasi@0.102.0': + dependencies: + '@napi-rs/wasm-runtime': 1.1.0 + optional: true + + '@oxc-transform/binding-win32-arm64-msvc@0.102.0': + optional: true + + '@oxc-transform/binding-win32-x64-msvc@0.102.0': + optional: true + + '@rolldown/binding-android-arm64@1.0.0-beta.54': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-beta.54': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-beta.54': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-beta.54': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.54': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.54': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.54': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.54': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.54': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.54': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.54': + dependencies: + '@napi-rs/wasm-runtime': 1.1.0 + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.54': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.54': + optional: true + + '@rolldown/pluginutils@1.0.0-beta.54': {} + + '@rollup/rollup-android-arm-eabi@4.53.4': + optional: true + + '@rollup/rollup-android-arm64@4.53.4': + optional: true + + '@rollup/rollup-darwin-arm64@4.53.4': + optional: true + + '@rollup/rollup-darwin-x64@4.53.4': + optional: true + + '@rollup/rollup-freebsd-arm64@4.53.4': + optional: true + + '@rollup/rollup-freebsd-x64@4.53.4': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.53.4': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.53.4': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.53.4': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.53.4': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.4': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.53.4': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.53.4': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.53.4': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.53.4': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.53.4': + optional: true + + '@rollup/rollup-linux-x64-musl@4.53.4': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.4': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.53.4': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.53.4': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.53.4': + optional: true + + '@standard-schema/spec@1.0.0': {} + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + + '@vitest/expect@4.0.15': + dependencies: + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 + chai: 6.2.1 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.15(vite@7.3.0(jiti@2.6.1))': + dependencies: + '@vitest/spy': 4.0.15 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.0(jiti@2.6.1) + + '@vitest/pretty-format@4.0.15': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.15': + dependencies: + '@vitest/utils': 4.0.15 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.15': + dependencies: + '@vitest/pretty-format': 4.0.15 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.0.15': {} + + '@vitest/utils@4.0.15': + dependencies: + '@vitest/pretty-format': 4.0.15 + tinyrainbow: 3.0.3 + + assertion-error@2.0.1: {} + + ast-kit@2.2.0: + dependencies: + '@babel/parser': 7.28.5 + pathe: 2.0.3 + + birpc@3.0.0: {} + + c12@3.3.2: + dependencies: + chokidar: 4.0.3 + confbox: 0.2.2 + defu: 6.1.4 + dotenv: 17.2.3 + exsolve: 1.0.8 + giget: 2.0.0 + jiti: 2.6.1 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 2.0.0 + pkg-types: 2.3.0 + rc9: 2.1.2 + + chai@6.2.1: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + citty@0.1.6: + dependencies: + consola: 3.4.2 + + confbox@0.2.2: {} + + consola@3.4.2: {} + + defu@6.1.4: {} + + destr@2.0.5: {} + + dotenv@17.2.3: {} + + dts-resolver@2.1.3: {} + + es-module-lexer@1.7.0: {} + + esbuild@0.27.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.1 + '@esbuild/android-arm': 0.27.1 + '@esbuild/android-arm64': 0.27.1 + '@esbuild/android-x64': 0.27.1 + '@esbuild/darwin-arm64': 0.27.1 + '@esbuild/darwin-x64': 0.27.1 + '@esbuild/freebsd-arm64': 0.27.1 + '@esbuild/freebsd-x64': 0.27.1 + '@esbuild/linux-arm': 0.27.1 + '@esbuild/linux-arm64': 0.27.1 + '@esbuild/linux-ia32': 0.27.1 + '@esbuild/linux-loong64': 0.27.1 + '@esbuild/linux-mips64el': 0.27.1 + '@esbuild/linux-ppc64': 0.27.1 + '@esbuild/linux-riscv64': 0.27.1 + '@esbuild/linux-s390x': 0.27.1 + '@esbuild/linux-x64': 0.27.1 + '@esbuild/netbsd-arm64': 0.27.1 + '@esbuild/netbsd-x64': 0.27.1 + '@esbuild/openbsd-arm64': 0.27.1 + '@esbuild/openbsd-x64': 0.27.1 + '@esbuild/openharmony-arm64': 0.27.1 + '@esbuild/sunos-x64': 0.27.1 + '@esbuild/win32-arm64': 0.27.1 + '@esbuild/win32-ia32': 0.27.1 + '@esbuild/win32-x64': 0.27.1 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + expect-type@1.3.0: {} + + exsolve@1.0.8: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fsevents@2.3.3: + optional: true + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + giget@2.0.0: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + defu: 6.1.4 + node-fetch-native: 1.6.7 + nypm: 0.6.2 + pathe: 2.0.3 + + jiti@2.6.1: {} + + jsesc@3.1.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + nanoid@3.3.11: {} + + node-fetch-native@1.6.7: {} + + nypm@0.6.2: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + pathe: 2.0.3 + pkg-types: 2.3.0 + tinyexec: 1.0.2 + + obug@2.1.1: {} + + obuild@0.4.8(typescript@5.9.3): + dependencies: + c12: 3.3.2 + consola: 3.4.2 + defu: 6.1.4 + exsolve: 1.0.8 + magic-string: 0.30.21 + oxc-minify: 0.102.0 + oxc-parser: 0.102.0 + oxc-transform: 0.102.0 + pathe: 2.0.3 + pretty-bytes: 7.1.0 + rolldown: 1.0.0-beta.54 + rolldown-plugin-dts: 0.18.3(rolldown@1.0.0-beta.54)(typescript@5.9.3) + tinyglobby: 0.2.15 + transitivePeerDependencies: + - '@ts-macro/tsc' + - '@typescript/native-preview' + - magicast + - oxc-resolver + - typescript + - vue-tsc + + ohash@2.0.11: {} + + oxc-minify@0.102.0: + optionalDependencies: + '@oxc-minify/binding-android-arm64': 0.102.0 + '@oxc-minify/binding-darwin-arm64': 0.102.0 + '@oxc-minify/binding-darwin-x64': 0.102.0 + '@oxc-minify/binding-freebsd-x64': 0.102.0 + '@oxc-minify/binding-linux-arm-gnueabihf': 0.102.0 + '@oxc-minify/binding-linux-arm64-gnu': 0.102.0 + '@oxc-minify/binding-linux-arm64-musl': 0.102.0 + '@oxc-minify/binding-linux-riscv64-gnu': 0.102.0 + '@oxc-minify/binding-linux-s390x-gnu': 0.102.0 + '@oxc-minify/binding-linux-x64-gnu': 0.102.0 + '@oxc-minify/binding-linux-x64-musl': 0.102.0 + '@oxc-minify/binding-openharmony-arm64': 0.102.0 + '@oxc-minify/binding-wasm32-wasi': 0.102.0 + '@oxc-minify/binding-win32-arm64-msvc': 0.102.0 + '@oxc-minify/binding-win32-x64-msvc': 0.102.0 + + oxc-parser@0.102.0: + dependencies: + '@oxc-project/types': 0.102.0 + optionalDependencies: + '@oxc-parser/binding-android-arm64': 0.102.0 + '@oxc-parser/binding-darwin-arm64': 0.102.0 + '@oxc-parser/binding-darwin-x64': 0.102.0 + '@oxc-parser/binding-freebsd-x64': 0.102.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.102.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.102.0 + '@oxc-parser/binding-linux-arm64-musl': 0.102.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.102.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.102.0 + '@oxc-parser/binding-linux-x64-gnu': 0.102.0 + '@oxc-parser/binding-linux-x64-musl': 0.102.0 + '@oxc-parser/binding-openharmony-arm64': 0.102.0 + '@oxc-parser/binding-wasm32-wasi': 0.102.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.102.0 + '@oxc-parser/binding-win32-x64-msvc': 0.102.0 + + oxc-transform@0.102.0: + optionalDependencies: + '@oxc-transform/binding-android-arm64': 0.102.0 + '@oxc-transform/binding-darwin-arm64': 0.102.0 + '@oxc-transform/binding-darwin-x64': 0.102.0 + '@oxc-transform/binding-freebsd-x64': 0.102.0 + '@oxc-transform/binding-linux-arm-gnueabihf': 0.102.0 + '@oxc-transform/binding-linux-arm64-gnu': 0.102.0 + '@oxc-transform/binding-linux-arm64-musl': 0.102.0 + '@oxc-transform/binding-linux-riscv64-gnu': 0.102.0 + '@oxc-transform/binding-linux-s390x-gnu': 0.102.0 + '@oxc-transform/binding-linux-x64-gnu': 0.102.0 + '@oxc-transform/binding-linux-x64-musl': 0.102.0 + '@oxc-transform/binding-openharmony-arm64': 0.102.0 + '@oxc-transform/binding-wasm32-wasi': 0.102.0 + '@oxc-transform/binding-win32-arm64-msvc': 0.102.0 + '@oxc-transform/binding-win32-x64-msvc': 0.102.0 + + pathe@2.0.3: {} + + perfect-debounce@2.0.0: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + pkg-types@2.3.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.8 + pathe: 2.0.3 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prettier@3.7.4: {} + + pretty-bytes@7.1.0: {} + + rc9@2.1.2: + dependencies: + defu: 6.1.4 + destr: 2.0.5 + + readdirp@4.1.2: {} + + resolve-pkg-maps@1.0.0: {} + + rolldown-plugin-dts@0.18.3(rolldown@1.0.0-beta.54)(typescript@5.9.3): + dependencies: + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + ast-kit: 2.2.0 + birpc: 3.0.0 + dts-resolver: 2.1.3 + get-tsconfig: 4.13.0 + magic-string: 0.30.21 + obug: 2.1.1 + rolldown: 1.0.0-beta.54 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - oxc-resolver + + rolldown@1.0.0-beta.54: + dependencies: + '@oxc-project/types': 0.102.0 + '@rolldown/pluginutils': 1.0.0-beta.54 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-beta.54 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.54 + '@rolldown/binding-darwin-x64': 1.0.0-beta.54 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.54 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.54 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.54 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.54 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.54 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.54 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.54 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.54 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.54 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.54 + + rollup@4.53.4: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.4 + '@rollup/rollup-android-arm64': 4.53.4 + '@rollup/rollup-darwin-arm64': 4.53.4 + '@rollup/rollup-darwin-x64': 4.53.4 + '@rollup/rollup-freebsd-arm64': 4.53.4 + '@rollup/rollup-freebsd-x64': 4.53.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.4 + '@rollup/rollup-linux-arm-musleabihf': 4.53.4 + '@rollup/rollup-linux-arm64-gnu': 4.53.4 + '@rollup/rollup-linux-arm64-musl': 4.53.4 + '@rollup/rollup-linux-loong64-gnu': 4.53.4 + '@rollup/rollup-linux-ppc64-gnu': 4.53.4 + '@rollup/rollup-linux-riscv64-gnu': 4.53.4 + '@rollup/rollup-linux-riscv64-musl': 4.53.4 + '@rollup/rollup-linux-s390x-gnu': 4.53.4 + '@rollup/rollup-linux-x64-gnu': 4.53.4 + '@rollup/rollup-linux-x64-musl': 4.53.4 + '@rollup/rollup-openharmony-arm64': 4.53.4 + '@rollup/rollup-win32-arm64-msvc': 4.53.4 + '@rollup/rollup-win32-ia32-msvc': 4.53.4 + '@rollup/rollup-win32-x64-gnu': 4.53.4 + '@rollup/rollup-win32-x64-msvc': 4.53.4 + fsevents: 2.3.3 + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@3.10.0: {} + + tinybench@2.9.0: {} + + tinyexec@1.0.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinyrainbow@3.0.3: {} + + tslib@2.8.1: + optional: true + + typescript@5.9.3: {} + + vite@7.3.0(jiti@2.6.1): + dependencies: + esbuild: 0.27.1 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.4 + tinyglobby: 0.2.15 + optionalDependencies: + fsevents: 2.3.3 + jiti: 2.6.1 + + vitest@4.0.15(jiti@2.6.1): + dependencies: + '@vitest/expect': 4.0.15 + '@vitest/mocker': 4.0.15(vite@7.3.0(jiti@2.6.1)) + '@vitest/pretty-format': 4.0.15 + '@vitest/runner': 4.0.15 + '@vitest/snapshot': 4.0.15 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.0(jiti@2.6.1) + why-is-node-running: 2.3.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..efc037a --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +onlyBuiltDependencies: + - esbuild diff --git a/scripts/build_npm.ts b/scripts/build_npm.ts deleted file mode 100644 index bcf6721..0000000 --- a/scripts/build_npm.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { build, emptyDir } from "@deno/dnt"; - -import denoJson from "../deno.json" with { type: "json" }; - -await emptyDir("./npm"); - -await build({ - entryPoints: ["./mod.ts"], - outDir: "./npm", - shims: { - // see JS docs for overview and more options - deno: true, - }, - compilerOptions: { - lib: ["ES2022"], - }, - package: { - // package.json properties - name: "@textplace/core", - version: denoJson.version, - description: "The core logic of TextPlace.", - license: "MIT", - repository: { - type: "git", - url: "https://github.com/TextPlace/CoreTextPlace", - }, - bugs: { - url: "https://github.com/TextPlace/CoreTextPlace/issues", - }, - }, - postBuild() { - // steps to run after building and before running the tests - Deno.copyFileSync("LICENSE", "npm/LICENSE"); - Deno.copyFileSync("README.md", "npm/README.md"); - }, -}); diff --git a/src/logic/board.ts b/src/logic/board.ts new file mode 100644 index 0000000..9d199c0 --- /dev/null +++ b/src/logic/board.ts @@ -0,0 +1,55 @@ +import type { SectionData, SectionPosition } from "../types/section"; +import type { BoardConfig, BoardData, CharacterPosition } from "../types/board"; +import { applyChange, createSection } from "./section"; +import type { BoardChange } from "../types/change"; + +export function createBoard(config: BoardConfig): BoardData { + return { config, sections: [] }; +} + +export function locateSection( + { x, y }: CharacterPosition, + config: BoardConfig, +): SectionPosition { + return { + sx: Math.floor(x / config.sectionWidth), + sy: Math.floor(y / config.sectionHeight), + }; +} + +/** + * Get a section from board. + * + * If the section does not exist yet, it will be created and (optionally) added to `board`. + */ +export function getSectionOnBoard( + { sx, sy }: SectionPosition, + board: BoardData, + options: { + /** + * Whether the section data is only used for reading. + * + * If `true`, this function will return an empty section configured with default values, but will not add it to the board data to save storage. + */ + readOnly: boolean; + } = { readOnly: false }, +): SectionData { + if (!board.sections[sy]) { + if (options.readOnly) return createSection({ sx, sy }, board.config); + board.sections[sy] = []; + } + + const row = board.sections[sy]; + const existing = row[sx]; + if (existing) return existing; + + const section = createSection({ sx, sy }, board.config); + if (!options.readOnly) row[sx] = section; + return section; +} + +export function applyChangeOnBoard(change: BoardChange, board: BoardData) { + const sPos = locateSection(change, board.config); + const section = getSectionOnBoard(sPos, board); + applyChange(change, section); +} diff --git a/src/logic/character.ts b/src/logic/character.ts new file mode 100644 index 0000000..5d046ff --- /dev/null +++ b/src/logic/character.ts @@ -0,0 +1,17 @@ +import { unicodeWidth } from "@std/cli/unicode-width"; + +const segmenter = /*#__PURE__*/ new Intl.Segmenter("en", { + granularity: "grapheme", +}); + +export function getCharacterWidth(ch: string): number { + const segments = [...segmenter.segment(ch)]; + if (segments.length !== 1) { + throw new Error( + `Expected exactly one grapheme cluster, got ${segments.length}.`, + ); + } + + // TODO: Properly fix this. + return Math.min(unicodeWidth(ch), 2); +} diff --git a/src/logic/render.ts b/src/logic/render.ts new file mode 100644 index 0000000..2eb72a4 --- /dev/null +++ b/src/logic/render.ts @@ -0,0 +1,127 @@ +import type { BoardData, BoardRegion } from "../types/board"; +import type { BoardRender } from "../types/render"; +import { getSectionOnBoard, locateSection } from "./board"; + +export function render(data: BoardData): BoardRender { + const totalLineCount = data.config.sectionHeight * data.config.ySections; + const lineLength = data.config.sectionWidth * data.config.xSections; + + const chLines: string[][] = Array(totalLineCount); + const colorLines: string[][] = Array(totalLineCount); + const bgColorLines: string[][] = Array(totalLineCount); + const widthLines: number[][] = Array(totalLineCount); + + for (let y = 0; y < totalLineCount; y++) { + const chLine: string[] = []; + const colorLine: string[] = []; + const bgColorLine: string[] = []; + const widthLine: number[] = []; + + let charsToSkip = 0; + + for (let x = 0; x < lineLength; x++) { + if (charsToSkip > 0) { + charsToSkip--; + continue; + } + + const sPos = locateSection({ x, y }, data.config); + const section = getSectionOnBoard(sPos, data, { readOnly: true }); + const xInSection = x % data.config.sectionWidth; + const yInSection = y % data.config.sectionHeight; + + const cCh = section.ch[yInSection]?.[xInSection] ?? " "; + const cCo = section.color[yInSection]?.[xInSection] ?? ""; + const cBg = section.bgColor[yInSection]?.[xInSection] ?? ""; + const cWd = section.width[yInSection]?.[xInSection] ?? 1; + + chLine.push(cCh); + colorLine.push(cCo); + bgColorLine.push(cBg); + widthLine.push(cWd); + charsToSkip += cWd - 1; + } + + chLines[y] = chLine; + colorLines[y] = colorLine; + bgColorLines[y] = bgColorLine; + widthLines[y] = widthLine; + } + + return { + w: lineLength, + h: totalLineCount, + ch: chLines.flat(), + color: colorLines.flat(), + bg_color: bgColorLines.flat(), + width: widthLines.flat(), + }; +} + +export function cropRender( + render: BoardRender, + region: BoardRegion, +): BoardRender { + const ch: string[] = []; + const color: string[] = []; + const bg_color: string[] = []; + const width: number[] = []; + + const regionEndX = region.x + region.width; + const regionEndY = region.y + region.height; + + let srcIdx = 0; + let displayX = 0; + let displayY = 0; + + while (srcIdx < render.ch.length) { + const cCh = render.ch[srcIdx]; + const cCo = render.color[srcIdx]; + const cBg = render.bg_color[srcIdx]; + const cWd = render.width[srcIdx]; + + if ( + typeof cCh !== "string" || + typeof cCo !== "string" || + typeof cBg !== "string" || + typeof cWd !== "number" + ) { + throw new Error("Invalid render data"); + } + + const charEndX = displayX + cWd; + + if (displayY >= region.y && displayY < regionEndY) { + // Check if this character overlaps with the crop region horizontally + if (charEndX > region.x && displayX < regionEndX) { + // Clamp the width to fit within the crop region + const clampedStartX = Math.max(displayX, region.x); + const clampedEndX = Math.min(charEndX, regionEndX); + const clampedWidth = clampedEndX - clampedStartX; + + ch.push(cCh); + color.push(cCo); + bg_color.push(cBg); + width.push(clampedWidth); + } + } + + // Advance display position + displayX += cWd; + if (displayX >= render.w) { + displayX = 0; + displayY++; + } + + srcIdx++; + } + + return { + w: region.width, + h: region.height, + ch, + color, + bg_color, + width, + }; +} diff --git a/src/logic/section.ts b/src/logic/section.ts new file mode 100644 index 0000000..54a6d8b --- /dev/null +++ b/src/logic/section.ts @@ -0,0 +1,74 @@ +import { getCharacterWidth } from "../mod"; +import type { BoardConfig } from "../types/board"; +import type { BoardChange } from "../types/change"; +import type { SectionData, SectionPosition } from "../types/section"; + +export function createSection( + { sx, sy }: SectionPosition, + boardConfig: BoardConfig, +): SectionData { + if (boardConfig.sectionWidth % 2 !== 0) { + throw new Error( + "sectionWidth must be multiple of 2 (least common multiples of all character widths)", + ); + } + + const offsetX = sx * boardConfig.sectionWidth; + const offsetY = sy * boardConfig.sectionHeight; + + const ch: string[][] = Array(boardConfig.sectionHeight) + .fill([]) + .map(() => Array(boardConfig.sectionWidth).fill(boardConfig.defaultCh)); + const color: string[][] = Array(boardConfig.sectionHeight) + .fill([]) + .map(() => Array(boardConfig.sectionWidth).fill(boardConfig.defaultColor)); + const bgColor: string[][] = Array(boardConfig.sectionHeight) + .fill([]) + .map(() => + Array(boardConfig.sectionWidth).fill(boardConfig.defaultBgColor), + ); + const width: number[][] = Array(boardConfig.sectionHeight) + .fill([]) + .map(() => Array(boardConfig.sectionWidth).fill(boardConfig.defaultWidth)); + + return { offsetX, offsetY, ch, color, bgColor, width }; +} + +export function applyChange(change: BoardChange, section: SectionData) { + const xInSection = change.x - section.offsetX; + const yInSection = change.y - section.offsetY; + + const row0 = section.ch[0]; + const validX = + xInSection >= 0 && row0 !== undefined && xInSection < row0.length; + const validY = yInSection >= 0 && yInSection < section.ch.length; + + const chRow = section.ch[yInSection]; + const widthRow = section.width[yInSection]; + const colorRow = section.color[yInSection]; + const bgColorRow = section.bgColor[yInSection]; + + const hasRowsForY = + chRow !== undefined && + widthRow !== undefined && + colorRow !== undefined && + bgColorRow !== undefined; + + if (!validX || !validY || !hasRowsForY) { + throw new Error("Change does not belong to this section"); + } + + if (change.ch) { + const chWidth = getCharacterWidth(change.ch); + const xCharacterOffset = xInSection % chWidth; + const offsetAdjustedXInSection = xInSection - xCharacterOffset; + chRow[offsetAdjustedXInSection] = change.ch; + widthRow[offsetAdjustedXInSection] = chWidth; + } + if (change.color) { + colorRow[xInSection] = change.color; + } + if (change.bg_color) { + bgColorRow[xInSection] = change.bg_color; + } +} diff --git a/src/mod.ts b/src/mod.ts new file mode 100644 index 0000000..6772c4e --- /dev/null +++ b/src/mod.ts @@ -0,0 +1,9 @@ +export * from "./types/board"; +export * from "./types/change"; +export * from "./types/render"; +export * from "./types/section"; + +export * from "./logic/board"; +export * from "./logic/character"; +export * from "./logic/render"; +export * from "./logic/section"; diff --git a/types/board.ts b/src/types/board.ts similarity index 61% rename from types/board.ts rename to src/types/board.ts index c4636be..9d553f0 100644 --- a/types/board.ts +++ b/src/types/board.ts @@ -1,26 +1,5 @@ import type { SectionData } from "./section.ts"; -/** - * A compact form to represent the whole game board. - * - * Note that this form is not designed for manipulation. It's designed for transmission and rendering, and can not be converted back to `BoardData` as all "over-shadowed" characters are removed. - */ -export interface FullBoard { - /** The total width of the board, in display characters (`ch`). */ - w: number; - /** The total height of the board, in `ch`. */ - h: number; - - /** Compact string of characters on board. */ - ch: string; - /** Compact string of color, for each character. */ - color: string; - /** Compact string of background color, for each character. */ - bg_color: string; - /** Compact string of width indicator for each character. */ - width: string; -} - /** * A structure defining a character position on board. * @@ -33,6 +12,14 @@ export interface CharacterPosition { y: number; } +/** + * A structure defining a region on board. + */ +export interface BoardRegion extends CharacterPosition { + width: number; + height: number; +} + export interface BoardConfig { xSections: number; ySections: number; diff --git a/types/change.ts b/src/types/change.ts similarity index 100% rename from types/change.ts rename to src/types/change.ts diff --git a/src/types/render.ts b/src/types/render.ts new file mode 100644 index 0000000..ef7ec80 --- /dev/null +++ b/src/types/render.ts @@ -0,0 +1,20 @@ +/** + * A compact form to represent a render of a board or a part of. + * + * Note that this form is not designed for manipulation. It's designed for transmission and rendering, and can not be converted back to `BoardData` as all "over-shadowed" characters are removed. + */ +export interface BoardRender { + /** The total width of the render, in display characters (`ch`). */ + w: number; + /** The total height of the render, in `ch`. */ + h: number; + + /** Compact array of characters on board. */ + ch: string[]; + /** Compact array of color, for each character. */ + color: string[]; + /** Compact array of background color, for each character. */ + bg_color: string[]; + /** Compact array of width indicator for each character. */ + width: number[]; +} diff --git a/types/section.ts b/src/types/section.ts similarity index 100% rename from types/section.ts rename to src/types/section.ts diff --git a/tests/board.test.ts b/tests/board.test.ts index 29cd7cd..1169f8f 100644 --- a/tests/board.test.ts +++ b/tests/board.test.ts @@ -1,21 +1,17 @@ -import { - assert, - assertEquals, -} from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { describe, it, expect } from "vitest"; -import { createBoard, renderFullBoard } from "../logic/board.ts"; -import type { BoardData } from "../types/board.ts"; -import { checkFullBoard } from "./checkFullBoard.ts"; -import { locateSection } from "../logic/board.ts"; -import { applyChangeOnBoard } from "../logic/board.ts"; +import { createBoard, getSectionOnBoard } from "../src/logic/board"; +import type { BoardData } from "../src/types/board"; +import { locateSection } from "../src/logic/board"; +import { applyChangeOnBoard } from "../src/logic/board"; -Deno.test("board", async (t) => { +describe("board", () => { let board: BoardData | undefined; - await t.step("createBoard", () => { + it("createBoard", () => { board = createBoard({ - xSections: 2, - ySections: 2, + xSections: 3, + ySections: 3, sectionWidth: 4, sectionHeight: 3, defaultCh: " ", @@ -24,59 +20,90 @@ Deno.test("board", async (t) => { defaultWidth: 1, }); - assertEquals(board.sections.length, 2); - assertEquals(board.sections[0].length, 2); - assertEquals(board.sections[0][0].offsetX, 0); - assertEquals(board.sections[0][0].offsetY, 0); - assertEquals(board.sections[0][1].offsetX, 4); - assertEquals(board.sections[0][1].offsetY, 0); - assertEquals(board.sections[1].length, 2); - assertEquals(board.sections[1][0].offsetX, 0); - assertEquals(board.sections[1][0].offsetY, 3); - assertEquals(board.sections[1][1].offsetX, 4); - assertEquals(board.sections[1][1].offsetY, 3); + // Sections are created on demand. + expect(board.sections.length).toBe(0); }); - await t.step("locateSection", () => { - assert(board); + it("locateSection", () => { + expect(board).toBeDefined(); - const { sx, sy } = locateSection({ x: 0, y: 0 }, board.config); - assertEquals(sx, 0); - assertEquals(sy, 0); + const { sx, sy } = locateSection({ x: 0, y: 0 }, board!.config); + expect(sx).toBe(0); + expect(sy).toBe(0); - const { sx: sx2, sy: sy2 } = locateSection({ x: 4, y: 0 }, board.config); - assertEquals(sx2, 1); - assertEquals(sy2, 0); + const { sx: sx2, sy: sy2 } = locateSection({ x: 4, y: 0 }, board!.config); + expect(sx2).toBe(1); + expect(sy2).toBe(0); }); - await t.step("applyChangeOnBoard", () => { - assert(board); + it("applyChangeOnBoard", () => { + expect(board).toBeDefined(); - applyChangeOnBoard({ x: 0, y: 0, ch: "A" }, board); - applyChangeOnBoard({ x: 4, y: 0, ch: "B" }, board); - applyChangeOnBoard({ x: 0, y: 3, ch: "C" }, board); - applyChangeOnBoard({ x: 4, y: 3, ch: "D" }, board); + applyChangeOnBoard({ x: 0, y: 0, ch: "A" }, board!); + applyChangeOnBoard({ x: 4, y: 0, ch: "B" }, board!); + applyChangeOnBoard({ x: 0, y: 3, ch: "C" }, board!); + applyChangeOnBoard({ x: 4, y: 3, ch: "D" }, board!); + applyChangeOnBoard({ x: 5, y: 3, ch: "E" }, board!); - assertEquals(board.sections[0][0].ch[0][0], "A"); - assertEquals(board.sections[0][1].ch[0][0], "B"); - assertEquals(board.sections[1][0].ch[0][0], "C"); - assertEquals(board.sections[1][1].ch[0][0], "D"); + expect(board!.sections[0]![0]!.ch[0]![0]).toBe("A"); + expect(board!.sections[0]![1]!.ch[0]![0]).toBe("B"); + expect(board!.sections[1]![0]!.ch[0]![0]).toBe("C"); + expect(board!.sections[1]![1]!.ch[0]).toEqual(["D", "E", " ", " "]); - applyChangeOnBoard({ x: 0, y: 1, ch: "你" }, board); - applyChangeOnBoard({ x: 4, y: 2, ch: "好" }, board); - applyChangeOnBoard({ x: 0, y: 4, ch: "嗎" }, board); - applyChangeOnBoard({ x: 4, y: 4, ch: "嘛" }, board); + applyChangeOnBoard({ x: 0, y: 1, ch: "你" }, board!); + applyChangeOnBoard({ x: 4, y: 2, ch: "好" }, board!); + applyChangeOnBoard({ x: 0, y: 4, ch: "嗎" }, board!); + applyChangeOnBoard({ x: 4, y: 4, ch: "嘛" }, board!); - assertEquals(board.sections[0][0].ch[1][0], "你"); - assertEquals(board.sections[0][1].ch[2][0], "好"); - assertEquals(board.sections[1][0].ch[1][0], "嗎"); - assertEquals(board.sections[1][1].ch[2][0], "嘛"); + expect(board!.sections[0]![0]!.ch[1]![0]).toBe("你"); + expect(board!.sections[0]![1]!.ch[2]![0]).toBe("好"); + expect(board!.sections[1]![0]!.ch[1]![0]).toBe("嗎"); + expect(board!.sections[1]![1]!.ch[1]).toEqual(["嘛", " ", " ", " "]); + + applyChangeOnBoard({ x: 5, y: 4, ch: "啊" }, board!); + expect(board!.sections[1]![1]!.ch[1]).toEqual(["啊", " ", " ", " "]); }); - await t.step("renderFullBoard", () => { - assert(board); + it("getSectionOnBoard: existing section", () => { + expect(board).toBeDefined(); - const rendered = renderFullBoard(board); - checkFullBoard(rendered); + const section = getSectionOnBoard({ sx: 1, sy: 1 }, board!, { + readOnly: true, + }); + expect(section.ch[0]).toEqual(["D", "E", " ", " "]); + expect(section.color[0]![0]).toBe("F"); + expect(section.bgColor[0]![0]).toBe("0"); + expect(section.width[0]).toEqual([1, 1, 1, 1]); + }); + + it("getSectionOnBoard: non-existing row", () => { + expect(board).toBeDefined(); + + const section = getSectionOnBoard({ sx: 1, sy: 2 }, board!, { + readOnly: true, + }); + expect(section.ch[0]![0]).toBe(" "); + expect(section.color[0]![0]).toBe("F"); + expect(section.bgColor[0]![0]).toBe("0"); + expect(section.width[0]![0]).toBe(1); + }); + + it("getSectionOnBoard: non-existing section", () => { + expect(board).toBeDefined(); + + const section = getSectionOnBoard({ sx: 2, sy: 1 }, board!, { + readOnly: true, + }); + expect(section.ch[0]![0]).toBe(" "); + expect(section.color[0]![0]).toBe("F"); + expect(section.bgColor[0]![0]).toBe("0"); + expect(section.width[0]![0]).toBe(1); + }); + + it("on-demand creation: only changed sections are saved", () => { + expect(board).toBeDefined(); + + expect(board!.sections[2]).toBeUndefined(); + expect(board!.sections[0]![2]).toBeUndefined(); }); }); diff --git a/tests/character.test.ts b/tests/character.test.ts index 886d7ad..05b675a 100644 --- a/tests/character.test.ts +++ b/tests/character.test.ts @@ -1,39 +1,40 @@ -import { - assertEquals, - assertThrows, -} from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { it, expect } from "vitest"; -import { getCharacterWidth } from "../mod.ts"; +import { getCharacterWidth } from "../src/mod"; -Deno.test("getCharacterWidth ASCII", () => { - assertEquals(getCharacterWidth("a"), 1); - assertEquals(getCharacterWidth("A"), 1); - assertEquals(getCharacterWidth("1"), 1); - assertEquals(getCharacterWidth("@"), 1); - assertEquals(getCharacterWidth(" "), 1); +it("getCharacterWidth ASCII", () => { + expect(getCharacterWidth("a")).toBe(1); + expect(getCharacterWidth("A")).toBe(1); + expect(getCharacterWidth("1")).toBe(1); + expect(getCharacterWidth("@")).toBe(1); + expect(getCharacterWidth(" ")).toBe(1); - assertThrows(() => getCharacterWidth("")); - assertThrows(() => getCharacterWidth("ab")); + expect(() => getCharacterWidth("")).toThrow(); + expect(() => getCharacterWidth("ab")).toThrow(); }); -Deno.test("getCharacterWidth CJK", () => { - assertEquals(getCharacterWidth("你"), 2); - assertEquals(getCharacterWidth("好"), 2); - assertEquals(getCharacterWidth("吗"), 2); +it("getCharacterWidth CJK", () => { + expect(getCharacterWidth("你")).toBe(2); + expect(getCharacterWidth("好")).toBe(2); + expect(getCharacterWidth("吗")).toBe(2); - assertEquals(getCharacterWidth("ガ"), 2); - assertEquals(getCharacterWidth("ギ"), 2); - assertEquals(getCharacterWidth("グ"), 2); - assertEquals(getCharacterWidth("ソ"), 2); + expect(getCharacterWidth("ガ")).toBe(2); + expect(getCharacterWidth("ギ")).toBe(2); + expect(getCharacterWidth("グ")).toBe(2); + expect(getCharacterWidth("ソ")).toBe(2); - assertThrows(() => getCharacterWidth("?")); - assertThrows(() => getCharacterWidth("!")); - assertThrows(() => getCharacterWidth("你好")); - assertThrows(() => getCharacterWidth("ヨスガノ")); + expect(getCharacterWidth("?")).toBe(2); + expect(getCharacterWidth("!")).toBe(2); + expect(() => getCharacterWidth("你好")).toThrow(); + expect(() => getCharacterWidth("ヨスガノ")).toThrow(); }); -Deno.test("getCharacterWidth previously faulty cases", () => { - assertEquals(getCharacterWidth("𤲶"), 2); - - assertThrows(() => getCharacterWidth("𤲶"[0])); +it("getCharacterWidth Emoji", () => { + expect(getCharacterWidth("👋")).toBe(2); + expect(getCharacterWidth("🌲️")).toBe(2); + expect(getCharacterWidth("👨‍👩‍👧‍👦")).toBe(2); +}); + +it("getCharacterWidth previously faulty cases", () => { + expect(getCharacterWidth("𤲶")).toBe(2); }); diff --git a/tests/checkBoardRender.ts b/tests/checkBoardRender.ts new file mode 100644 index 0000000..1a17ed2 --- /dev/null +++ b/tests/checkBoardRender.ts @@ -0,0 +1,93 @@ +import { getCharacterWidth } from "../src/logic/character"; +import type { BoardRender } from "../src/types/render"; + +function isValidColor(color: string): boolean { + return /^[0-9A-F]$/.test(color); +} + +interface Options { + /** + * Whether to allow the width of a character to be narrower than the "correct" width. + * + * For partial renders, some wide characters may be clipped to a narrower width. This option allows for that. + */ + allowsNarrowerWidth?: boolean; +} + +export function checkBoardRender(render: BoardRender, options?: Options) { + let chLine = ""; + let colorLine = ""; + let bgColorLine = ""; + let widthLine = ""; + let lines = 0; + const ch = [...render.ch]; + const chLength = ch.length; + + let unsafeCurrentOffset = 0; + + function isCorrectWidth(cWd: number, cCh: string): boolean { + const correctWidth = getCharacterWidth(cCh); + return options?.allowsNarrowerWidth + ? cWd <= correctWidth && cWd > 0 + : cWd === correctWidth; + } + + for (let i = 0; i < chLength; i++) { + const cCh = ch[i]; + const cCo = render.color[i]; + const cBg = render.bg_color[i]; + const cWd = render.width[i]; + + const printSituation = () => { + console.error( + "offset:", + i, + "offset (unsafe):", + unsafeCurrentOffset, + "cCh:", + JSON.stringify(cCh), + "cCo:", + JSON.stringify(cCo), + "cBg:", + JSON.stringify(cBg), + "cWd:", + JSON.stringify(cWd), + ); + console.error("ch: ", chLine); + console.error("color: ", colorLine); + console.error("bg_color:", bgColorLine); + console.error("width: ", widthLine); + }; + + if (typeof cCh !== "string") { + printSituation(); + throw new Error("cCh is not string"); + } + + if (!isValidColor(cCo) || !isValidColor(cBg)) { + printSituation(); + throw new Error("cCo or cBg is not valid"); + } + + if (!isCorrectWidth(cWd, cCh)) { + printSituation(); + throw new Error("cWd is wrong"); + } + + chLine += cCh; + colorLine += cCo.padEnd(cWd); + bgColorLine += cBg.padEnd(cWd); + widthLine += String(cWd).padEnd(cWd); + unsafeCurrentOffset += cCh.length; + + if (colorLine.length === render.w) { + lines++; + chLine = ""; + colorLine = ""; + bgColorLine = ""; + widthLine = ""; + } + } + + if (lines !== render.h) throw new Error("board height error"); +} diff --git a/tests/checkFullBoard.ts b/tests/checkFullBoard.ts deleted file mode 100644 index 78d916c..0000000 --- a/tests/checkFullBoard.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { getCharacterWidth } from "../logic/character.ts"; -import type { FullBoard } from "../types/board.ts"; - -function isCorrectWidth(cWdRaw: string, cCh: string): boolean { - return getCharacterWidth(cCh).toString() === cWdRaw; -} - -function isValidColor(color: string): boolean { - return /^[0-9A-F]$/.test(color); -} - -export function checkFullBoard(board: FullBoard) { - let chLine = ""; - let colorLine = ""; - let bgColorLine = ""; - let widthLine = ""; - let lines = 0; - const ch = [...board.ch]; - const chLength = ch.length; - - let unsafeCurrentOffset = 0; - - for (let i = 0; i < chLength; i++) { - const cCh = ch[i]; - const cCo = board.color[i]; - const cBg = board.bg_color[i]; - const cWdRaw = board.width[i]; - const cWd = parseInt(cWdRaw); - - const printSituation = () => { - console.error( - "offset:", - i, - "offset (unsafe):", - unsafeCurrentOffset, - "cCh:", - JSON.stringify(cCh), - "cCo:", - JSON.stringify(cCo), - "cBg:", - JSON.stringify(cBg), - "cWd:", - JSON.stringify(cWdRaw), - ); - console.error("ch: ", chLine); - console.error("color: ", colorLine); - console.error("bg_color:", bgColorLine); - console.error("width: ", widthLine); - }; - - if (cCh === "\n") { - if (cCo !== "\n" || cBg !== "\n" || cWdRaw !== "\n") { - printSituation(); - throw new Error("cCh is newline while at least one other field aren't"); - } - - if (colorLine.length !== board.w) { - printSituation(); - throw new Error("color line length error"); - } - if (bgColorLine.length !== board.w) { - printSituation(); - throw new Error("bg color line length error"); - } - if (widthLine.length !== board.w) { - printSituation(); - throw new Error("width line length error"); - } - - chLine = ""; - colorLine = ""; - bgColorLine = ""; - widthLine = ""; - lines++; - unsafeCurrentOffset += cCh.length; - - continue; - } - - if (!isValidColor(cCo) || !isValidColor(cBg)) { - printSituation(); - throw new Error("cCo or cBg is not valid"); - } - - if (!isCorrectWidth(cWdRaw, cCh)) { - printSituation(); - throw new Error("cWd is wrong"); - } - - chLine += cCh; - colorLine += cCo.padEnd(cWd); - bgColorLine += cBg.padEnd(cWd); - widthLine += cWdRaw.padEnd(cWd); - unsafeCurrentOffset += cCh.length; - } - - if (lines + 1 !== board.h) throw new Error("board height error"); -} diff --git a/tests/render.test.ts b/tests/render.test.ts new file mode 100644 index 0000000..4174094 --- /dev/null +++ b/tests/render.test.ts @@ -0,0 +1,307 @@ +import { describe, expect, it } from "vitest"; +import { render, cropRender } from "../src/logic/render"; +import { checkBoardRender } from "./checkBoardRender"; +import { applyChangeOnBoard, createBoard } from "../src/logic/board"; +import type { BoardData } from "../src/types/board"; +import type { BoardRender } from "../src/types/render"; + +const board: BoardData = createBoard({ + xSections: 1, + ySections: 1, + sectionWidth: 10, + sectionHeight: 5, + defaultCh: " ", + defaultColor: "F", + defaultBgColor: "0", + defaultWidth: 1, +}); + +// Add CJK characters (width 2) +applyChangeOnBoard({ x: 0, y: 0, ch: "你" }, board); +applyChangeOnBoard({ x: 2, y: 0, ch: "好" }, board); +applyChangeOnBoard({ x: 4, y: 0, ch: "世" }, board); +applyChangeOnBoard({ x: 6, y: 0, ch: "界" }, board); + +// Add cell with non-default foreground color +applyChangeOnBoard({ x: 0, y: 1, ch: "A" }, board); +applyChangeOnBoard({ x: 0, y: 1, color: "C" }, board); + +// Add cell with non-default background color +applyChangeOnBoard({ x: 2, y: 1, ch: "B" }, board); +applyChangeOnBoard({ x: 2, y: 1, bg_color: "A" }, board); + +// Add cell with both non-default foreground and background colors +applyChangeOnBoard({ x: 4, y: 1, ch: "C" }, board); +applyChangeOnBoard({ x: 4, y: 1, color: "9" }, board); +applyChangeOnBoard({ x: 4, y: 1, bg_color: "E" }, board); + +// Add CJK character with custom colors +applyChangeOnBoard({ x: 0, y: 2, ch: "中" }, board); +applyChangeOnBoard({ x: 0, y: 2, color: "D" }, board); +applyChangeOnBoard({ x: 0, y: 2, bg_color: "B" }, board); + +describe("render", () => { + it("render", () => { + expect(board).toBeDefined(); + + const rendered = render(board!); + checkBoardRender(rendered); + }); +}); + +describe("cropRender", () => { + // Helper to create a simple render for testing + function createTestRender(): BoardRender { + // Create a 10x5 board with: + // Row 0: "你好世界 " (4 CJK chars = 8 display cols + 2 spaces) + // Row 1: "A B C " (ASCII with spaces) + // Row 2: "中 " (1 CJK char + 8 spaces) + // Row 3: " " (all spaces) + // Row 4: " " (all spaces) + return render(board); + } + + describe("basic cropping with width-1 characters", () => { + it("crops a region containing only width-1 characters", () => { + const rendered = createTestRender(); + // Crop row 1 (ASCII chars), columns 0-5 + const cropped = cropRender(rendered, { x: 0, y: 1, width: 5, height: 1 }); + + expect(cropped.w).toBe(5); + expect(cropped.h).toBe(1); + checkBoardRender(cropped); + }); + + it("crops middle section of width-1 characters", () => { + const rendered = createTestRender(); + // Crop row 1, columns 1-4 (should get " B C") + const cropped = cropRender(rendered, { x: 1, y: 1, width: 4, height: 1 }); + + expect(cropped.w).toBe(4); + expect(cropped.h).toBe(1); + checkBoardRender(cropped); + }); + }); + + describe("wide characters (width 2)", () => { + it("includes wide character entirely within crop region", () => { + const rendered = createTestRender(); + // Crop row 0, columns 0-4 (should include "你好") + const cropped = cropRender(rendered, { x: 0, y: 0, width: 4, height: 1 }); + + expect(cropped.w).toBe(4); + expect(cropped.h).toBe(1); + expect(cropped.ch).toContain("你"); + expect(cropped.ch).toContain("好"); + checkBoardRender(cropped); + }); + + it("handles wide character at crop start boundary (character starts before region)", () => { + const rendered = createTestRender(); + // Crop starting at x=1 - "你" starts at x=0 and extends to x=2 + // The crop should include "你" with clamped width of 1 + const cropped = cropRender(rendered, { x: 1, y: 0, width: 3, height: 1 }); + + expect(cropped.w).toBe(3); + expect(cropped.h).toBe(1); + // "你" should be included but with width clamped to 1 (only 1 col visible) + expect(cropped.ch).toContain("你"); + checkBoardRender(cropped, { allowsNarrowerWidth: true }); + }); + + it("handles wide character at crop end boundary (character extends beyond region)", () => { + const rendered = createTestRender(); + // Crop ending at x=3 - "好" starts at x=2 and extends to x=4 + // The crop should include "好" with clamped width of 1 + const cropped = cropRender(rendered, { x: 0, y: 0, width: 3, height: 1 }); + + expect(cropped.w).toBe(3); + expect(cropped.h).toBe(1); + // "你" should be included with full width, "好" should be clamped + expect(cropped.ch).toContain("你"); + expect(cropped.ch).toContain("好"); + checkBoardRender(cropped, { allowsNarrowerWidth: true }); + }); + + it("handles wide character with both boundaries clamped", () => { + const rendered = createTestRender(); + // Crop from x=1 to x=2 (width 1) - only partial view of "你" + const cropped = cropRender(rendered, { x: 1, y: 0, width: 1, height: 1 }); + + expect(cropped.w).toBe(1); + expect(cropped.h).toBe(1); + checkBoardRender(cropped, { allowsNarrowerWidth: true }); + }); + + it("excludes wide character entirely outside crop region", () => { + const rendered = createTestRender(); + // Crop row 0, columns 8-10 (should only get spaces, no CJK) + const cropped = cropRender(rendered, { x: 8, y: 0, width: 2, height: 1 }); + + expect(cropped.w).toBe(2); + expect(cropped.h).toBe(1); + // Should only contain spaces + expect(cropped.ch.every((c) => c === " ")).toBe(true); + checkBoardRender(cropped); + }); + }); + + describe("multi-row cropping", () => { + it("crops multiple rows correctly", () => { + const rendered = createTestRender(); + // Crop rows 0-2, columns 0-4 + const cropped = cropRender(rendered, { x: 0, y: 0, width: 4, height: 3 }); + + expect(cropped.w).toBe(4); + expect(cropped.h).toBe(3); + checkBoardRender(cropped); + }); + + it("crops rows from middle of board", () => { + const rendered = createTestRender(); + // Crop rows 1-3, columns 2-6 + const cropped = cropRender(rendered, { x: 2, y: 1, width: 4, height: 3 }); + + expect(cropped.w).toBe(4); + expect(cropped.h).toBe(3); + checkBoardRender(cropped); + }); + + it("handles wide characters across multiple rows", () => { + const rendered = createTestRender(); + // Crop rows 0 and 2 which both have CJK characters + const cropped = cropRender(rendered, { x: 0, y: 0, width: 4, height: 3 }); + + expect(cropped.w).toBe(4); + expect(cropped.h).toBe(3); + expect(cropped.ch).toContain("你"); + expect(cropped.ch).toContain("中"); + checkBoardRender(cropped); + }); + }); + + describe("full board crop", () => { + it("returns equivalent data when cropping entire board", () => { + const rendered = createTestRender(); + const cropped = cropRender(rendered, { + x: 0, + y: 0, + width: rendered.w, + height: rendered.h, + }); + + expect(cropped.w).toBe(rendered.w); + expect(cropped.h).toBe(rendered.h); + expect(cropped.ch).toEqual(rendered.ch); + expect(cropped.color).toEqual(rendered.color); + expect(cropped.bg_color).toEqual(rendered.bg_color); + expect(cropped.width).toEqual(rendered.width); + checkBoardRender(cropped); + }); + }); + + describe("edge cases", () => { + it("handles zero-width region", () => { + const rendered = createTestRender(); + const cropped = cropRender(rendered, { x: 0, y: 0, width: 0, height: 1 }); + + expect(cropped.w).toBe(0); + expect(cropped.h).toBe(1); + expect(cropped.ch).toEqual([]); + }); + + it("handles zero-height region", () => { + const rendered = createTestRender(); + const cropped = cropRender(rendered, { x: 0, y: 0, width: 5, height: 0 }); + + expect(cropped.w).toBe(5); + expect(cropped.h).toBe(0); + expect(cropped.ch).toEqual([]); + }); + + it("handles single cell crop", () => { + const rendered = createTestRender(); + const cropped = cropRender(rendered, { x: 0, y: 1, width: 1, height: 1 }); + + expect(cropped.w).toBe(1); + expect(cropped.h).toBe(1); + expect(cropped.ch.length).toBe(1); + expect(cropped.ch[0]).toBe("A"); + checkBoardRender(cropped); + }); + + it("handles crop at bottom-right corner", () => { + const rendered = createTestRender(); + const cropped = cropRender(rendered, { x: 8, y: 4, width: 2, height: 1 }); + + expect(cropped.w).toBe(2); + expect(cropped.h).toBe(1); + checkBoardRender(cropped); + }); + + it("handles crop region starting beyond first row", () => { + const rendered = createTestRender(); + const cropped = cropRender(rendered, { x: 0, y: 3, width: 5, height: 2 }); + + expect(cropped.w).toBe(5); + expect(cropped.h).toBe(2); + // Should only contain spaces (rows 3-4 are empty) + expect(cropped.ch.every((c) => c === " ")).toBe(true); + checkBoardRender(cropped); + }); + }); + + describe("color preservation", () => { + it("preserves foreground color when cropping", () => { + const rendered = createTestRender(); + // Crop to get cell with custom foreground color (A at 0,1 with color C) + const cropped = cropRender(rendered, { x: 0, y: 1, width: 1, height: 1 }); + + expect(cropped.color[0]).toBe("C"); + checkBoardRender(cropped); + }); + + it("preserves background color when cropping", () => { + const rendered = createTestRender(); + // Crop to get cell with custom background color (B at 2,1 with bg_color A) + const cropped = cropRender(rendered, { x: 2, y: 1, width: 1, height: 1 }); + + expect(cropped.bg_color[0]).toBe("A"); + checkBoardRender(cropped); + }); + + it("preserves both colors on CJK character when cropping", () => { + const rendered = createTestRender(); + // Crop to get CJK with custom colors (中 at 0,2 with color D, bg_color B) + const cropped = cropRender(rendered, { x: 0, y: 2, width: 2, height: 1 }); + + expect(cropped.ch[0]).toBe("中"); + expect(cropped.color[0]).toBe("D"); + expect(cropped.bg_color[0]).toBe("B"); + checkBoardRender(cropped); + }); + }); + + describe("consecutive wide characters", () => { + it("handles crop in middle of consecutive wide characters", () => { + const rendered = createTestRender(); + // Row 0 has: 你(0-1)好(2-3)世(4-5)界(6-7) + // Crop from x=2 to x=6 should get 好世 + const cropped = cropRender(rendered, { x: 2, y: 0, width: 4, height: 1 }); + + expect(cropped.w).toBe(4); + expect(cropped.ch).toContain("好"); + expect(cropped.ch).toContain("世"); + checkBoardRender(cropped); + }); + + it("handles crop splitting multiple wide characters", () => { + const rendered = createTestRender(); + // Crop from x=1 to x=7 - should clip 你 at start and 界 at end + const cropped = cropRender(rendered, { x: 1, y: 0, width: 6, height: 1 }); + + expect(cropped.w).toBe(6); + checkBoardRender(cropped, { allowsNarrowerWidth: true }); + }); + }); +}); diff --git a/tests/section.test.ts b/tests/section.test.ts index 8a5a139..a9a5abe 100644 --- a/tests/section.test.ts +++ b/tests/section.test.ts @@ -1,17 +1,13 @@ -import { - assert, - assertEquals, - assertThrows, -} from "https://deno.land/std@0.224.0/assert/mod.ts"; +import { describe, it, expect } from "vitest"; -import { applyChange, createSection } from "../logic/section.ts"; -import type { SectionData } from "../types/section.ts"; +import { applyChange, createSection } from "../src/logic/section"; +import type { SectionData } from "../src/types/section"; -Deno.test("section", async (t) => { +describe("section", () => { let section: SectionData | undefined; - await t.step("createSection non-lcm", () => { - assertThrows(() => { + it("createSection non-lcm", () => { + expect(() => { createSection( { sx: 0, sy: 0 }, { @@ -25,10 +21,10 @@ Deno.test("section", async (t) => { defaultWidth: 1, }, ); - }); + }).toThrow(); }); - await t.step("createSection non-origin section", () => { + it("createSection non-origin section", () => { section = createSection( { sx: 1, sy: 1 }, { @@ -43,11 +39,11 @@ Deno.test("section", async (t) => { }, ); - assertEquals(section.offsetX, 4); - assertEquals(section.offsetY, 3); + expect(section.offsetX).toBe(4); + expect(section.offsetY).toBe(3); }); - await t.step("createSection", () => { + it("createSection", () => { section = createSection( { sx: 0, sy: 0 }, { @@ -62,8 +58,8 @@ Deno.test("section", async (t) => { }, ); - assertEquals(section.offsetX, 0); - assertEquals(section.offsetY, 0); + expect(section.offsetX).toBe(0); + expect(section.offsetY).toBe(0); function assertSectionContent( content: T[][], @@ -71,11 +67,11 @@ Deno.test("section", async (t) => { columnCount: number, value: T, ) { - assertEquals(content.length, rowCount); + expect(content.length).toBe(rowCount); for (const row of content) { - assertEquals(row.length, columnCount); + expect(row.length).toBe(columnCount); for (const item of row) { - assertEquals(item, value); + expect(item).toBe(value); } } } @@ -86,38 +82,44 @@ Deno.test("section", async (t) => { assertSectionContent(section.width, 3, 4, 1); }); - await t.step("applyChange 1-width", () => { - assert(section); + it("applyChange 1-width", () => { + expect(section).toBeDefined(); - applyChange({ x: 0, y: 0, ch: "t" }, section); - assertEquals(section.ch[0][0], "t"); - assertEquals(section.ch[0][1], " "); - assertEquals(section.width[0][0], 1); + applyChange({ x: 0, y: 0, ch: "t" }, section!); + expect(section!.ch[0]).toEqual(["t", " ", " ", " "]); + expect(section!.ch[1]).toEqual([" ", " ", " ", " "]); + expect(section!.width[0]).toEqual([1, 1, 1, 1]); }); - await t.step("applyChange 2-width at a correct position", () => { - assert(section); + it("applyChange 1-width at odd position", () => { + expect(section).toBeDefined(); - applyChange({ x: 0, y: 0, ch: "あ" }, section); - assertEquals(section.ch[0][0], "あ"); - assertEquals(section.ch[0][1], " "); - assertEquals(section.width[0][0], 2); + applyChange({ x: 1, y: 0, ch: "t" }, section!); + expect(section!.ch[0]).toEqual(["t", "t", " ", " "]); + expect(section!.width[0]).toEqual([1, 1, 1, 1]); }); - await t.step("applyChange 2-width at an alternate position", () => { - assert(section); + it("applyChange 2-width at a correct position", () => { + expect(section).toBeDefined(); - applyChange({ x: 1, y: 0, ch: "あ" }, section); - assertEquals(section.ch[0][0], "あ"); - assertEquals(section.ch[0][1], " "); - assertEquals(section.width[0][0], 2); + applyChange({ x: 0, y: 0, ch: "あ" }, section!); + expect(section!.ch[0]).toEqual(["あ", "t", " ", " "]); + expect(section!.width[0]).toEqual([2, 1, 1, 1]); }); - await t.step("applyChange incorrect section", () => { - assertThrows(() => { - assert(section); + it("applyChange 2-width at an alternate position", () => { + expect(section).toBeDefined(); - applyChange({ x: 6, y: 3, ch: "あ" }, section); - }); + applyChange({ x: 1, y: 0, ch: "あ" }, section!); + expect(section!.ch[0]).toEqual(["あ", "t", " ", " "]); + expect(section!.width[0]).toEqual([2, 1, 1, 1]); + }); + + it("applyChange incorrect section", () => { + expect(section).toBeDefined(); + + expect(() => { + applyChange({ x: 6, y: 3, ch: "あ" }, section!); + }).toThrow(); }); }); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c83df2e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,44 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + // "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "ES2022", + "target": "ES2022", + "types": [], + "moduleResolution": "bundler", + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true, + }, +}