diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9c63a68 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# https://editorconfig.org + +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 +indent_style = space +indent_size = 4 + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.forgejo/workflows/check.yml b/.forgejo/workflows/check.yml deleted file mode 100644 index 6e13d91..0000000 --- a/.forgejo/workflows/check.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: Check - -on: - push: - branches: ['main'] - pull_request: - branches: ['main'] - -env: - CARGO_TERM_COLOR: always - RUSTFLAGS: '-Dwarnings' - -jobs: - build: - runs-on: docker - - steps: - - uses: actions/checkout@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: '22.x' - check-latest: true - - - name: Cache Rust dependencies - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo- - - - name: Cache npm dependencies - uses: actions/cache@v4 - with: - path: | - reconcile-js/node_modules - examples/website/node_modules - ~/.npm - key: >- - ${{ runner.os }}-npm-${{ - hashFiles( - 'reconcile-js/package-lock.json', - 'examples/website/package-lock.json' - ) - }} - restore-keys: | - ${{ runner.os }}-npm- - - - name: Install Rust toolchain - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ - | sh -s -- -y --default-toolchain none --profile minimal - echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" - - - name: Install uv - run: | - curl --proto '=https' --tlsv1.2 -LsSf https://astral.sh/uv/install.sh | sh - echo "$HOME/.local/bin" >> "$GITHUB_PATH" - - - name: Lint - run: scripts/lint.sh - - - name: Test - run: scripts/test.sh - - - name: Build website - run: scripts/build-website.sh diff --git a/.forgejo/workflows/publish.yml b/.forgejo/workflows/publish.yml deleted file mode 100644 index d546361..0000000 --- a/.forgejo/workflows/publish.yml +++ /dev/null @@ -1,172 +0,0 @@ -name: Publish - -on: - push: - branches: ['main'] - tags: ['*'] - workflow_dispatch: - -env: - CARGO_TERM_COLOR: always - RUSTFLAGS: '-Dwarnings' - -concurrency: - group: 'pages' - cancel-in-progress: false - -jobs: - build: - runs-on: docker - - steps: - - uses: actions/checkout@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: '22.x' - check-latest: true - - - name: Cache Rust dependencies - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo- - - - name: Cache npm dependencies - uses: actions/cache@v4 - with: - path: | - reconcile-js/node_modules - examples/website/node_modules - ~/.npm - key: >- - ${{ runner.os }}-npm-${{ - hashFiles( - 'reconcile-js/package-lock.json', - 'examples/website/package-lock.json' - ) - }} - restore-keys: | - ${{ runner.os }}-npm- - - - name: Install Rust toolchain - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ - | sh -s -- -y --default-toolchain none --profile minimal - echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" - - - name: Install uv - run: | - curl --proto '=https' --tlsv1.2 -LsSf https://astral.sh/uv/install.sh | sh - echo "$HOME/.local/bin" >> "$GITHUB_PATH" - - - name: Lint - run: scripts/lint.sh - - - name: Test - run: scripts/test.sh - - - name: Build website - run: scripts/build-website.sh - - - name: Deploy to pages mount - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - run: | - apt-get update && apt-get install -y rsync - rsync -a --delete examples/website/dist/ /pages/reconcile - - publish-crate: - needs: build - runs-on: docker - if: startsWith(github.ref, 'refs/tags/') - - steps: - - uses: actions/checkout@v4 - - - name: Cache Rust dependencies - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo- - - - name: Install Rust toolchain - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ - | sh -s -- -y --default-toolchain none --profile minimal - echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" - - - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CRATES_IO_TOKEN }} - - publish-npm: - needs: build - runs-on: docker - if: startsWith(github.ref, 'refs/tags/') - - steps: - - uses: actions/checkout@v4 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: '22.x' - check-latest: true - registry-url: 'https://registry.npmjs.org' - - - name: Cache Rust dependencies - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.os }}-cargo- - - - name: Cache npm dependencies - uses: actions/cache@v4 - with: - path: | - reconcile-js/node_modules - ~/.npm - key: >- - ${{ runner.os }}-npm-${{ - hashFiles('reconcile-js/package-lock.json') - }} - restore-keys: | - ${{ runner.os }}-npm- - - - name: Install Rust toolchain - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ - | sh -s -- -y --default-toolchain none --profile minimal - echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" - - - name: Build website - run: scripts/build-website.sh - - - name: Publish reconcile-js to NPM - run: | - cd reconcile-js - cp ../README.md . - npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2b6b252 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "cargo" + directories: ["**"] + schedule: + interval: "daily" + + - package-ecosystem: "github-actions" + directories: ["**"] + schedule: + interval: "daily" diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..fbff055 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,43 @@ +name: Check + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js environment + uses: actions/setup-node@v4.2.0 + with: + node-version: "22.x" + check-latest: true + + - name: Setup rust + run: | + cargo install wasm-pack cargo-machete + + - name: Build wasm + run: | + wasm-pack build --target web --features wasm + + - name: Lint + run: | + cargo clippy --all-targets --all-features + cargo fmt --all -- --check + cargo machete + + - name: Test + run: | + cargo test --verbose -- --include-ignored + wasm-pack test --node --features wasm diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 0000000..b6eafaa --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,51 @@ +name: Deploy Website to GitHub Pages + +on: + push: + branches: + - main + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build wasm + run: | + cargo install wasm-pack + wasm-pack build --target web --features wasm + cp -R pkg/reconcile.js examples/website/ + cp -R pkg/reconcile_bg.wasm examples/website/ + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: examples/website + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 0957a69..514cff0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,12 +3,3 @@ # Rust build folder target - -# Node dependencies -node_modules - -# WebPack build output -dist - -# Python virtual environment -.venv diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index a07289d..0000000 --- a/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "trailingComma": "es5", - "printWidth": 90, - "tabWidth": 2, - "singleQuote": true, - "endOfLine": "lf" -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 4571f3c..e6c9453 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,15 +1,10 @@ { + "jest.jestCommandLine": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" npx jest", + "jest.rootPath": "plugin", "files.exclude": { - "**/snapshots": true, // cargo-insta outputs - "**/node_modules": true, // node.js dependencies - "**/dist": true, // webpack build directory - "pkg": true, // wasm-pack build directory - "target": true, // rust build directory - }, - "rust-analyzer.cargo.features": [ - "all" - ], - "python.analysis.extraPaths": [ - "./reconcile-python/python" - ] -} \ No newline at end of file + "**/dist": true, + "**/node_modules": true, + "**/.sqlx": true, + "**/snapshots": true, + } +} diff --git a/Cargo.lock b/Cargo.lock index 3253830..19871fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,95 +2,37 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "bitflags" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" - [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cc" -version = "1.2.56" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" dependencies = [ - "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-link", -] +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "console" -version = "0.15.11" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -103,284 +45,128 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" -[[package]] -name = "diff-match-patch-rs" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1e836eb966678daed8789e698dbdb1016a260c3cdaca6172585e13580c4e4be" -dependencies = [ - "chrono", - "percent-encoding", -] - [[package]] name = "encode_unicode" -version = "1.0.0" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "equivalent" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - -[[package]] -name = "getrandom" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", - "wasip3", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "indexmap" -version = "2.13.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", + "hashbrown", ] [[package]] name = "insta" -version = "1.46.3" +version = "1.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82db8c87c7f1ccecb34ce0c24399b8a73081427f3c7c50a5d597925356115e4" +checksum = "50259abbaa67d11d2bcafc7ba1d094ed7a0c70e3ce893f0d0997f73558cb3084" dependencies = [ "console", + "linked-hash-map", "once_cell", + "pin-project", "similar", - "tempfile", ] [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] -name = "leb128fmt" -version = "0.1.0" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.183" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] -name = "libm" -version = "0.2.16" +name = "linked-hash-map" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "log" -version = "0.4.29" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "minicov" -version = "0.3.8" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4869b6a491569605d66d3952bcdf03df789e5b536e5f0cf7758a7f08a55ae24d" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" dependencies = [ "cc", "walkdir", ] -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - [[package]] name = "once_cell" -version = "1.21.3" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] -name = "oorandom" -version = "11.1.5" +name = "pin-project" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] [[package]] -name = "percent-encoding" -version = "2.3.2" +name = "pin-project-internal" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "pretty_assertions" @@ -392,80 +178,43 @@ dependencies = [ "yansi", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "reconcile-text" -version = "0.11.0" +name = "reconcile" +version = "0.4.0" dependencies = [ "console_error_panic_hook", - "diff-match-patch-rs", "insta", "pretty_assertions", "serde", "serde_yaml", "test-case", - "thiserror", "wasm-bindgen", "wasm-bindgen-test", ] -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - [[package]] name = "ryu" -version = "1.0.23" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -477,54 +226,31 @@ dependencies = [ ] [[package]] -name = "semver" -version = "1.0.27" +name = "scoped-tls" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "serde" -version = "1.0.228" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -546,40 +272,21 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "similar" -version = "2.7.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - [[package]] name = "test-case" version = "3.3.1" @@ -613,37 +320,11 @@ dependencies = [ "test-case-core", ] -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unsafe-libyaml" @@ -661,45 +342,38 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", - "rustversion", "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.64" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", - "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -708,9 +382,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -718,104 +392,53 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ - "bumpalo", "proc-macro2", "quote", "syn", + "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" -dependencies = [ - "unicode-ident", -] +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-bindgen-test" -version = "0.3.64" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6311c867385cc7d5602463b31825d454d0837a3aba7cdb5e56d5201792a3f7fe" +checksum = "c61d44563646eb934577f2772656c7ad5e9c90fac78aa8013d776fcdaf24625d" dependencies = [ - "async-trait", - "cast", "js-sys", - "libm", "minicov", - "nu-ansi-term", - "num-traits", - "oorandom", - "serde", - "serde_json", + "scoped-tls", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", - "wasm-bindgen-test-shared", ] [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.64" +version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67008cdde4769831958536b0f11b3bdd0380bde882be17fff9c2f34bb4549abd" +checksum = "54171416ce73aa0b9c377b51cc3cb542becee1cd678204812e8392e5b0e4a031" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "wasm-bindgen-test-shared" -version = "0.2.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe29135b180b72b04c74aa97b2b4a2ef275161eff9a6c7955ea9eaedc7e1d4e" - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "web-sys" -version = "0.3.91" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -823,70 +446,20 @@ dependencies = [ [[package]] name = "winapi-util" -version = "0.1.11" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] -name = "windows-core" -version = "0.62.2" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", + "windows-targets", ] [[package]] @@ -898,15 +471,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -971,102 +535,8 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index a3e7c63..4adaf56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,34 +1,19 @@ [package] -name = "reconcile-text" -description = "Intelligent 3-way text merging with automated conflict resolution" -version = "0.11.0" -rust-version = "1.94" +name = "reconcile" +rust-version = "1.85" authors = ["Andras Schmelczer "] edition = "2024" license = "MIT" -readme = "README.md" repository = "https://github.com/schmelczer/reconcile" -homepage = "https://schmelczer.dev/reconcile" -keywords = ["merge", "OT", "CRDT", "3-way", "diff"] -categories = ["wasm", "text-processing", "text-editors", "algorithms", "data-structures"] -exclude = ["reconcile-js", "reconcile-python", ".*", "examples/website"] +version = "0.4.0" [lib] crate-type = ["cdylib", "rlib"] -[[example]] -name = "merge-file" -path = "examples/merge-file.rs" - -[[example]] -name = "compare-with-diff-match-patch" -path = "examples/compare-with-diff-match-patch.rs" - [dependencies] -serde = { version = "1.0.228", optional = true, features = ["derive"] } -thiserror = "2.0.18" +serde = { version = "1.0.219", optional = true, features = ["derive"] } -wasm-bindgen = { version = "0.2.114", optional = true } +wasm-bindgen = { version = "0.2.99", optional = true } # The `console_error_panic_hook` crate provides better debugging of panics by # logging them with `console.error`. This is great for development, but requires @@ -39,27 +24,22 @@ console_error_panic_hook = { version = "0.1.7", optional = true } [features] default = [] serde = [ "dep:serde" ] -wasm = [ "dep:wasm-bindgen", "console_error_panic_hook" ] +wasm = [ "dep:wasm-bindgen"] console_error_panic_hook = [ "dep:console_error_panic_hook" ] -all = [ "wasm", "serde" ] [dev-dependencies] -insta = "1.46.3" +insta = "1.42.2" pretty_assertions = "1.4.1" -serde = { version = "1.0.228", features = ["derive"] } -serde_yaml = "0.9.34" +serde = { version = "1.0.219", features = ["derive"] } +serde_yaml ="0.9.34" test-case = "3.3.1" -wasm-bindgen-test = "0.3.64" -diff-match-patch-rs = "0.5.1" +wasm-bindgen-test = "0.3.49" [profile.release] codegen-units = 1 lto = true opt-level = 3 -strip = "symbols" - -[package.metadata.wasm-pack.profile.release] -wasm-opt = ['-O4', '--enable-bulk-memory', '--enable-nontrapping-float-to-int'] +strip="debuginfo" # Keep some info for better panics [lints.rust] unsafe_code = "forbid" @@ -69,7 +49,7 @@ missing_debug_implementations = "warn" [lints.clippy] await_holding_lock = "warn" dbg_macro = "warn" -empty_enums = "warn" +empty_enum = "warn" enum_glob_use = "warn" exit = "warn" filter_map_next = "warn" @@ -96,9 +76,14 @@ verbose_file_reads = "warn" large_stack_arrays = { level = "allow", priority = 1 } # https://github.com/rust-lang/rust-clippy/issues/13774 +# TODO: fix these +cast_possible_truncation = { level = "allow", priority = 1 } +cast_sign_loss = { level = "allow", priority = 1 } +cast_possible_wrap = { level = "allow", priority = 1 } + # Silly lints implicit_return = { level = "allow", priority = 1 } -question_mark_used = { level = "allow", priority = 1 } +question_mark_used = { level = "allow", priority = 1 } struct_field_names = { level = "allow", priority = 1 } single_char_lifetime_names = { level = "allow", priority = 1 } single_call_fn = { level = "allow", priority = 1 } diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 24d84a4..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 András Schmelczer - -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: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index c25ef92..a3d3d7f 100644 --- a/README.md +++ b/README.md @@ -1,249 +1,54 @@ -# `reconcile-text`: conflict-free 3-way text merging +# VaultLink self-hosted Obsidian plugin for file syncing -A Rust, TypeScript, and Python library for merging conflicting text edits without manual intervention. Unlike traditional 3-way merge tools that produce conflict markers, `reconcile-text` automatically resolves conflicts by applying both sets of changes (while updating cursor positions) using an algorithm inspired by Operational Transformation. +[![Check](https://github.com/schmelczer/reconcile/actions/workflows/check.yml/badge.svg)](https://github.com/schmelczer/reconcile/actions/workflows/check.yml) +[![Publish to GitHub Pages](https://github.com/schmelczer/reconcile/actions/workflows/gh-pages.yml/badge.svg)](https://github.com/schmelczer/reconcile/actions/workflows/gh-pages.yml) -## Try it +## Develop -✨ **[Try the interactive demo][8]** to see it in action! +### Install [nvm](https://github.com/nvm-sh/nvm) -### Install it in your project +- `curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash` +- `nvm install 22` +- `nvm use 22` +- Optionally set the system-wide default: `nvm alias default 22` -- `cargo add reconcile-text` ([reconcile-text on crates.io][9]) -- `npm install reconcile-text` ([reconcile-text on NPM][10]) -- `uv add reconcile-text` or `pip install reconcile-text` ([reconcile-text on PyPI][27]) +### Set up Rust -## Key features +- Install [`rustup`](https://rustup.rs): `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` +- Install [`wasm-pack`](https://rustwasm.github.io/wasm-pack/installer): `curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh` +- `cargo install cargo-insta sqlx-cli cargo-edit` -- **No conflict markers** - Clean, merged output without Git's `<<<<<<<` markers -- **Cursor tracking** - Automatically repositions cursors and selections throughout the merging process -- **Flexible tokenisation** - Word-level (default), character-level, line-level, or custom tokenisation strategies -- **Unicode support** - Full UTF-8 support with proper handling of complex scripts and grapheme clusters -- **Cross-platform** - Native Rust performance with WebAssembly bindings for JavaScript and native bindings for Python - -## Quick start - -### Rust - -Install via crates.io: +### Install Obsidian on Linux ```sh -cargo add reconcile-text +apt install flatpak +flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo +flatpak install flathub md.obsidian.Obsidian +flatpak run md.obsidian.Obsidian ``` -Alternatively, add `reconcile-text` to your `Cargo.toml`: - -```toml -[dependencies] -reconcile-text = "0.8" -``` - -Then start merging: - -```rust -use reconcile_text::{reconcile, BuiltinTokenizer}; - -// Start with the original text -let parent = "Hello world"; -// Two users edit simultaneously -let left = "Hello beautiful world"; // Added "beautiful" -let right = "Hi world"; // Changed "Hello" to "Hi" - -// Reconcile combines both changes -let result = reconcile(parent, &left.into(), &right.into(), &*BuiltinTokenizer::Word); -assert_eq!(result.apply().text(), "Hi beautiful world"); -``` - -See the [merge-file example](examples/merge-file.rs) for another example, or the [library's documentation][11]. - -### JavaScript/TypeScript - -Install via NPM: - -```sh -npm install reconcile-text -``` - -Then use it in your application: - -```javascript -import { reconcile } from 'reconcile-text'; - -// Start with the original text -const parent = 'Hello world'; -// Two users edit simultaneously -const left = 'Hello beautiful world'; -const right = 'Hi world'; - -const result = reconcile(parent, left, right); -console.log(result.text); // "Hi beautiful world" -``` - -See the [example website source](examples/website/src/index.ts) for a more complex example, or the [advanced examples document](docs/advanced-ts.md). - -### Python - -Install via uv or pip: - -```sh -uv add reconcile-text -# or: pip install reconcile-text -``` - -Then use it in your application: - -```python -from reconcile_text import reconcile - -# Start with the original text -parent = "Hello world" -# Two users edit simultaneously -left = "Hello beautiful world" -right = "Hi world" - -result = reconcile(parent, left, right) -print(result["text"]) # "Hi beautiful world" -``` - -See the [merge-file example](examples/merge_file.py) for a file-merging CLI, or the [advanced examples document](docs/advanced-python.md) for cursor tracking, change provenance, and compact diffs. - -## Motivation - -Collaborative editing presents the challenge of merging conflicting changes when multiple users edit documents simultaneously or asynchronously whilst offline. Traditional solutions like Conflict-free Replicated Data Types (CRDTs) or Operational Transformation (OT) work well when you control the complete editing infrastructure and can capture every individual operation ([1]). However, many workflows involve users editing with various tools, for example, Obsidian users editing Markdown files with various editors ranging from Vim to VS Code. - -This creates **Differential Synchronisation** scenarios ([2], [3]): we only know the final state of each document, not the sequence of operations that produced it. This is the same challenge Git addresses, but Git requires manual conflict resolution. The key insight is that while incorrect merges in source code can introduce bugs, human text is more forgiving: a slightly imperfect sentence is often preferable to conflict markers interrupting the flow. - -> **Note**: Some text domains require more careful handling. Legal contracts, for instance, could have unintended meaning changes from conflicting edits that create double negations. At the same time, semantic conflicts can still arise when merging code, even in the absence of syntactic conflicts. - -Differential sync is implemented by [universal-sync][12], and it requires a merging tool that creates conflict-free results for the best user experience. - -## How it works - -`reconcile-text` starts off similarly to `diff3` ([4], [5]) but adds automated conflict resolution. Given a **parent** document and two modified versions (`left` and `right`), the following happens: - -1. **Tokenisation** - Input texts are split into meaningful units (words, characters, etc.) for granular merging -2. **Diff computation** - Myers' algorithm calculates differences between (parent ↔ left) and (parent ↔ right) -3. **Diff optimisation** - Operations are reordered and consolidated to maximise chained changes -4. **Operational Transformation** - Edits are woven together using OT principles, preserving all modifications and updating cursors - -Whilst the primary goal of `reconcile-text` isn't to implement OT, it provides an elegant way to merge Myers' diff outputs. (For a dedicated Rust OT implementation, see [operational-transform-rs][13].) The same could be achieved with CRDTs, which many libraries implement well for text (see [Loro][14], [cola][15], and [automerge][16]). - -However, when only the end result of concurrent changes is observable, merge quality depends entirely on the quality of the underlying 2-way diffs. For instance, `move` operations cannot be supported because Myers' algorithm decomposes them into separate `insert` and `delete` operations, regardless of the merging algorithm used. - -## Comparison with other approaches - -### Traditional 3-way merge (diff3, Git) - -Tools like `diff3` ([4]) and Git produce **conflict markers** (`<<<<<<<` / `=======` / `>>>>>>>`) when both sides modify the same region. This works for source code where a human must verify correctness, but breaks the reading flow for prose. `reconcile-text` uses the same diff3-like foundation but adds an OT-inspired resolution step that eliminates conflict markers entirely. Libraries like [diffy][17], [merge3][18] (Rust), and [node-diff3][19] (JavaScript) all fall into this category. - -### diff-match-patch - -[diff-match-patch][6] is a widely-used library created by Neil Fraser at Google in 2006, providing character-level diffing (Myers' algorithm), fuzzy string matching (Bitap algorithm), and patch application. It powers Fraser's **Differential Synchronisation** protocol ([2]): compute a diff between two texts, apply the patch to a third text that may have drifted, and repeat until convergence. If a patch fails, the failure self-corrects in the next sync cycle. - -The key differences from `reconcile-text`: - -- **2-way vs 3-way** - diff-match-patch diffs two texts and applies the result as a patch. It has no concept of a common ancestor and cannot reason about "left changes" vs "right changes". `reconcile-text` performs true 3-way merging, understanding the intent behind each side's edits. - -- **Character-level only** - Word-level and line-level diffs require encoding tokens as single Unicode characters before diffing ([7]). `reconcile-text` supports word, character, line, and custom tokenisation natively. - -- **Patches can fail** - `patch_apply` returns a boolean array indicating success per patch; failed patches are silently dropped. In Differential Synchronisation, failures self-correct in the next cycle, but for one-shot merges edits can be lost. `reconcile-text` always produces a complete merged result. - -- **No cursor tracking or change provenance** - diff-match-patch does not reposition cursors or track which side made which edit. `reconcile-text` does both automatically. - -See the [comparison example](examples/compare-with-diff-match-patch.rs) for concrete cases where diff-match-patch garbles adjacent edits and silently drops an entire sentence, while `reconcile-text` merges both users' changes correctly. - -> **When to use diff-match-patch instead**: when you don't have a common ancestor, for example synchronising texts that have diverged through an unknown sequence of edits. If you have a common ancestor (as in most version control and collaborative editing scenarios), `reconcile-text` produces more reliable results. - -### CRDTs (Yjs, Automerge, Loro, diamond-types) - -Conflict-free Replicated Data Types guarantee convergence by mathematical construction: every operation commutes, so the order of application doesn't matter. Libraries like [Yjs][20] (and its Rust port [Yrs][21]), [Automerge][16], [Loro][14], [cola][15], and [diamond-types][22] implement this approach. - -CRDTs capture every individual keystroke or operation, assigning each a unique identity. This makes them ideal when you control the complete editing infrastructure: the editor, the transport layer, and the storage format. They work peer-to-peer, handle arbitrary numbers of concurrent editors, and never lose an edit. - -The trade-off is that CRDTs require **maintaining document state over time** - an operation log or internal data structure that grows with the document's edit history. You cannot simply hand a CRDT library three plain strings and get a merged result. This makes them unsuitable for Differential Synchronisation scenarios where you only observe the final state of each document, which is exactly the niche `reconcile-text` fills. - -> **When to use CRDTs instead**: if you control the complete editing stack and can capture every operation as it happens, CRDTs provide stronger convergence guarantees. They also support more than two concurrent editors naturally, whereas `reconcile-text` merges exactly two forks at a time (though merges can be chained). - -### Operational Transformation (OT) - -OT libraries like [ot.js][23] and [ShareJS][24] transform concurrent operations against each other so that applying them in any order produces the same result. Like CRDTs, they capture individual operations and require infrastructure to coordinate them, typically a central server that determines the canonical operation order. - -`reconcile-text` borrows the *concept* of OT (transforming one side's edits against the other) but applies it to a different problem. Instead of transforming individual keystrokes in real time, it transforms the consolidated diff output of two complete edits. This means it doesn't need a server, doesn't need to capture operations as they happen, and works entirely offline. - -> **When to use OT instead**: if you need real-time collaboration with sub-second latency and can run a coordination server, dedicated OT libraries handle this well. `reconcile-text` is designed for merge points, not live keystroke-by-keystroke synchronisation. - -## Development - -Contributions are welcome! - -### Environment - -#### Python setup - -Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and build the extension for development: - -```sh -cd reconcile-python -uv run maturin develop -``` - -#### Node.js setup - -1. Install [nvm][25]: - ```sh - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash - ``` -2. Install and use Node 22: - ```sh - nvm install 22 && nvm use 22 - ``` -3. Optionally, set as default: - ```sh - nvm alias default 22 - ``` - -#### Rust toolchain - -Install [rustup][26]: - ```bash - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - ``` - ### Scripts -- **Run tests**: `scripts/test.sh` -- **Lint and format**: `scripts/lint.sh` -- **Develop demo website**: `scripts/dev-website.sh` -- **Build demo website**: `scripts/build-website.sh` -- **Publish new version**: `scripts/bump-version.sh patch` +#### Update HTTP API TS bindings -## License +```sh +scripts/update-api-types.sh +``` -[MIT](./LICENSE) +#### Publish new version -[1]: https://marijnhaverbeke.nl/blog/collaborative-editing-cm.html -[2]: https://neil.fraser.name/writing/sync/ -[3]: https://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf -[4]: https://blog.jcoglan.com/2017/05/08/merging-with-diff3/ -[5]: https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/35605.pdf -[6]: https://github.com/google/diff-match-patch -[7]: https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs -[8]: https://schmelczer.dev/reconcile -[9]: https://crates.io/crates/reconcile-text -[10]: https://www.npmjs.com/package/reconcile-text -[11]: https://docs.rs/reconcile-text/latest/reconcile_text -[12]: https://github.com/invisible-college/universal-sync -[13]: https://github.com/spebern/operational-transform-rs -[14]: https://github.com/loro-dev/loro/ -[15]: https://github.com/nomad/cola -[16]: https://github.com/automerge/automerge -[17]: https://crates.io/crates/diffy -[18]: https://github.com/breezy-team/merge3-rs -[19]: https://github.com/bhousel/node-diff3 -[20]: https://github.com/yjs/yjs -[21]: https://github.com/y-crdt/y-crdt -[22]: https://github.com/josephg/diamond-types -[23]: https://ot.js.org/ -[24]: https://github.com/josephg/ShareJS -[25]: https://github.com/nvm-sh/nvm -[26]: https://rustup.rs -[27]: https://pypi.org/project/reconcile-text/ +```sh +scripts/bump-version.sh patch +``` + +#### Run E2E tests + +```sh +scripts/e2e.sh +``` + +And to clean up the logs & database files, run `scripts/clean-up.sh` + +## Projects + +- [Sync server](./backend/sync_server/README.md) diff --git a/docs/advanced-python.md b/docs/advanced-python.md deleted file mode 100644 index ed2a8e9..0000000 --- a/docs/advanced-python.md +++ /dev/null @@ -1,92 +0,0 @@ -# Advanced Usage (Python) - -## Edit Provenance - -Track which changes came from where using `reconcile_with_history`: - -```python -from reconcile_text import reconcile_with_history - -result = reconcile_with_history( - "Hello world", - "Hello beautiful world", - "Hi world", -) - -print(result["text"]) # "Hi beautiful world" -print(result["history"]) # -# [ -# {"text": "Hello", "history": "RemovedFromRight"}, -# {"text": "Hi", "history": "AddedFromRight"}, -# {"text": " beautiful", "history": "AddedFromLeft"}, -# {"text": " ", "history": "Unchanged"}, -# {"text": "world", "history": "Unchanged"}, -# ] -``` - -## Tokenization Strategies - -`reconcile-text` offers different approaches to split text for merging: - -- **Word tokenizer** (`"Word"`) - Splits on word boundaries (recommended for prose) -- **Character tokenizer** (`"Character"`) - Individual characters (fine-grained control) -- **Line tokenizer** (`"Line"`) - Line-by-line (similar to `git merge` or more precisely [`git merge-file`](https://git-scm.com/docs/git-merge-file)) -- **Markdown tokenizer** (`"Markdown"`) - Splits on Markdown structural boundaries (headings, list items, paragraphs) - -```python -from reconcile_text import reconcile - -result = reconcile("abc", "axc", "abyc", "Character") -print(result["text"]) # "axyc" -``` - -## Cursor Tracking - -`reconcile-text` automatically tracks cursor positions through merges, which is useful for collaborative editors. Selections can be tracked by providing them as a pair of cursors. - -```python -from reconcile_text import reconcile - -result = reconcile( - "Hello world", - { - "text": "Hello beautiful world", - "cursors": [{"id": 1, "position": 6}], # After "Hello " - }, - { - "text": "Hi world", - "cursors": [{"id": 2, "position": 0}], # At the beginning - }, -) - -# Result: "Hi beautiful world" with repositioned cursors -print(result["text"]) # "Hi beautiful world" -print(result["cursors"]) # [{"id": 2, "position": 0}, {"id": 1, "position": 3}] -``` - -> The `cursors` list is sorted by character position (not IDs). - -## Compact Diffs - -Generate and apply compact diff representations: - -```python -from reconcile_text import diff, undiff - -original = "Hello world" -changed = "Hello beautiful world" - -# Generate a compact diff -d = diff(original, changed) -print(d) # [5, ' beautiful world'] - -# Reconstruct the changed text from the diff -reconstructed = undiff(original, d) -assert reconstructed == changed -``` - -Diff entries are positive integers (retain N characters), negative integers (delete N characters), and strings (insert text). - -## File Merging Example - -For a complete file-merging CLI (a trivial `git merge-file`), see [`examples/merge_file.py`](../examples/merge_file.py). diff --git a/docs/advanced-ts.md b/docs/advanced-ts.md deleted file mode 100644 index dd1633d..0000000 --- a/docs/advanced-ts.md +++ /dev/null @@ -1,231 +0,0 @@ -# Advanced Usage (TypeScript) - -## Edit Provenance - -Track which changes came from where using `reconcileWithHistory`. The result's -`history` field is typed as `SpanWithHistory[]`, and each span's `history` is a -`History` string-literal union. - -```typescript -import { reconcileWithHistory, type History, type SpanWithHistory } from 'reconcile-text'; - -const result = reconcileWithHistory('Hello world', 'Hello beautiful world', 'Hi world'); - -console.log(result.text); // "Hi beautiful world" - -const history: SpanWithHistory[] = result.history; -console.log(history); -// [ -// { text: "Hello", history: "RemovedFromRight" }, -// { text: "Hi", history: "AddedFromRight" }, -// { text: " beautiful", history: "AddedFromLeft" }, -// { text: " ", history: "Unchanged" }, -// { text: "world", history: "Unchanged" }, -// ] - -const classByHistory = { - Unchanged: 'merge-unchanged', - AddedFromLeft: 'merge-added-left', - AddedFromRight: 'merge-added-right', - RemovedFromLeft: 'merge-removed-left', - RemovedFromRight: 'merge-removed-right', -} satisfies Record; -``` - -Using `satisfies Record` keeps the object literal's values -narrow while forcing every history case to be handled. If a future version adds -another `History` value, TypeScript will point at this mapping. - -For control flow, use the same union as an exhaustiveness check: - -```typescript -import type { History } from 'reconcile-text'; - -function historyLabel(history: History): string { - switch (history) { - case 'Unchanged': - return 'unchanged'; - case 'AddedFromLeft': - return 'added by left'; - case 'AddedFromRight': - return 'added by right'; - case 'RemovedFromLeft': - return 'removed from left'; - case 'RemovedFromRight': - return 'removed from right'; - default: - return assertNever(history); - } -} - -function assertNever(value: never): never { - throw new Error(`Unhandled history value: ${value}`); -} -``` - -## Tokenisation Strategies - -`reconcile-text` offers different approaches to split text for merging: - -- **Word tokeniser** (`"Word"`) - Splits on word boundaries (recommended for prose) -- **Character tokeniser** (`"Character"`) - Individual characters (fine-grained control) -- **Line tokeniser** (`"Line"`) - Line-by-line (similar to `git merge` or more precisely [`git merge-file`](https://git-scm.com/docs/git-merge-file)) -- **Markdown tokeniser** (`"Markdown"`) - Splits on Markdown structural boundaries (headings, list items, paragraphs) - -```typescript -import { reconcile, type BuiltinTokenizer } from 'reconcile-text'; - -const tokenizers = [ - 'Word', - 'Character', - 'Line', - 'Markdown', -] as const satisfies readonly BuiltinTokenizer[]; - -const result = reconcile('abc', 'axc', 'abyc', 'Character'); -console.log(result.text); // "axyc" - -for (const tokenizer of tokenizers) { - const merged = reconcile( - '# Title\n\n- old item\n', - '# Title\n\n- old item\n- left item\n', - '# New title\n\n- old item\n', - tokenizer - ); - - console.log(tokenizer, merged.text); -} -``` - -## Cursor Tracking - -`reconcile-text` automatically tracks cursor positions through merges, which is -useful for collaborative editors. Selections can be tracked by providing them as -a pair of cursors. - -```typescript -import { reconcile, type TextWithOptionalCursors } from 'reconcile-text'; - -const left = { - text: 'Hello beautiful world', - cursors: [{ id: 1, position: 6 }], // After "Hello " -} satisfies TextWithOptionalCursors; - -const right = { - text: 'Hi world', - cursors: [{ id: 2, position: 0 }], // At the beginning -} satisfies TextWithOptionalCursors; - -const result = reconcile('Hello world', left, right); - -// Result: "Hi beautiful world" with repositioned cursors -console.log(result.text); // "Hi beautiful world" -console.log(result.cursors); // [{ id: 2, position: 0 }, { id: 1, position: 3 }] -``` - -> The `cursors` list is sorted by character position (not IDs). - -## Generic Helpers and Inference - -The exported merge functions are intentionally small: they merge strings, or -strings plus cursor metadata. In TypeScript applications, keep domain-specific -metadata in your own typed wrappers and let inference preserve the surrounding -shape. - -```typescript -import { reconcile, type BuiltinTokenizer } from 'reconcile-text'; - -type ReconciledText = Omit & { - text: string; -}; - -function reconcileDraft( - parent: TDraft, - left: TDraft, - right: TDraft, - tokenizer?: BuiltinTokenizer -): ReconciledText { - return { - ...right, - text: reconcile(parent.text, left.text, right.text, tokenizer).text, - }; -} - -interface MarkdownDraft { - id: string; - text: string; - updatedAt: Date; -} - -const parent: MarkdownDraft = { - id: 'intro', - text: '# Title\n\nOld text\n', - updatedAt: new Date('2026-01-01T00:00:00Z'), -}; - -const left: MarkdownDraft = { - ...parent, - text: '# Title\n\nOld text\n\n- left note\n', -}; - -const right: MarkdownDraft = { - ...parent, - text: '# New title\n\nOld text\n', -}; - -const merged = reconcileDraft(parent, left, right, 'Markdown'); -// merged is inferred as { id: string; updatedAt: Date; text: string } -``` - -Use `satisfies` for configuration objects and cursor payloads when you want -compile-time checking without widening everything to the library interface. - -```typescript -import type { BuiltinTokenizer, TextWithOptionalCursors } from 'reconcile-text'; - -const mergeOptions = { - tokenizer: 'Markdown', - renderDeletedSpans: true, -} satisfies { - tokenizer: BuiltinTokenizer; - renderDeletedSpans: boolean; -}; - -const documentWithSelection = { - text: 'Hello beautiful world', - cursors: [ - { id: 1, position: 6 }, - { id: 2, position: 15 }, - ], -} satisfies TextWithOptionalCursors; -``` - -## Compact Diffs - -Generate and apply compact diff representations. The TypeScript type is -`Array` for `diff()` and `Array` for -`undiff()`, because the underlying WebAssembly layer may represent integer -entries as `bigint`. - -```typescript -import { diff, undiff } from 'reconcile-text'; - -const original = 'Hello world'; -const changed = 'Hello beautiful world'; - -// Generate a compact diff -const changes = diff(original, changed); -console.log(changes); // [5, " beautiful world"] - -// Reconstruct the changed text from the diff -const reconstructed = undiff(original, changes); -console.assert(reconstructed === changed); -``` - -Diff entries are positive integers (retain N characters), negative integers -(delete N characters), and strings (insert text). - -## Complete Example - -For a complete browser example that renders `SpanWithHistory` values and cursor -selections, see the [example website source](../examples/website/src/index.ts). diff --git a/examples/compare-with-diff-match-patch.rs b/examples/compare-with-diff-match-patch.rs deleted file mode 100644 index dac6d6c..0000000 --- a/examples/compare-with-diff-match-patch.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::panic; - -use diff_match_patch_rs::{Compat, DiffMatchPatch, PatchInput}; -use reconcile_text::{BuiltinTokenizer, reconcile}; - -fn dmp_merge(parent: &str, left: &str, right: &str) -> Option { - let parent = parent.to_owned(); - let left = left.to_owned(); - let right = right.to_owned(); - - // diff-match-patch-rs can panic on some inputs, so we catch that. - panic::catch_unwind(|| { - let dmp = DiffMatchPatch::new(); - let diffs = dmp.diff_main::(&parent, &left).ok()?; - let patches = dmp - .patch_make(PatchInput::new_text_diffs(&parent, &diffs)) - .ok()?; - let (result, _) = dmp.patch_apply(&patches, &right).ok()?; - Some(result) - }) - .ok() - .flatten() -} - -fn try_merge(parent: &str, left: &str, right: &str) { - let dmp_result = dmp_merge(parent, left, right); - - let reconcile_result = reconcile( - parent, - &left.into(), - &right.into(), - &*BuiltinTokenizer::Word, - ) - .apply() - .text(); - - println!("Parent: {parent:?}"); - println!("Left: {left:?}"); - println!("Right: {right:?}"); - println!(); - match dmp_result { - Some(r) => println!("diff-match-patch: {r:?}"), - None => println!("diff-match-patch: "), - } - println!("reconcile-text: {reconcile_result:?}"); - println!(); -} - -/// Demonstrates cases where diff-match-patch silently produces incorrect -/// output, while reconcile-text preserves both users' edits correctly -/// -/// Run it with: -/// `cargo run --example compare-with-diff-match-patch` -fn main() { - // Example 1 - // Two users edit the same short phrase. Alice replaces "old(!)" with - // "new improved", Bob replaces "broken" with "working". These are - // independent changes to adjacent words. - // - // diff-match-patch has no common ancestor, so it diffs parent → left - // and applies the patch to right. The character-level patches overlap - // and produce garbled text ("impovind"). It reports success. - // - // reconcile-text sees both changes relative to the parent and merges - // them cleanly. - - println!("── Example 1: adjacent edits ──"); - try_merge( - "old(!) broken code", - "new improved code", - "old(!) working code", - ); - - // Example 2 - // Alice adds a sentence. Bob rewrites the surrounding text. Because - // diff-match-patch works without a common ancestor, Alice's entire - // sentence is silently lost. - - println!("── Example 2: sentence lost ──"); - // Alice adds a sentence in the middle of a paragraph. Bob rephrases - // the same paragraph. Because the patch context from Alice's edit no - // longer appears in Bob's version, diff-match-patch silently drops - // Alice's entire sentence. - // - // reconcile-text understands both edits relative to the common ancestor - // and keeps both. - try_merge( - "We used the existing parsing approach for processing. The output was saved to the \ - database.", - "We used the existing parsing approach for processing. Always validate the schema! The \ - output was saved to the database.", - "We adopted a brand new analysis pipeline for execution. The results were written to \ - cloud storage.", - ); -} diff --git a/examples/merge-file.rs b/examples/merge-file.rs deleted file mode 100644 index c94cd48..0000000 --- a/examples/merge-file.rs +++ /dev/null @@ -1,58 +0,0 @@ -use std::{env, fs, process}; - -use reconcile_text::{BuiltinTokenizer, reconcile}; - -/// Merges three versions of a file: mine, base, and theirs. -/// Implement a trivial version git merge-file () -/// -/// Run it with: -/// `cargo run --example merge-file my.txt base.txt their.txt [output_file.txt]` -fn main() { - let args: Vec = env::args().collect(); - - if args.len() < 4 || args.len() > 5 { - eprintln!("Usage: merge-file [output]"); - process::exit(1); - } - - let mine_file = &args[1]; - let base_file = &args[2]; - let theirs_file = &args[3]; - let output_file = args.get(4); - - // Read files - let mine_content = fs::read_to_string(mine_file).unwrap_or_else(|e| { - eprintln!("Error reading {mine_file}: {e}"); - process::exit(1); - }); - - let base_content = fs::read_to_string(base_file).unwrap_or_else(|e| { - eprintln!("Error reading {base_file}: {e}"); - process::exit(1); - }); - - let theirs_content = fs::read_to_string(theirs_file).unwrap_or_else(|e| { - eprintln!("Error reading {theirs_file}: {e}"); - process::exit(1); - }); - - // Perform the merge using reconcile - let result = reconcile( - &base_content, - &mine_content.into(), - &theirs_content.into(), - &*BuiltinTokenizer::Word, - ); - - let merged_content = result.apply().text(); - - // Write the result - if let Some(output_path) = output_file { - if let Err(e) = fs::write(output_path, merged_content) { - eprintln!("Error writing to {output_path}: {e}"); - process::exit(1); - } - } else { - print!("{merged_content}"); - } -} diff --git a/examples/merge_file.py b/examples/merge_file.py deleted file mode 100644 index 23e8555..0000000 --- a/examples/merge_file.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Merge three versions of a file: mine, base, and theirs. - -A trivial version of git merge-file (https://git-scm.com/docs/git-merge-file). - -Run it with: - uv run --directory reconcile-python \ - python ../examples/merge_file.py my.txt base.txt their.txt [output.txt] -""" - -from __future__ import annotations - -import sys -from pathlib import Path - -from reconcile_text import reconcile - - -def main() -> None: - args = sys.argv[1:] - - if len(args) < 3 or len(args) > 4: - print("Usage: merge_file.py [output]", file=sys.stderr) - sys.exit(1) - - mine = Path(args[0]).read_text() - base = Path(args[1]).read_text() - theirs = Path(args[2]).read_text() - - result = reconcile(base, mine, theirs) - - if len(args) == 4: - Path(args[3]).write_text(result["text"]) - else: - print(result["text"], end="") - - -if __name__ == "__main__": - main() diff --git a/examples/website/.gitignore b/examples/website/.gitignore new file mode 100644 index 0000000..8c5f1db --- /dev/null +++ b/examples/website/.gitignore @@ -0,0 +1,3 @@ +reconcile.js +reconcile_bg.wasm + diff --git a/examples/website/index.html b/examples/website/index.html new file mode 100644 index 0000000..f4e8ed6 --- /dev/null +++ b/examples/website/index.html @@ -0,0 +1,77 @@ + + + + + + + + + + + + + 3-Way Text Merge + + + +
+

3-Way Text Merge

+

Use this tool to merge three versions of a text.

+
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ + + +
+ + +
+
+ + + + + + diff --git a/examples/website/package-lock.json b/examples/website/package-lock.json deleted file mode 100644 index 3f5c201..0000000 --- a/examples/website/package-lock.json +++ /dev/null @@ -1,5902 +0,0 @@ -{ - "name": "reconcile-example-website", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "reconcile-example-website", - "license": "GPL-3.0-or-later", - "devDependencies": { - "copy-webpack-plugin": "^14.0.0", - "css-loader": "^7.1.4", - "html-webpack-plugin": "^5.6.6", - "inline-source-webpack-plugin": "^3.0.1", - "mini-css-extract-plugin": "^2.10.1", - "prettier": "^3.8.1", - "reconcile-text": "file:../../reconcile-js", - "resolve-url-loader": "^5.0.0", - "sass": "^1.98.0", - "sass-loader": "^16.0.7", - "svg-inline-loader": "^0.8.2", - "terser-webpack-plugin": "^5.4.0", - "ts-loader": "^9.5.4", - "typescript": "^5.9.3", - "webpack": "^5.105.4", - "webpack-cli": "^6.0.1", - "webpack-dev-server": "^5.2.3" - } - }, - "../../reconcile-js": { - "name": "reconcile-text", - "version": "0.11.0", - "dev": true, - "license": "MIT", - "devDependencies": { - "@types/jest": "^30.0.0", - "jest": "^30.3.0", - "prettier": "^3.8.1", - "reconcile-text": "file:../pkg", - "ts-jest": "^29.4.6", - "ts-loader": "^9.5.4", - "tslib": "2.8.1", - "typescript": "5.9.3", - "webpack": "^5.105.4", - "webpack-cli": "^6.0.1", - "webpack-merge": "^6.0.1" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", - "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.17.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz", - "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@jsonjoy.com/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/json-pack": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.2.0.tgz", - "integrity": "sha512-io1zEbbYcElht3tdlqEOFxZ0dMTYrHz9iMf0gqn1pPjZFTCgM5R4R5IMA20Chb2UPYYsxjzs8CgZ7Nb5n2K2rA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/base64": "^1.1.1", - "@jsonjoy.com/util": "^1.1.2", - "hyperdyperid": "^1.2.0", - "thingies": "^1.20.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/util": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.6.0.tgz", - "integrity": "sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@parcel/watcher": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", - "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^1.0.3", - "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.1", - "@parcel/watcher-darwin-arm64": "2.5.1", - "@parcel/watcher-darwin-x64": "2.5.1", - "@parcel/watcher-freebsd-x64": "2.5.1", - "@parcel/watcher-linux-arm-glibc": "2.5.1", - "@parcel/watcher-linux-arm-musl": "2.5.1", - "@parcel/watcher-linux-arm64-glibc": "2.5.1", - "@parcel/watcher-linux-arm64-musl": "2.5.1", - "@parcel/watcher-linux-x64-glibc": "2.5.1", - "@parcel/watcher-linux-x64-musl": "2.5.1", - "@parcel/watcher-win32-arm64": "2.5.1", - "@parcel/watcher-win32-ia32": "2.5.1", - "@parcel/watcher-win32-x64": "2.5.1" - } - }, - "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", - "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", - "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", - "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", - "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", - "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", - "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", - "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", - "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", - "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", - "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", - "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", - "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", - "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@peculiar/asn1-cms": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz", - "integrity": "sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "@peculiar/asn1-x509-attr": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-csr": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.6.1.tgz", - "integrity": "sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-ecc": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.6.1.tgz", - "integrity": "sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-pfx": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.6.1.tgz", - "integrity": "sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.6.1", - "@peculiar/asn1-pkcs8": "^2.6.1", - "@peculiar/asn1-rsa": "^2.6.1", - "@peculiar/asn1-schema": "^2.6.0", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-pkcs8": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.1.tgz", - "integrity": "sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-pkcs9": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.1.tgz", - "integrity": "sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.6.1", - "@peculiar/asn1-pfx": "^2.6.1", - "@peculiar/asn1-pkcs8": "^2.6.1", - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "@peculiar/asn1-x509-attr": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-rsa": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.6.1.tgz", - "integrity": "sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-schema": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz", - "integrity": "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "asn1js": "^3.0.6", - "pvtsutils": "^1.3.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-x509": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.6.1.tgz", - "integrity": "sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "asn1js": "^3.0.6", - "pvtsutils": "^1.3.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-x509-attr": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.1.tgz", - "integrity": "sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "asn1js": "^3.0.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/x509": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", - "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.6.0", - "@peculiar/asn1-csr": "^2.6.0", - "@peculiar/asn1-ecc": "^2.6.0", - "@peculiar/asn1-pkcs9": "^2.6.0", - "@peculiar/asn1-rsa": "^2.6.0", - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.0", - "pvtsutils": "^1.3.6", - "reflect-metadata": "^0.2.2", - "tslib": "^2.8.1", - "tsyringe": "^4.10.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "4.17.25", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", - "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "^1" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/express/node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/http-proxy": { - "version": "1.17.16", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz", - "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz", - "integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.8.0" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/retry": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", - "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", - "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", - "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", - "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "license": "Apache-2.0", - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/asn1js": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.7.tgz", - "integrity": "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "pvtsutils": "^1.3.6", - "pvutils": "^1.1.3", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", - "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "dev": true, - "license": "MIT" - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.14.0", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/bonjour-service": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", - "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true, - "license": "ISC" - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/bundle-name": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "run-applescript": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bytestreamjs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", - "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001777", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz", - "integrity": "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.1.0", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "dev": true, - "license": "MIT" - }, - "node_modules/copy-webpack-plugin": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-14.0.0.tgz", - "integrity": "sha512-3JLW90aBGeaTLpM7mYQKpnVdgsUZRExY55giiZgLuX/xTQRUs1dOCwbBnWnvY6Q6rfZoXMNwzOQJCSZPppfqXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-parent": "^6.0.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.2.0", - "serialize-javascript": "^7.0.3", - "tinyglobby": "^0.2.12" - }, - "engines": { - "node": ">= 20.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-loader": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.4.tgz", - "integrity": "sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.40", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.6.3" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", - "webpack": "^5.27.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-select/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/css-select/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/css-tree": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/default-browser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", - "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "bundle-name": "^4.1.0", - "default-browser-id": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", - "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true, - "license": "Apache-2.0", - "optional": true, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, - "license": "MIT" - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "license": "MIT", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.307", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz", - "integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==", - "dev": true, - "license": "ISC" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", - "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/envinfo": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", - "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", - "dev": true, - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true, - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.3", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.14.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true, - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.6.6", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.6.tgz", - "integrity": "sha512-bLjW01UTrvoWTJQL5LsMRo1SypHW80FTm12OJRSnr3v6YHNhfe+1r0MYUZJMACxnCHURVnBWRwAsWs2yPU9Ezw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", - "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", - "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/hyperdyperid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", - "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.18" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/immutable": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", - "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", - "dev": true, - "license": "MIT" - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/inline-source": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/inline-source/-/inline-source-8.0.3.tgz", - "integrity": "sha512-2k0V+qA8buiBSfchYGg6KgugU/YFboGRZyRWQS0AC1wPBIwehi63KVTKLuk7grnqtneafktIxMUn/nhCDCkW5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "csso": "^5.0.5", - "htmlparser2": "^9.0.0", - "node-fetch": "^3.3.2", - "svgo": "^3.0.0", - "terser": "^5.24.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/inline-source-webpack-plugin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/inline-source-webpack-plugin/-/inline-source-webpack-plugin-3.0.1.tgz", - "integrity": "sha512-pCsgDHaVK5kk+GbRNdyT+nkXBihDNyG0oDqu7M1oR0hhr6DFjGtLx2UYC/moS/ibJ7QBMias6E4CqQcHGXsf5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "htmlparser2": "^8.0.1", - "inline-source": "^8.0.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/inline-source/node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" - } - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-network-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", - "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/launch-editor": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.10.0.tgz", - "integrity": "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" - } - }, - "node_modules/loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "dev": true, - "license": "MIT" - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.2.tgz", - "integrity": "sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/json-pack": "^1.0.3", - "@jsonjoy.com/util": "^1.3.0", - "tree-dump": "^1.0.1", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">= 4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.10.1.tgz", - "integrity": "sha512-k7G3Y5QOegl380tXmZ68foBRRjE9Ljavx835ObdvmZjQ639izvZD8CS7BkWw1qKPPzHsGL/JDhl0uyU1zc2rJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true, - "license": "ISC" - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "dev": true, - "license": "MIT", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, - "license": "MIT", - "optional": true - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true, - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/open": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", - "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-retry": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", - "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/retry": "0.12.2", - "is-network-error": "^1.0.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkijs": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.3.3.tgz", - "integrity": "sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@noble/hashes": "1.4.0", - "asn1js": "^3.0.6", - "bytestreamjs": "^2.0.1", - "pvtsutils": "^1.3.6", - "pvutils": "^1.1.3", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", - "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", - "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", - "dev": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pvtsutils": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", - "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.8.1" - } - }, - "node_modules/pvutils": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", - "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/reconcile-text": { - "resolved": "../../reconcile-js", - "link": true - }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/regex-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.1.tgz", - "integrity": "sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/renderkid/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-url-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", - "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.14", - "source-map": "0.6.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/run-applescript": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", - "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/sass": { - "version": "1.98.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.98.0.tgz", - "integrity": "sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^4.0.0", - "immutable": "^5.1.5", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" - }, - "optionalDependencies": { - "@parcel/watcher": "^2.4.1" - } - }, - "node_modules/sass-loader": { - "version": "16.0.7", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.7.tgz", - "integrity": "sha512-w6q+fRHourZ+e+xA1kcsF27iGM6jdB8teexYCfdUw0sYgcDNeZESnDNT9sUmmPm3ooziwUJXGwZJSTF3kOdBfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/sax": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz", - "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "dev": true, - "license": "MIT" - }, - "node_modules/selfsigned": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz", - "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@peculiar/x509": "^1.14.2", - "pkijs": "^3.3.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/serialize-javascript": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.4.tgz", - "integrity": "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true, - "license": "ISC" - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true, - "license": "ISC" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/simple-html-tokenizer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/simple-html-tokenizer/-/simple-html-tokenizer-0.1.1.tgz", - "integrity": "sha512-Mc/gH3RvlKvB/gkp9XwgDKEWrSYyefIJPGG8Jk1suZms/rISdUuVEMx5O1WBnTWaScvxXDvGJrZQWblUmQHjkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/spdy-transport/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/spdy-transport/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/spdy/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/spdy/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-inline-loader": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/svg-inline-loader/-/svg-inline-loader-0.8.2.tgz", - "integrity": "sha512-kbrcEh5n5JkypaSC152eGfGcnT4lkR0eSfvefaUJkLqgGjRQJyKDvvEE/CCv5aTSdfXuc+N98w16iAojhShI3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "loader-utils": "^1.1.0", - "object-assign": "^4.0.1", - "simple-html-tokenizer": "^0.1.1" - } - }, - "node_modules/svg-inline-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/svg-inline-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/svgo": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.3.tgz", - "integrity": "sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^7.2.0", - "css-select": "^5.1.0", - "css-tree": "^2.3.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.0.0", - "sax": "^1.5.0" - }, - "bin": { - "svgo": "bin/svgo" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/svgo/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/svgo/node_modules/css-select": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", - "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/svgo/node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/svgo/node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser": { - "version": "5.43.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", - "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", - "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/thingies": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", - "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", - "dev": true, - "license": "Unlicense", - "engines": { - "node": ">=10.18" - }, - "peerDependencies": { - "tslib": "^2" - } - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tree-dump": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.3.tgz", - "integrity": "sha512-il+Cv80yVHFBwokQSfd4bldvr1Md951DpgAGfmhydt04L+YzHgubm2tQ7zueWDcGENKHq0ZvGFR/hjvNXilHEg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/ts-loader": { - "version": "9.5.4", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.4.tgz", - "integrity": "sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "typescript": "*", - "webpack": "^5.0.0" - } - }, - "node_modules/ts-loader/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, - "node_modules/tsyringe": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", - "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^1.9.3" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/tsyringe/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "dev": true, - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/webpack": { - "version": "5.105.4", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", - "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.16.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.20.0", - "es-module-lexer": "^2.0.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.3.1", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.17", - "watchpack": "^2.5.1", - "webpack-sources": "^3.3.4" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", - "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@discoveryjs/json-ext": "^0.6.1", - "@webpack-cli/configtest": "^3.0.1", - "@webpack-cli/info": "^3.0.1", - "@webpack-cli/serve": "^3.0.1", - "colorette": "^2.0.14", - "commander": "^12.1.0", - "cross-spawn": "^7.0.3", - "envinfo": "^7.14.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^6.0.1" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.82.0" - }, - "peerDependenciesMeta": { - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/webpack-dev-middleware": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", - "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^4.6.0", - "mime-types": "^2.1.31", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.3.tgz", - "integrity": "sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/bonjour": "^3.5.13", - "@types/connect-history-api-fallback": "^1.5.4", - "@types/express": "^4.17.25", - "@types/express-serve-static-core": "^4.17.21", - "@types/serve-index": "^1.9.4", - "@types/serve-static": "^1.15.5", - "@types/sockjs": "^0.3.36", - "@types/ws": "^8.5.10", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.2.1", - "chokidar": "^3.6.0", - "colorette": "^2.0.10", - "compression": "^1.8.1", - "connect-history-api-fallback": "^2.0.0", - "express": "^4.22.1", - "graceful-fs": "^4.2.6", - "http-proxy-middleware": "^2.0.9", - "ipaddr.js": "^2.1.0", - "launch-editor": "^2.6.1", - "open": "^10.0.3", - "p-retry": "^6.2.0", - "schema-utils": "^4.2.0", - "selfsigned": "^5.5.0", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^7.4.2", - "ws": "^8.18.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/webpack-dev-server/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/webpack-dev-server/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/webpack-merge": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", - "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", - "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - } - } -} diff --git a/examples/website/package.json b/examples/website/package.json deleted file mode 100644 index 9a16f32..0000000 --- a/examples/website/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "reconcile-example-website", - "description": "", - "private": true, - "scripts": { - "start": "webpack serve --open --mode development", - "format": "prettier --write \"./**/*.(ts|scss|json|html)\"", - "build": "webpack --mode production" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/schmelczer/reconcile.git" - }, - "keywords": [], - "author": "Andras Schmelczer", - "license": "GPL-3.0-or-later", - "bugs": { - "url": "https://github.com/schmelczer/reconcile/issues" - }, - "browserslist": [ - "defaults" - ], - "homepage": "https://github.com/schmelczer/reconcile#readme", - "devDependencies": { - "copy-webpack-plugin": "^14.0.0", - "css-loader": "^7.1.4", - "html-webpack-plugin": "^5.6.6", - "inline-source-webpack-plugin": "^3.0.1", - "mini-css-extract-plugin": "^2.10.1", - "prettier": "^3.8.1", - "reconcile-text": "file:../../reconcile-js", - "resolve-url-loader": "^5.0.0", - "sass": "^1.98.0", - "sass-loader": "^16.0.7", - "svg-inline-loader": "^0.8.2", - "terser-webpack-plugin": "^5.4.0", - "ts-loader": "^9.5.4", - "typescript": "^5.9.3", - "webpack": "^5.105.4", - "webpack-cli": "^6.0.1", - "webpack-dev-server": "^5.2.3" - } -} diff --git a/examples/website/script.js b/examples/website/script.js new file mode 100644 index 0000000..4dcbaf9 --- /dev/null +++ b/examples/website/script.js @@ -0,0 +1,41 @@ +import init, { mergeText } from "./reconcile.js"; + +const originalTextArea = document.getElementById("original"); +const leftTextArea = document.getElementById("left"); +const rightTextArea = document.getElementById("right"); +const mergedTextArea = document.getElementById("merged"); +const mergeButton = document.getElementById("merge-button"); + +const sampleTexts = [ + "The quick brown fox jumps over the lazy dog.", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.", + "A journey of a thousand miles begins with a single step.", + "To be, or not to be, that is the question.", +]; + +async function run() { + await init(); + + mergeButton.addEventListener("click", () => { + const original = originalTextArea.value; + const left = leftTextArea.value; + const right = rightTextArea.value; + + const result = mergeText(original, left, right); + mergedTextArea.value = result; + }); + + loadSample(); +} + +function loadSample() { + const randomIndex = Math.floor(Math.random() * sampleTexts.length); + const text = sampleTexts[randomIndex]; + originalTextArea.value = text; + leftTextArea.value = text; + rightTextArea.value = text; + mergedTextArea.value = ""; +} + +run(); diff --git a/examples/website/src/favicon.ico b/examples/website/src/favicon.ico deleted file mode 100644 index b345eb5..0000000 Binary files a/examples/website/src/favicon.ico and /dev/null differ diff --git a/examples/website/src/index.html b/examples/website/src/index.html deleted file mode 100644 index 4519472..0000000 --- a/examples/website/src/index.html +++ /dev/null @@ -1,279 +0,0 @@ - - - - - - - - - - - - - - - - reconcile-text: conflict-free 3-way text merging - - - - -
- -
-
-
-

reconcile-text: conflict-free 3-way text merging

-

- Think - diff3 - (or more specifically, git merge), but with intelligent conflict - resolution that requires no user intervention. The - reconcile-text - library tackles a fundamental challenge in collaborative editing: what happens - when multiple users edit the same text simultaneously, but the conflict - resolver only has access to the final results, not the intermediate steps? -

- -

- Where traditional merge tools leave you with conflict markers to resolve - manually, reconcile-text automatically weaves changes together. - The reconcile(parent, left, right) function takes conflicting - edits and produces clean, unified results using an algorithm inspired by - Operational Transformation. No more - <<<<<<< markers cluttering your text. -

- -

- The process starts with your chosen tokenisation strategy, then applies Myers' - 2-way diff algorithm to compare the original with both modified versions. - These diffs are optimised and transformed to preserve the longest meaningful - changes, before a final merge strategy combines all insertions and deletions - without losing any edits. Cursor positions can be tracked and updated during - merging as well. -

- -

- Ready to dive deeper? Check out the - documentation - or try editing the text boxes below to see reconcile-text in - action. Use the tokenisation options to experiment with different approaches - - the Rust library also supports custom tokenisers. -

-
- -
-
-
- - - -
-
- -
- - -
- -
- - -
- -
- - -
- -
- -
-
-
- - -
-
- - - - - - diff --git a/examples/website/src/index.ts b/examples/website/src/index.ts deleted file mode 100644 index 232e843..0000000 --- a/examples/website/src/index.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { reconcileWithHistory } from 'reconcile-text'; -import type { BuiltinTokenizer } from 'reconcile-text'; -import './style.scss'; - -const originalTextArea = document.getElementById('original') as HTMLTextAreaElement; -const leftTextArea = document.getElementById('left') as HTMLTextAreaElement; -const rightTextArea = document.getElementById('right') as HTMLTextAreaElement; -const mergedTextArea = document.getElementById('merged') as HTMLDivElement; -const tokenizerRadios = document.querySelectorAll( - 'input[name="tokenizer"]' -) as NodeListOf; - -const sampleText = `The reconcile-text library is embedded on this page as a WASM module and powers these text boxes. Experiment with changing the "Original", "First user's edit", and "Second user's edit" text boxes to see competing changes get merged in real-time within the "Merged result" box. - -Here, you will see color-coded tokens marking the origin of each token, including ones that got deleted. The result highly depends on the tokenisation strategy which may be: -- Character-based -- Word-based`; - -let pendingUpdate: number | null = null; -function scheduleUpdate(): void { - if (pendingUpdate === null) { - pendingUpdate = requestAnimationFrame(() => { - pendingUpdate = null; - updateMergedText(); - }); - } -} - -async function main(): Promise { - originalTextArea.addEventListener('input', scheduleUpdate); - leftTextArea.addEventListener('input', scheduleUpdate); - rightTextArea.addEventListener('input', scheduleUpdate); - - document.addEventListener('selectionchange', () => { - if ( - document.activeElement === leftTextArea || - document.activeElement === rightTextArea - ) { - scheduleUpdate(); - } - }); - - window.addEventListener('resize', resizeTextAreas); - - tokenizerRadios.forEach((radio) => { - radio.addEventListener('change', scheduleUpdate); - }); - - loadSample(); - updateMergedText(); -} - -// Edit the instructions to generate example edits -function loadSample(): void { - originalTextArea.value = sampleText; - leftTextArea.value = - sampleText.replace('color', 'colour') + - "\n- Line-based\n\nCheck out what's the most complex conflict you can come up with!"; - rightTextArea.value = - sampleText.replace(', for example,', ' such as').replace('WASM', 'WebAssembly') + - '\n- Or your custom tokeniser'; -} - -function updateMergedText(): void { - resizeTextAreas(); - - const original = originalTextArea.value; - const left = leftTextArea.value; - const right = rightTextArea.value; - - const selectedTokenizer = getSelectedTokenizer(); - - const { leftCursors, rightCursors } = getCursorsFromActiveTextArea(); - - const results = reconcileWithHistory( - original, - { - text: left, - cursors: leftCursors, - }, - { - text: right, - cursors: rightCursors, - }, - selectedTokenizer - ); - - let selectionStart: number = Number.NEGATIVE_INFINITY; - let selectionEnd: number = Number.NEGATIVE_INFINITY; - if ((results.cursors?.length ?? 0) > 0) { - selectionStart = results.cursors![0].position; - selectionEnd = results.cursors![1].position; - } - const isSelection = selectionStart !== selectionEnd; - - const selectionSide = leftCursors ? 'left' : 'right'; - const fragment = document.createDocumentFragment(); - - let currentPosition = 0; - if (selectionEnd === 0) { - fragment.appendChild(createSelectionOverlay(selectionSide === 'left', isSelection)); - } - - for (const { text, history } of results.history) { - const isDelete = history === 'RemovedFromLeft' || history === 'RemovedFromRight'; - let spanChars: string[] = []; - let currentClass = ''; - - const flushSpan = () => { - if (spanChars.length > 0) { - const span = document.createElement('span'); - span.className = currentClass; - span.textContent = spanChars.join(''); - fragment.appendChild(span); - spanChars = []; - } - }; - - for (const character of text) { - let className = history; - if ( - !isDelete && - selectionStart <= currentPosition && - currentPosition < selectionEnd - ) { - className += ` selection-${selectionSide}`; - } - - if (className !== currentClass) { - flushSpan(); - currentClass = className; - } - spanChars.push(character); - - if (!isDelete) { - if (currentPosition === selectionEnd - 1) { - flushSpan(); - fragment.appendChild( - createSelectionOverlay(selectionSide === 'left', isSelection) - ); - } - currentPosition++; - } - } - - flushSpan(); - } - - mergedTextArea.innerHTML = ''; - mergedTextArea.appendChild(fragment); -} - -function getCursorsFromActiveTextArea() { - const activeElement = document.activeElement; - let leftCursors = undefined; - let rightCursors = undefined; - - if (activeElement === leftTextArea) { - leftCursors = [ - { id: 1, position: leftTextArea.selectionStart }, - { id: 2, position: leftTextArea.selectionEnd }, - ]; - } else if (activeElement === rightTextArea) { - rightCursors = [ - { id: 1, position: rightTextArea.selectionStart }, - { id: 2, position: rightTextArea.selectionEnd }, - ]; - } - return { leftCursors, rightCursors }; -} - -function createSelectionOverlay(isLeft: boolean, isSelection: boolean): HTMLSpanElement { - const caretSpan = document.createElement('span'); - caretSpan.className = `selection-caret selection-caret-${isLeft ? 'left' : 'right'}`; - - const stickDiv = document.createElement('div'); - stickDiv.className = 'stick'; - caretSpan.appendChild(stickDiv); - - const dotDiv = document.createElement('div'); - dotDiv.className = 'dot'; - caretSpan.appendChild(dotDiv); - - const infoDiv = document.createElement('div'); - infoDiv.className = 'info'; - const selectionType = isSelection ? 'selection' : 'cursor'; - infoDiv.textContent = isLeft - ? `Left user's ${selectionType}` - : `Right user's ${selectionType}`; - caretSpan.appendChild(infoDiv); - - return caretSpan; -} - -function getSelectedTokenizer(): BuiltinTokenizer { - const selectedRadio = Array.from(tokenizerRadios).find((radio) => radio.checked); - return (selectedRadio?.value ?? 'Markdown') as BuiltinTokenizer; -} - -function resizeTextAreas(): void { - // Only auto-resize if field-sizing CSS property is not supported, like in Safari as of now - if (!CSS.supports('field-sizing', 'content')) { - autoResize(originalTextArea); - autoResize(leftTextArea); - autoResize(rightTextArea); - } -} - -function autoResize(textarea: HTMLTextAreaElement): void { - textarea.style.height = 'auto'; - textarea.style.height = textarea.scrollHeight + 'px'; -} - -main().catch((error) => { - document.body.textContent = - 'Failed to load the application. Please ensure your browser supports WebAssembly.'; - console.error(error); -}); diff --git a/examples/website/src/og-image.png b/examples/website/src/og-image.png deleted file mode 100644 index f13a1dc..0000000 Binary files a/examples/website/src/og-image.png and /dev/null differ diff --git a/examples/website/src/style.scss b/examples/website/src/style.scss deleted file mode 100644 index 7ef66d2..0000000 --- a/examples/website/src/style.scss +++ /dev/null @@ -1,507 +0,0 @@ -@use 'sass:color'; - -// Colour palette -$primary-blue: #2451a6; -$light-blue: #85bff7; -$green: #12d197; -$text-primary: #23272f; -$text-secondary: #5a6272; -$border-grey: #d1d5db; -$code-bg: #61769a; -$code-text: #e2e8f0; -$white: #fff; -$light-bg: #f8fafc; -$gradient-end: #e0e7ef; - -// Function to create selection colour with opacity -@function selection-colour($colour, $opacity: 0.3) { - @return rgba($colour, $opacity); -} - -@function caret-colour($colour, $amount: 20%) { - @return color.adjust($colour, $lightness: -$amount); -} - -* { - box-sizing: border-box; - margin: 0; - user-select: none; -} - -html, -body { - height: 100%; -} - -body { - font-family: 'Segoe UI', Arial, sans-serif; - color: $text-primary; -} - -.scroll-container { - height: 100vh; - height: 100dvh; - overflow-y: auto; - overflow-x: hidden; -} - -.background { - background: linear-gradient(135deg, $light-bg 0%, $gradient-end 100%); - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - z-index: -1; -} - -.page-wrapper { - display: flex; - flex-direction: column; - justify-content: space-between; - min-height: 100%; - max-width: 1000px; - margin: 0 auto; -} - -header { - padding: 32px 32px 0 32px; -} - -header > h1 { - font-size: 2.5rem; - font-weight: 700; - color: $primary-blue; - margin-bottom: 24px; - text-align: center; -} - -h1, -code, -p, -p * { - user-select: text; -} - -code { - background: $code-bg; - color: $code-text; - padding: 2px 6px; - border-radius: 4px; - font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Consolas', monospace; - font-size: 0.875em; - font-weight: 500; -} - -header > p { - color: $text-secondary; - font-size: 1.1rem; - margin-bottom: 0; -} - -header > p:not(:first-of-type) { - margin-top: 16px; -} - -main { - display: grid; - grid-template-rows: min-content min-content min-content; - grid-template-columns: 1fr 1fr; - gap: 20px; - justify-items: center; - align-items: center; - padding: 32px; -} - -.tokenizer-selector { - grid-column: 1 / -1; - grid-row: 1; - width: 100%; - margin-bottom: 8px; -} - -.radio-group { - display: flex; - gap: 16px; - justify-content: center; - flex-wrap: wrap; -} - -.radio-option { - display: flex; - align-items: center; - gap: 12px; - padding: 16px 20px; - background: $white; - border-radius: 12px; - box-shadow: 0 2px 8px selection-colour($primary-blue, 0.08); - cursor: pointer; - transition: all 0.2s ease; - border: 2px solid transparent; - min-width: 180px; - position: relative; -} - -.radio-option:hover { - box-shadow: 0 4px 16px selection-colour($primary-blue, 0.12); - transform: translateY(-2px); -} - -.radio-option:has(input:checked) { - background: $gradient-end; - border-color: $primary-blue; - box-shadow: 0 4px 16px selection-colour($primary-blue, 0.16); -} - -.radio-option input[type='radio'] { - position: absolute; - opacity: 0; - pointer-events: none; -} - -.radio-custom { - width: 20px; - height: 20px; - border: 2px solid $border-grey; - border-radius: 50%; - position: relative; - transition: all 0.2s ease; - flex-shrink: 0; -} - -.radio-option:has(input:checked) .radio-custom { - border-color: $primary-blue; - background: $primary-blue; -} - -.radio-custom::after { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%) scale(0); - width: 8px; - height: 8px; - border-radius: 50%; - background: white; - transition: transform 0.2s ease; -} - -.radio-option:has(input:checked) .radio-custom::after { - transform: translate(-50%, -50%) scale(1); -} - -.radio-content { - display: flex; - flex-direction: column; - gap: 2px; -} - -.radio-label { - font-weight: 600; - color: $primary-blue; - font-size: 0.95rem; -} - -.radio-description { - font-size: 0.8rem; - color: $text-primary; - line-height: 1.2; -} - -.diamond-parent { - grid-column: 1 / -1; - grid-row: 2; -} - -.diamond-left { - grid-column: 1; - grid-row: 3; -} - -.diamond-right { - grid-column: 2; - grid-row: 3; -} - -.diamond-result { - grid-column: 1 / -1; - grid-row: 4; -} - -.diamond-result label { - display: flex; - align-items: center; -} - -.diamond-result svg { - width: 20px; - height: 20px; - margin-left: 8px; -} - -.text-area-card { - width: 100%; - height: 100%; - background: $white; - border-radius: 10px; - box-shadow: 0 2px 12px 0 selection-colour($primary-blue, 0.06); - padding: 18px 20px 16px 20px; - margin-bottom: 0; -} - -label { - display: inline-block; - margin-bottom: 8px; - font-weight: 600; - color: $primary-blue; - cursor: help; -} - -.box { - width: 1ch; - height: 1ch; - border-radius: 50%; - margin-left: 6px; - display: inline-block; - transform: scale(1.5); -} - -textarea { - width: 100%; - border: none; - font-size: 1rem; - font-family: inherit; - color: $text-primary; - box-sizing: border-box; - resize: none; - outline: none; - margin-bottom: 0; - field-sizing: content; /* Doesn't work in Safari yet */ -} - -#merged { - width: 100%; - user-select: text; - - > * { - position: relative; - white-space: pre-wrap; - } -} - -.Unchanged { - user-select: text; -} - -.Left, -.AddedFromLeft, -.RemovedFromLeft { - user-select: text; - background: $green; -} - -.selection-left::after, -.selection-right::after { - content: ''; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} - -.selection-left::after { - background: selection-colour($green); -} - -.selection-right::after { - background: selection-colour($light-blue); -} - -.Right, -.AddedFromRight, -.RemovedFromRight { - user-select: text; - background: $light-blue; -} - -.RemovedFromLeft, -.RemovedFromRight { - user-select: none; - text-decoration: line-through; -} - -// Selection caret styles -$CARET_WIDTH: 2; -$DOT_RADIUS: 4; - -.selection-caret { - position: relative; - z-index: 1000; - - &.selection-caret-left { - background: caret-colour($green); - } - - &.selection-caret-right { - background: caret-colour($light-blue); - } - - > * { - position: absolute; - background-color: inherit; - } - - > .stick { - left: 0; - top: 0; - transform: translateX(-50%); - width: #{$CARET_WIDTH}px; - height: 100%; - display: block; - border-radius: calc(#{$CARET_WIDTH} / 2 * 1px); - animation: blink-stick 1s steps(1) infinite; - } - - > .dot { - border-radius: 50%; - width: #{$DOT_RADIUS * 2}px; - height: #{$DOT_RADIUS * 2}px; - top: -#{$DOT_RADIUS}px; - left: -#{$DOT_RADIUS}px; - transition: opacity 0.3s ease-in-out; - transform-origin: bottom center; - box-sizing: border-box; - - &::before { - content: ''; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 30px; - height: 30px; - border-radius: 50%; - } - } - - &:hover > .dot { - opacity: 0; - } - - > .info { - top: -1.3em; - left: calc(-#{$CARET_WIDTH} / 2 * 1px); - font-size: 0.9em; - user-select: none; - color: white; - padding: 0 2px; - transition: opacity 0.3s ease-in-out; - opacity: 0; - white-space: nowrap; - border-radius: 3px 3px 3px 0; - } - - &:hover > .info { - opacity: 1; - } -} - -@keyframes blink-stick { - 0%, - 100% { - opacity: 1; - } - 50% { - opacity: 0; - } -} - -@media (max-width: 900px) { - header { - padding: 32px 18px 0 18px; - } - - header > h1 { - margin-bottom: 18px; - } - - header > p { - font-size: 1rem; - } - - main { - padding: 18px; - } -} - -@media (max-width: 768px) { - main { - grid-template-columns: 1fr; - grid-template-rows: auto auto auto auto auto; - } - - .tokenizer-selector { - grid-column: 1; - grid-row: 1; - } - - .diamond-parent { - grid-column: 1; - grid-row: 2; - } - - .diamond-left { - grid-column: 1; - grid-row: 3; - } - - .diamond-right { - grid-column: 1; - grid-row: 4; - } - - .radio-group { - flex-direction: column; - gap: 12px; - } - - .radio-option { - min-width: unset; - width: 100%; - } - - .diamond-result { - grid-column: 1; - grid-row: 5; - } -} - -footer { - padding: 32px 16px; - width: 100%; - display: flex; - justify-content: center; - align-items: center; - gap: 24px; - color: $text-secondary; -} - -.footer-links { - display: flex; - align-items: center; - gap: 16px; -} - -.footer-links > a > svg { - color: $text-secondary; - width: 28px; - height: 28px; - transition: transform 0.2s; -} - -.footer-links > a > svg:hover { - cursor: pointer; - transform: scale(1.15); -} diff --git a/examples/website/style.css b/examples/website/style.css new file mode 100644 index 0000000..6324812 --- /dev/null +++ b/examples/website/style.css @@ -0,0 +1,192 @@ +* { + box-sizing: border-box; + margin: 0; +} + +html, +body { + height: 100%; +} + +body { + font-family: "Segoe UI", Arial, sans-serif; + background: linear-gradient(135deg, #f8fafc 0%, #e0e7ef 100%); + color: #23272f; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +header { + padding: 32px 20px 0 20px; + + text-align: center; +} + +header > h1 { + font-size: 2.5rem; + font-weight: 700; + color: #2451a6; + margin-bottom: 8px; +} + +header > p { + color: #5a6272; + font-size: 1.1rem; + margin-bottom: 0; +} + +main { + flex: 1; + display: grid; + grid-template-rows: auto auto auto; + grid-template-columns: 1fr auto 1fr; + gap: 20px; + justify-items: center; + align-items: center; + padding: 32px 12vw 32px 12vw; + min-height: 540px; +} + +.diamond-parent { + grid-column: 1 / -1; +} + +.diamond-left { + grid-column: 1; + grid-row: 2; +} + +.diamond-right { + grid-column: 3; + grid-row: 2; +} + +#merge-button { + grid-column: 2; + grid-row: 2; + padding: 12px 36px; + border: none; + border-radius: 8px; + background: linear-gradient(90deg, #2451a6 0%, #3486eb 100%); + color: #fff; + font-size: 1.15rem; + font-weight: 600; + box-shadow: 0 2px 12px 0 rgba(36, 81, 166, 0.08); + cursor: pointer; + align-self: center; + transition: transform 0.2s; + margin: 0 32px; +} + +#merge-button:hover { + transform: scale(1.1); +} + +.diamond-right { + grid-column: 3; + grid-row: 2; +} + +.diamond-result { + grid-column: 1 / -1; + grid-row: 3; + display: flex; + align-items: center; + pointer-events: none; +} + +.text-area { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 100%; + background: #fff; + border-radius: 10px; + box-shadow: 0 2px 12px 0 rgba(36, 81, 166, 0.06); + padding: 18px 20px 16px 20px; + margin-bottom: 0; +} + +label { + margin-bottom: 8px; + font-weight: 600; + color: #2451a6; +} + +textarea { + width: 100%; + border: none; + font-size: 1rem; + font-family: inherit; + color: #23272f; + box-sizing: border-box; + resize: none; + outline: none; + margin-bottom: 0; + height: 100%; +} + +@media (max-width: 900px) { + main { + padding: 24px 2vw; + } +} + +@media (max-width: 768px) { + main { + grid-template-columns: 1fr; + grid-template-rows: auto auto auto auto auto; + } + .diamond-parent { + grid-row: 1; + grid-column: 1; + } + + .diamond-left { + grid-row: 2; + grid-column: 1; + } + + .diamond-right { + grid-row: 3; + grid-column: 1; + } + + #merge-button { + grid-row: 4; + grid-column: 1; + } + + .diamond-result { + grid-row: 5; + grid-column: 1; + } +} + +footer { + position: relative; + margin-top: 32px; + padding: 28px 0 18px 0; + text-align: center; + color: #5a6272; + font-size: 1rem; + box-shadow: 0 -4px 12px 0 rgba(28, 28, 87, 0.1), + 0 -1px 2px 0 rgba(1, 1, 3, 0.1); +} + +.github-link > svg { + position: absolute; + top: 50%; + right: 36px; + transform: translateY(-50%); + width: 32px; + height: 32px; + transition: transform 0.2s; +} + +.github-link > svg:hover { + cursor: pointer; + transform: translateY(-50%) scale(1.15); +} diff --git a/examples/website/tsconfig.json b/examples/website/tsconfig.json deleted file mode 100644 index fb72539..0000000 --- a/examples/website/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "module": "ESNext", - "target": "ES2017", - "strict": true, - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "moduleResolution": "bundler", - "outDir": "./dist", - "rootDir": "./src", - "skipLibCheck": true, - "inlineSourceMap": true - }, - "exclude": ["./dist"] -} diff --git a/examples/website/webpack.config.js b/examples/website/webpack.config.js deleted file mode 100644 index 8a21293..0000000 --- a/examples/website/webpack.config.js +++ /dev/null @@ -1,94 +0,0 @@ -const path = require('path'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const TerserPlugin = require('terser-webpack-plugin'); -const InlineSourceWebpackPlugin = require('inline-source-webpack-plugin'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); - -module.exports = (_env, argv) => ({ - devtool: argv.mode === 'development' ? 'inline-source-map' : false, - entry: { - index: './src/index.ts', - }, - devServer: { - allowedHosts: 'all', - }, - watchOptions: { - ignored: '**/node_modules', - }, - optimization: { - minimizer: [ - new TerserPlugin({ - terserOptions: { - module: true, - }, - }), - ], - }, - performance: { - assetFilter: (f) => !/\.(webm|mp4|pdf)$/.test(f), - maxEntrypointSize: 100000, - maxAssetSize: 512000, - }, - plugins: [ - new HtmlWebpackPlugin({ - template: './src/index.html', - }), - new MiniCssExtractPlugin(), - new CopyWebpackPlugin({ - patterns: [ - { - from: 'src/*.ico', - to: '[name][ext]', - }, - { - from: 'src/*.png', - to: '[name][ext]', - }, - ], - }), - argv.mode === 'production' - ? new InlineSourceWebpackPlugin({ - compress: true, - }) - : null, - ].filter(Boolean), - module: { - rules: [ - { - test: /\.svg$/i, - use: 'svg-inline-loader', - }, - { - test: /\.scss$/i, - use: [ - MiniCssExtractPlugin.loader, - 'css-loader', - 'resolve-url-loader', - { - loader: 'sass-loader', - options: { - sourceMap: true, // required by resolve-url-loader - }, - }, - ], - }, - { - test: /\.ts$/, - use: 'ts-loader', - }, - ], - }, - resolve: { - extensions: [ - '.ts', - '.js', // required for development - ], - }, - output: { - clean: true, - filename: '[name].js', - path: path.resolve(__dirname, 'dist'), - publicPath: '', - }, -}); diff --git a/reconcile-js/__mocks__/wasm.js b/reconcile-js/__mocks__/wasm.js deleted file mode 100644 index 1724448..0000000 --- a/reconcile-js/__mocks__/wasm.js +++ /dev/null @@ -1,9 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -// Read the actual WASM file and convert to base64 for testing -const wasmPath = path.join(__dirname, '../../pkg/reconcile_text_bg.wasm'); -const wasmBuffer = fs.readFileSync(wasmPath); -const wasmBase64 = wasmBuffer.toString('base64'); - -module.exports = wasmBase64; diff --git a/reconcile-js/jest.config.js b/reconcile-js/jest.config.js deleted file mode 100644 index 358968a..0000000 --- a/reconcile-js/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - preset: 'ts-jest/presets/js-with-babel-esm', - moduleNameMapper: { - '^reconcile-text/reconcile_text_bg\\.wasm$': `/__mocks__/wasm.js`, - }, -}; diff --git a/reconcile-js/package-lock.json b/reconcile-js/package-lock.json deleted file mode 100644 index 18ad46b..0000000 --- a/reconcile-js/package-lock.json +++ /dev/null @@ -1,5431 +0,0 @@ -{ - "name": "reconcile-text", - "version": "0.11.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "reconcile-text", - "version": "0.11.0", - "license": "MIT", - "devDependencies": { - "@types/jest": "^30.0.0", - "jest": "^30.3.0", - "prettier": "^3.8.1", - "reconcile-text": "file:../pkg", - "ts-jest": "^29.4.6", - "ts-loader": "^9.5.4", - "tslib": "2.8.1", - "typescript": "5.9.3", - "webpack": "^5.105.4", - "webpack-cli": "^6.0.1", - "webpack-merge": "^6.0.1" - } - }, - "../pkg": { - "name": "reconcile-text", - "version": "0.11.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.6.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.17.0" - } - }, - "node_modules/@emnapi/core": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", - "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", - "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.3.0.tgz", - "integrity": "sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.3.0.tgz", - "integrity": "sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.3.0", - "@jest/pattern": "30.0.1", - "@jest/reporters": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-changed-files": "30.3.0", - "jest-config": "30.3.0", - "jest-haste-map": "30.3.0", - "jest-message-util": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-resolve-dependencies": "30.3.0", - "jest-runner": "30.3.0", - "jest-runtime": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "jest-watcher": "30.3.0", - "pretty-format": "30.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/diff-sequences": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", - "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.3.0.tgz", - "integrity": "sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "jest-mock": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.3.0.tgz", - "integrity": "sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "30.3.0", - "jest-snapshot": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", - "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.3.0.tgz", - "integrity": "sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@sinonjs/fake-timers": "^15.0.0", - "@types/node": "*", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-util": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.3.0.tgz", - "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.3.0", - "@jest/expect": "30.3.0", - "@jest/types": "30.3.0", - "jest-mock": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.3.0.tgz", - "integrity": "sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "@jridgewell/trace-mapping": "^0.3.25", - "@types/node": "*", - "chalk": "^4.1.2", - "collect-v8-coverage": "^1.0.2", - "exit-x": "^0.2.2", - "glob": "^10.5.0", - "graceful-fs": "^4.2.11", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^5.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "jest-worker": "30.3.0", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/jest-worker": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", - "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.3.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", - "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", - "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.3.0.tgz", - "integrity": "sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.3.0", - "@jest/types": "30.3.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz", - "integrity": "sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.3.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.3.0.tgz", - "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.3.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/types": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", - "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.34.48", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", - "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz", - "integrity": "sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "30.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^30.0.0", - "pretty-format": "^30.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.10.1", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "dev": true, - "license": "ISC" - }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", - "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "webpack": "^5.82.0", - "webpack-cli": "6.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/babel-jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.3.0.tgz", - "integrity": "sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "30.3.0", - "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.1", - "babel-preset-jest": "30.3.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0 || ^8.0.0-0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", - "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", - "dev": true, - "license": "BSD-3-Clause", - "workspaces": [ - "test/babel-8" - ], - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.3.0.tgz", - "integrity": "sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/babel__core": "^7.20.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.3.0.tgz", - "integrity": "sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "30.3.0", - "babel-preset-current-node-syntax": "^1.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0 || ^8.0.0-beta.1" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", - "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001777", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz", - "integrity": "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "4.3.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", - "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/colorette": { - "version": "2.0.20", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", - "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.307", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz", - "integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/enhanced-resolve": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", - "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/envinfo": { - "version": "7.14.0", - "dev": true, - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "dev": true, - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/events": { - "version": "3.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/exit-x": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", - "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", - "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.3.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-util": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "dev": true, - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/interpret": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.7.2", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.3.0.tgz", - "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "30.3.0", - "@jest/types": "30.3.0", - "import-local": "^3.2.0", - "jest-cli": "30.3.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.3.0.tgz", - "integrity": "sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.1.1", - "jest-util": "30.3.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-circus": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.3.0.tgz", - "integrity": "sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.3.0", - "@jest/expect": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.3.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-runtime": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", - "p-limit": "^3.1.0", - "pretty-format": "30.3.0", - "pure-rand": "^7.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-cli": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.3.0.tgz", - "integrity": "sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", - "chalk": "^4.1.2", - "exit-x": "^0.2.2", - "import-local": "^3.2.0", - "jest-config": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "yargs": "^17.7.2" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.3.0.tgz", - "integrity": "sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.1.0", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.3.0", - "@jest/types": "30.3.0", - "babel-jest": "30.3.0", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.5.0", - "graceful-fs": "^4.2.11", - "jest-circus": "30.3.0", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-runner": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "parse-json": "^5.2.0", - "pretty-format": "30.3.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "esbuild-register": ">=3.4.0", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", - "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.3.0", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", - "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-each": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.3.0.tgz", - "integrity": "sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", - "chalk": "^4.1.2", - "jest-util": "30.3.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.3.0.tgz", - "integrity": "sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.3.0", - "@jest/fake-timers": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "jest-mock": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", - "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", - "jest-worker": "30.3.0", - "picomatch": "^4.0.3", - "walker": "^1.0.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" - } - }, - "node_modules/jest-haste-map/node_modules/jest-worker": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", - "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.3.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-leak-detector": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz", - "integrity": "sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", - "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.3.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", - "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.3.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.3", - "pretty-format": "30.3.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-message-util/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-mock": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", - "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "jest-util": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "30.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.3.0.tgz", - "integrity": "sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz", - "integrity": "sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "30.0.1", - "jest-snapshot": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.3.0.tgz", - "integrity": "sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.3.0", - "@jest/environment": "30.3.0", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.3.0", - "jest-haste-map": "30.3.0", - "jest-leak-detector": "30.3.0", - "jest-message-util": "30.3.0", - "jest-resolve": "30.3.0", - "jest-runtime": "30.3.0", - "jest-util": "30.3.0", - "jest-watcher": "30.3.0", - "jest-worker": "30.3.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-worker": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", - "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.3.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runtime": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.3.0.tgz", - "integrity": "sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.3.0", - "@jest/fake-timers": "30.3.0", - "@jest/globals": "30.3.0", - "@jest/source-map": "30.0.1", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "cjs-module-lexer": "^2.1.0", - "collect-v8-coverage": "^1.0.2", - "glob": "^10.5.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", - "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@babel/generator": "^7.27.5", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1", - "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.3.0", - "@jest/get-type": "30.1.0", - "@jest/snapshot-utils": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "babel-preset-current-node-syntax": "^1.2.0", - "chalk": "^4.1.2", - "expect": "30.3.0", - "graceful-fs": "^4.2.11", - "jest-diff": "30.3.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "pretty-format": "30.3.0", - "semver": "^7.7.2", - "synckit": "^0.11.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", - "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.3" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-validate": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", - "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.3.0.tgz", - "integrity": "sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "jest-util": "30.3.0", - "string-length": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/napi-postinstall": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", - "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", - "dev": true, - "license": "MIT", - "bin": { - "napi-postinstall": "lib/cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "dev": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-format": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", - "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pure-rand": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", - "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/react-is": { - "version": "18.3.1", - "dev": true, - "license": "MIT" - }, - "node_modules/rechoir": { - "version": "0.8.0", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/reconcile-text": { - "resolved": "../pkg", - "link": true - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-length/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/synckit": { - "version": "0.11.12", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", - "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", - "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", - "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-jest": { - "version": "29.4.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", - "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.3", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ts-loader": { - "version": "9.5.4", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.4.tgz", - "integrity": "sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "typescript": "*", - "webpack": "^5.0.0" - } - }, - "node_modules/ts-loader/node_modules/semver": { - "version": "7.7.2", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-loader/node_modules/source-map": { - "version": "0.7.4", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "dev": true, - "license": "0BSD" - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/undici-types": { - "version": "7.16.0", - "dev": true, - "license": "MIT" - }, - "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.3.0" - }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" - }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack": { - "version": "5.105.4", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.4.tgz", - "integrity": "sha512-jTywjboN9aHxFlToqb0K0Zs9SbBoW4zRUlGzI2tYNxVYcEi/IPpn+Xi4ye5jTLvX2YeLuic/IvxNot+Q1jMoOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.16.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.20.0", - "es-module-lexer": "^2.0.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.3.1", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.17", - "watchpack": "^2.5.1", - "webpack-sources": "^3.3.4" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@discoveryjs/json-ext": "^0.6.1", - "@webpack-cli/configtest": "^3.0.1", - "@webpack-cli/info": "^3.0.1", - "@webpack-cli/serve": "^3.0.1", - "colorette": "^2.0.14", - "commander": "^12.1.0", - "cross-spawn": "^7.0.3", - "envinfo": "^7.14.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^6.0.1" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.82.0" - }, - "peerDependenciesMeta": { - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "12.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/webpack-merge": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", - "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/reconcile-js/package.json b/reconcile-js/package.json deleted file mode 100644 index 42b92df..0000000 --- a/reconcile-js/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "reconcile-text", - "version": "0.11.0", - "description": "Intelligent 3-way text merging with automated conflict resolution", - "main": "dist/reconcile.node.js", - "browser": "dist/reconcile.web.js", - "keywords": [ - "text editing", - "sync", - "collaborative editing", - "3-way", - "merge", - "conflict resolution", - "OT", - "operational transformation", - "CRDT" - ], - "homepage": "https://schmelczer.dev/reconcile/", - "repository": { - "type": "git", - "url": "git+https://github.com/schmelczer/reconcile.git" - }, - "bugs": { - "url": "https://github.com/schmelczer/reconcile/issues", - "email": "andras@schmelczer.dev" - }, - "author": "András Schmelczer ", - "license": "MIT", - "types": "dist/types/index.d.ts", - "files": [ - "dist/**/*" - ], - "scripts": { - "build": "webpack --mode production", - "format": "prettier --write \"./**/*.(ts|scss|json|html)\"", - "test": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest" - }, - "devDependencies": { - "@types/jest": "^30.0.0", - "jest": "^30.3.0", - "prettier": "^3.8.1", - "reconcile-text": "file:../pkg", - "ts-jest": "^29.4.6", - "ts-loader": "^9.5.4", - "tslib": "2.8.1", - "typescript": "5.9.3", - "webpack": "^5.105.4", - "webpack-cli": "^6.0.1", - "webpack-merge": "^6.0.1" - } -} diff --git a/reconcile-js/src/index.test.ts b/reconcile-js/src/index.test.ts deleted file mode 100644 index 0de924c..0000000 --- a/reconcile-js/src/index.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { reconcile, reconcileWithHistory, diff, undiff } from './index'; -import { installWasmLeakDetector, checkForWasmLeaks } from './wasm-leak-detector'; -import * as fs from 'fs'; -import * as path from 'path'; -import { fileURLToPath } from 'url'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -installWasmLeakDetector(); - -afterEach(() => { - const leaks = checkForWasmLeaks(); - if (leaks.length > 0) { - throw new Error( - `WASM memory leak: ${leaks.length} object(s) not freed:\n ${leaks.join('\n ')}` - ); - } -}); - -describe('reconcile', () => { - it('call reconcile without cursors', () => { - expect(reconcile('Hello', 'Hello world', 'Hi world').text).toEqual('Hi world'); - }); - - it('call reconcile with cursors', () => { - const result = reconcile( - 'Hello', - { - text: 'Hello world', - cursors: [ - { - id: 3, - position: 2, - }, - ], - }, - { - text: 'Hi world', - cursors: [ - { - id: 4, - position: 0, - }, - { id: 5, position: 3 }, - ], - } - ); - - expect(result.text).toEqual('Hi world'); - expect(result.cursors).toEqual([ - { id: 3, position: 0 }, - { id: 4, position: 0 }, - { id: 5, position: 3 }, - ]); - }); - - it('call reconcileWithHistory', () => { - const result = reconcileWithHistory('Hello', 'Hello world', 'Hi world'); - - expect(result.text).toEqual('Hi world'); - expect(result.history.length).toBeGreaterThan(0); - }); -}); - -describe('test_diff_and_undiff_are_inverse', () => { - const resourcesPath = path.join(__dirname, '../../tests/resources'); - - const readFileSlice = (fileName: string, start: number, end: number): string => { - const filePath = path.join(resourcesPath, fileName); - const content = fs.readFileSync(filePath, 'utf-8'); - const chars = Array.from(content); // Handle unicode properly - return chars.slice(start, Math.min(end, chars.length)).join(''); - }; - - const files = ['pride_and_prejudice.txt', 'room_with_a_view.txt', 'blns.txt']; - - const ranges = [{ start: 0, end: 50000 }]; - - files.forEach((file1) => { - files.forEach((file2) => { - ranges.forEach((range1) => { - ranges.forEach((range2) => { - it(`should diff & undiff ${file1}[${range1.start}..${range1.end}], ${file2}[${range2.start}..${range2.end}] without panic`, () => { - const content1 = readFileSlice(file1, range1.start, range1.end); - const content2 = readFileSlice(file2, range2.start, range2.end); - - const changes = diff(content1, content2); - const actual = undiff(content1, changes); - expect(actual).toEqual(content2); - }); - }); - }); - }); - }); -}); diff --git a/reconcile-js/src/index.ts b/reconcile-js/src/index.ts deleted file mode 100644 index d00051c..0000000 --- a/reconcile-js/src/index.ts +++ /dev/null @@ -1,351 +0,0 @@ -import { - CursorPosition as wasmCursorPosition, - reconcile as wasmReconcile, - TextWithCursors as wasmTextWithCursors, - SpanWithHistory as wasmSpanWithHistory, - reconcileWithHistory as wasmReconcileWithHistory, - diff as wasmDiff, - undiff as wasmUndiff, - initSync, -} from 'reconcile-text'; - -import wasmBytes from 'reconcile-text/reconcile_text_bg.wasm'; - -// Define the enum values as const arrays to avoid duplication -const BUILTIN_TOKENIZERS = ['Character', 'Line', 'Markdown', 'Word'] as const; -const HISTORY_VALUES = [ - 'Unchanged', - 'AddedFromLeft', - 'AddedFromRight', - 'RemovedFromLeft', - 'RemovedFromRight', -] as const; - -/** - * Tokenisation strategies for text merging. - * - * These correspond to the built-in tokenizers available in the underlying WASM module. - */ -export type BuiltinTokenizer = (typeof BUILTIN_TOKENIZERS)[number]; - -/** - * History classification for text spans in merge results. - * - * Indicates the origin of each text span in the merged document. - */ -export type History = (typeof HISTORY_VALUES)[number]; - -/** - * Represents a text document with associated cursor positions. - * - * This interface is used both as input to reconcile functions (to specify where - * cursors are positioned in the original documents) and as output (with cursors - * automatically repositioned after merging). - */ -export interface TextWithCursors { - /** The document's entire content as a string */ - text: string; - - /** - * Array of cursor positions within the text. Can be empty if there are no cursors to track. - * Each cursor has a unique ID and position. - */ - cursors: CursorPosition[]; -} - -/** - * Like `TextWithCursors`, but cursors may be null or undefined (treated as empty). - * Used as input where cursor tracking is optional. - */ -export interface TextWithOptionalCursors { - /** The document's entire content as a string */ - text: string; - - /** - * Array of cursor positions within the text. Can be null, undefined, or empty - * if there are no cursors to track. Each cursor has a unique ID and position. - */ - cursors: null | undefined | CursorPosition[]; -} - -/** - * Represents a cursor position within a text document. - * - * Cursors are automatically repositioned during text merging to maintain their - * relative positions as text is inserted, deleted, or modified around them. - */ -export interface CursorPosition { - /** Unique identifier for the cursor (can be any number, must be unique within the document) */ - id: number; - - /** Character position in the text, 0-based index from the beginning of the document */ - position: number; -} - -/** - * Represents a merged text document with cursor positions and detailed change history. - * - * This is the return type of `reconcileWithHistory()` and provides complete information - * about how the merge was performed, including which parts of the final text came from - * which source documents. - */ -export interface TextWithCursorsAndHistory { - /** The merged document's entire content */ - text: string; - - /** - * Array of cursor positions within the merged text. Can be empty if there are no cursors to track. - * All cursors are automatically repositioned from the left and right documents. - */ - cursors: CursorPosition[]; - - /** - * Detailed provenance information showing the origin of each text span in the result. - * Each span indicates whether it was unchanged, added from left, added from right, etc. - */ - history: SpanWithHistory[]; -} - -/** - * Represents a span of text in the merged result with its change history. - * - * This shows exactly which source document contributed each piece of text to the - * final merged result. Useful for understanding merge decisions and creating - * visualisations of how documents were combined. - */ -export interface SpanWithHistory { - /** The text content of this span */ - text: string; - - /** The origin of this text span in the merge result */ - history: History; -} - -const UNSUPPORTED_TOKENIZER_ERROR = `Unsupported tokenizer, only ${BUILTIN_TOKENIZERS.join( - ', ' -)} are supported`; - -let isInitialised = false; - -/** - * Merges three versions of text using intelligent conflict resolution. - * - * This is the primary function for 3-way text merging. Unlike traditional merge tools - * that produce conflict markers, this function automatically resolves conflicts by - * applying both sets of changes where possible. - * - * @param original - The original/base version of the text that both sides diverged from - * @param left - The left version of the text (either string or TextWithCursors with cursor positions) - * @param right - The right version of the text (either string or TextWithCursors with cursor positions) - * @param tokenizer - The tokenisation strategy: "Word" (default, recommended for prose), - * "Character" (fine-grained), or "Line" (similar to git merge) - * @returns The reconciled text with automatically repositioned cursor positions - * - * @example - * ```typescript - * const original = "Hello world"; - * const left = "Hello beautiful world"; // Added "beautiful" - * const right = "Hi world"; // Changed "Hello" to "Hi" - * - * const result = reconcile(original, left, right); - * console.log(result.text); // "Hi beautiful world" - * ``` - */ -export function reconcile( - original: string, - left: string | TextWithOptionalCursors, - right: string | TextWithOptionalCursors, - tokenizer: BuiltinTokenizer = 'Word' -): TextWithCursors { - init(); - - if (!BUILTIN_TOKENIZERS.includes(tokenizer)) { - throw new Error(UNSUPPORTED_TOKENIZER_ERROR); - } - - const leftCursor = toWasmTextWithCursors(left); - const rightCursor = toWasmTextWithCursors(right); - - const result = wasmReconcile(original, leftCursor, rightCursor, tokenizer); - - leftCursor.free(); - rightCursor.free(); - - const jsResult = toTextWithCursors(result); - result.free(); - - return jsResult; -} - -/** - * Generates a compact diff representation between an original and changed text. - * - * These can be parsed and unpacked using the `undiff` function or the Rust crate's EditedText::from_diff. - * Cursor positions are omitted from the diff result. - * - * This function computes the differences between two versions of text and returns - * a compact representation of those changes. - * - * @param original - The original/base version of the text - * @param changed - The modified version of the text (either string or TextWithCursors with cursor positions) - * @param tokenizer - The tokenisation strategy, which is the same as used in `reconcile`. - * @returns An array of inserts (strings), deletes (negative integers), and retained spans (positive integers). - */ -export function diff( - original: string, - changed: string | TextWithOptionalCursors, - tokenizer: BuiltinTokenizer = 'Word' -): Array { - init(); - - if (!BUILTIN_TOKENIZERS.includes(tokenizer)) { - throw new Error(UNSUPPORTED_TOKENIZER_ERROR); - } - - const changedWasm = toWasmTextWithCursors(changed); - - const result = wasmDiff(original, changedWasm, tokenizer); - - changedWasm.free(); - - return result.map((item) => (typeof item === 'bigint' ? Number(item) : item)); -} - -/** - * Applies a compact diff to an original text to reconstruct the changed version. - * - * This function takes an original text and a compact diff representation (as produced - * by the `diff` function) and reconstructs the modified text. - * - * @param original - The original/base version of the text - * @param diff - The compact diff array (inserts as strings, deletes as negative integers, retained spans as positive integers) - * @param tokenizer - The tokenisation strategy, which is the same as used in `reconcile`. - * @returns The reconstructed changed text as a string. - */ -export function undiff( - original: string, - diff: Array, - tokenizer: BuiltinTokenizer = 'Word' -): string { - init(); - - if (!BUILTIN_TOKENIZERS.includes(tokenizer)) { - throw new Error(UNSUPPORTED_TOKENIZER_ERROR); - } - - return wasmUndiff(original, diff, tokenizer); -} - -/** - * Merges three versions of text and returns detailed provenance information. - * - * This function behaves like `reconcile()` but also provides - * detailed historical information about the origin of each text span in the result. - * This is valuable for understanding how the merge was performed and which changes - * came from which source. - * - * Note: Computing the history is computationally more expensive than the basic merge. - * - * @param original - The original/base version of the text that both sides diverged from - * @param left - The left version of the text (either string or TextWithCursors with cursor positions) - * @param right - The right version of the text (either string or TextWithCursors with cursor positions) - * @param tokenizer - The tokenisation strategy: "Word" (default, recommended for prose), - * "Character" (fine-grained), or "Line" (similar to git merge) - * @returns The reconciled text with cursor positions and detailed change history - * - * @example - * ```typescript - * const original = "Hello world"; - * const left = "Hello beautiful world"; - * const right = "Hi world"; - * - * const result = reconcileWithHistory(original, left, right); - * console.log(result.text); // "Hi beautiful world" - * console.log(result.history); // Array of SpanWithHistory objects showing change origins - * ``` - */ -export function reconcileWithHistory( - original: string, - left: string | TextWithOptionalCursors, - right: string | TextWithOptionalCursors, - tokenizer: BuiltinTokenizer = 'Word' -): TextWithCursorsAndHistory { - init(); - - if (!BUILTIN_TOKENIZERS.includes(tokenizer)) { - throw new Error(UNSUPPORTED_TOKENIZER_ERROR); - } - - const leftCursor = toWasmTextWithCursors(left); - const rightCursor = toWasmTextWithCursors(right); - - const result = wasmReconcileWithHistory(original, leftCursor, rightCursor, tokenizer); - - leftCursor.free(); - rightCursor.free(); - - const jsResult = toTextWithCursors(result); - const history = result.history().map(toSpanWithHistory); - result.free(); - - return { - ...jsResult, - history, - }; -} - -function init() { - if (isInitialised) { - return; - } - - const wasmBinary = Uint8Array.from(atob(wasmBytes as unknown as string), (c) => - c.charCodeAt(0) - ); - initSync({ module: wasmBinary }); - - isInitialised = true; -} - -function toWasmTextWithCursors( - text: string | TextWithOptionalCursors -): wasmTextWithCursors { - const isInputString = typeof text === 'string'; - const leftText = isInputString ? text : text.text; - const leftCursors = isInputString ? [] : (text.cursors ?? []); - - return new wasmTextWithCursors(leftText, leftCursors.map(toWasmCursorPosition)); -} - -function toWasmCursorPosition({ id, position }: CursorPosition): wasmCursorPosition { - return new wasmCursorPosition(id, position); -} - -function toTextWithCursors(textWithCursor: wasmTextWithCursors): TextWithCursors { - const wasmCursors = textWithCursor.cursors(); - const cursors = wasmCursors.map(toCursorPosition); - for (const cursor of wasmCursors) { - cursor.free(); - } - - return { - text: textWithCursor.text(), - cursors, - }; -} - -function toCursorPosition(cursor: wasmCursorPosition): CursorPosition { - return { - id: cursor.id(), - position: cursor.characterIndex(), - }; -} - -function toSpanWithHistory(span: wasmSpanWithHistory): SpanWithHistory { - const result = { - text: span.text(), - history: span.history(), - }; - span.free(); - return result; -} diff --git a/reconcile-js/src/wasm-leak-detector.ts b/reconcile-js/src/wasm-leak-detector.ts deleted file mode 100644 index a8bcec4..0000000 --- a/reconcile-js/src/wasm-leak-detector.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Test utility for detecting WASM memory leaks. - * - * wasm-bindgen registers every JS-side object with a `FinalizationRegistry`. - * This detector patches `FinalizationRegistry.prototype.register` to collect - * references to all WASM objects. After each test, {@link checkForWasmLeaks} - * inspects `__wbg_ptr` on every tracked object - a non-zero pointer means - * `.free()` was never called, i.e. a leak. - * - * Install once (before any WASM calls) and call {@link checkForWasmLeaks} - * in an `afterEach` hook. - */ - -let trackedObjects: object[] = []; -let originalRegister: Function | null = null; - -interface WasmBindgenObject { - __wbg_ptr: number; - constructor: { name?: string }; -} - -function isWasmBindgenObject(target: unknown): target is WasmBindgenObject { - return ( - target !== null && - typeof target === 'object' && - '__wbg_ptr' in (target as Record) - ); -} - -/** - * Patches `FinalizationRegistry.prototype.register` to track all wasm-bindgen - * objects. Safe to call multiple times (idempotent). - */ -export function installWasmLeakDetector(): void { - if (originalRegister) return; - - originalRegister = FinalizationRegistry.prototype.register; - - FinalizationRegistry.prototype.register = function ( - target: object, - heldValue: unknown, - unregisterToken?: object - ) { - if (isWasmBindgenObject(target)) { - trackedObjects.push(target); - } - return originalRegister!.call(this, target, heldValue, unregisterToken); - }; -} - -/** - * Returns any tracked WASM objects whose `__wbg_ptr` is still non-zero - * (i.e. `.free()` was never called). Clears the tracked set afterwards. - */ -export function checkForWasmLeaks(): string[] { - const leaks = trackedObjects - .filter(isWasmBindgenObject) - .filter((obj) => obj.__wbg_ptr !== 0) - .map((obj) => `${obj.constructor?.name ?? 'Unknown'} (ptr=${obj.__wbg_ptr})`); - - trackedObjects = []; - return leaks; -} diff --git a/reconcile-js/tsconfig.json b/reconcile-js/tsconfig.json deleted file mode 100644 index 08dee60..0000000 --- a/reconcile-js/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "module": "ESNext", - "target": "ESNext", - "strict": true, - "allowSyntheticDefaultImports": true, - "moduleResolution": "bundler", - "declaration": true, - "declarationDir": "./dist/types", - "skipLibCheck": true, - "inlineSourceMap": true - } -} diff --git a/reconcile-js/webpack.config.js b/reconcile-js/webpack.config.js deleted file mode 100644 index bf126fa..0000000 --- a/reconcile-js/webpack.config.js +++ /dev/null @@ -1,61 +0,0 @@ -const path = require('path'); -const { merge } = require('webpack-merge'); - -const common = { - entry: './src/index.ts', - optimization: { - // the consuming project should take care of minification - minimize: false, - }, - resolve: { - extensions: ['.ts', '.js'], - alias: { - root: __dirname, - src: path.resolve(__dirname, 'src'), - }, - }, - performance: { - hints: false, - }, - experiments: { - asyncWebAssembly: true, - }, - module: { - rules: [ - { - test: /\.ts$/, - use: ['ts-loader'], - }, - { - test: /\.wasm$/, - type: 'asset/inline', - generator: { - dataUrl: (content) => content.toString('base64'), - }, - }, - ], - }, -}; - -module.exports = [ - merge(common, { - target: 'web', - output: { - path: path.resolve(__dirname, 'dist'), - filename: 'reconcile.web.js', - library: { - name: 'reconcile', - type: 'umd', - }, - globalObject: 'this', - }, - }), - merge(common, { - target: 'node', - output: { - path: path.resolve(__dirname, 'dist'), - filename: 'reconcile.node.js', - libraryTarget: 'commonjs2', - }, - }), -]; diff --git a/reconcile-python/.gitignore b/reconcile-python/.gitignore deleted file mode 100644 index c93ab73..0000000 --- a/reconcile-python/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -.venv/ -.pytest_cache/ -.ruff_cache/ -__pycache__/ -*.egg-info/ -*.so -*.dylib -*.dSYM/ -dist/ -README.md diff --git a/reconcile-python/Cargo.lock b/reconcile-python/Cargo.lock deleted file mode 100644 index 2dbb233..0000000 --- a/reconcile-python/Cargo.lock +++ /dev/null @@ -1,161 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "libc" -version = "0.2.183" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "portable-atomic" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "pyo3" -version = "0.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf85e27e86080aafd5a22eae58a162e133a589551542b3e5cee4beb27e54f8e1" -dependencies = [ - "libc", - "once_cell", - "portable-atomic", - "pyo3-build-config", - "pyo3-ffi", - "pyo3-macros", -] - -[[package]] -name = "pyo3-build-config" -version = "0.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf94ee265674bf76c09fa430b0e99c26e319c945d96ca0d5a8215f31bf81cf7" -dependencies = [ - "target-lexicon", -] - -[[package]] -name = "pyo3-ffi" -version = "0.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "491aa5fc66d8059dd44a75f4580a2962c1862a1c2945359db36f6c2818b748dc" -dependencies = [ - "libc", - "pyo3-build-config", -] - -[[package]] -name = "pyo3-macros" -version = "0.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d671734e9d7a43449f8480f8b38115df67bef8d21f76837fa75ee7aaa5e52e" -dependencies = [ - "proc-macro2", - "pyo3-macros-backend", - "quote", - "syn", -] - -[[package]] -name = "pyo3-macros-backend" -version = "0.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22faaa1ce6c430a1f71658760497291065e6450d7b5dc2bcf254d49f66ee700a" -dependencies = [ - "heck", - "proc-macro2", - "pyo3-build-config", - "quote", - "syn", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "reconcile-text" -version = "0.11.0" -dependencies = [ - "thiserror", -] - -[[package]] -name = "reconcile-text-python" -version = "0.11.0" -dependencies = [ - "pyo3", - "reconcile-text", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "target-lexicon" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" diff --git a/reconcile-python/Cargo.toml b/reconcile-python/Cargo.toml deleted file mode 100644 index fb6d55e..0000000 --- a/reconcile-python/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "reconcile-text-python" -version = "0.11.0" -edition = "2024" -rust-version = "1.94" -authors = ["Andras Schmelczer "] -license = "MIT" -publish = false - -[lib] -name = "_native" -crate-type = ["cdylib"] - -[dependencies] -reconcile-text = { path = ".." } -pyo3 = { version = "0.28.2", features = ["extension-module"] } diff --git a/reconcile-python/pyproject.toml b/reconcile-python/pyproject.toml deleted file mode 100644 index f2b5d5a..0000000 --- a/reconcile-python/pyproject.toml +++ /dev/null @@ -1,52 +0,0 @@ -[build-system] -requires = ["maturin>=1.0,<2.0"] -build-backend = "maturin" - -[project] -name = "reconcile-text" -version = "0.11.0" -description = "Intelligent 3-way text merging with automated conflict resolution" -readme = "README.md" -license = { text = "MIT" } -authors = [{ name = "Andras Schmelczer", email = "andras@schmelczer.dev" }] -requires-python = ">=3.9" -classifiers = [ - "Programming Language :: Rust", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Typing :: Typed", -] -keywords = ["merge", "OT", "CRDT", "3-way", "diff", "text"] - -[dependency-groups] -dev = ["maturin>=1.0,<2.0", "pytest>=8", "ruff>=0.15", "pyright>=1"] - -[project.urls] -Homepage = "https://schmelczer.dev/reconcile" -Repository = "https://github.com/schmelczer/reconcile" -Issues = "https://github.com/schmelczer/reconcile/issues" - -[tool.maturin] -manifest-path = "Cargo.toml" -module-name = "reconcile_text._native" -python-source = "python" - -[tool.pytest.ini_options] -testpaths = ["tests"] - -[tool.ruff] -target-version = "py39" -line-length = 100 - -[tool.ruff.lint] -select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF"] - -[tool.ruff.lint.isort] -known-first-party = ["reconcile_text"] - -[tool.pyright] -pythonVersion = "3.9" -typeCheckingMode = "strict" -include = ["python", "tests"] diff --git a/reconcile-python/python/reconcile_text/__init__.py b/reconcile-python/python/reconcile_text/__init__.py deleted file mode 100644 index fca9c56..0000000 --- a/reconcile-python/python/reconcile_text/__init__.py +++ /dev/null @@ -1,165 +0,0 @@ -"""Intelligent 3-way text merging with automated conflict resolution.""" - -from __future__ import annotations - -from typing import Literal, TypedDict, Union - -from reconcile_text._native import diff as _diff -from reconcile_text._native import reconcile as _reconcile -from reconcile_text._native import reconcile_with_history as _reconcile_with_history -from reconcile_text._native import undiff as _undiff - -BuiltinTokenizer = Literal["Character", "Line", "Markdown", "Word"] -"""Tokenization strategy for text merging.""" - -History = Literal[ - "Unchanged", "AddedFromLeft", "AddedFromRight", "RemovedFromLeft", "RemovedFromRight" -] -"""Provenance label for each span in a merge result.""" - - -class CursorPosition(TypedDict): - """A cursor position within a text document.""" - - id: int - """Unique identifier for the cursor.""" - position: int - """Character position in the text (0-based).""" - - -class TextWithCursors(TypedDict): - """A text document with associated cursor positions.""" - - text: str - """The document content.""" - cursors: list[CursorPosition] - """Cursor positions within the text.""" - - -class SpanWithHistory(TypedDict): - """A text span annotated with its origin in a merge result.""" - - text: str - """The text content of this span.""" - history: History - """Which source this span came from.""" - - -class TextWithCursorsAndHistory(TypedDict): - """A merged text document with cursor positions and change provenance.""" - - text: str - """The merged document content.""" - cursors: list[CursorPosition] - """Repositioned cursor positions.""" - history: list[SpanWithHistory] - """Provenance information for each text span.""" - - -TextInput = Union[str, TextWithCursors] -"""Input type for text arguments: either a plain string or a dict with text and cursors.""" - - -def reconcile( - parent: str, - left: TextInput, - right: TextInput, - tokenizer: BuiltinTokenizer = "Word", -) -> TextWithCursors: - """Merge three versions of text using conflict-free resolution. - - Takes a parent text and two concurrent edits (left and right), returning - the merged result with automatically repositioned cursors. - - Args: - parent: The original text that both sides diverged from. - left: The left edit (string or dict with "text" and "cursors"). - right: The right edit (string or dict with "text" and "cursors"). - tokenizer: Tokenization strategy. Defaults to "Word". - - Returns: - A dict with "text" (merged string) and "cursors" (repositioned cursor list). - """ - return _reconcile(parent, left, right, tokenizer) # type: ignore[return-value] - - -def reconcile_with_history( - parent: str, - left: TextInput, - right: TextInput, - tokenizer: BuiltinTokenizer = "Word", -) -> TextWithCursorsAndHistory: - """Merge three versions of text and return provenance history. - - Like `reconcile`, but also returns which source each text span came from. - - Args: - parent: The original text that both sides diverged from. - left: The left edit (string or dict with "text" and "cursors"). - right: The right edit (string or dict with "text" and "cursors"). - tokenizer: Tokenization strategy. Defaults to "Word". - - Returns: - A dict with "text", "cursors", and "history". - """ - return _reconcile_with_history(parent, left, right, tokenizer) # type: ignore[return-value] - - -def diff( - parent: str, - changed: TextInput, - tokenizer: BuiltinTokenizer = "Word", -) -> list[int | str]: - """Generate a compact diff between two texts. - - Returns retain counts (positive ints), delete counts (negative ints), - and inserted strings. - - Args: - parent: The original text. - changed: The modified text (string or dict with "text" and "cursors"). - tokenizer: Tokenization strategy. Defaults to "Word". - - Returns: - A list of ints and strings representing the diff. - - Raises: - ValueError: If the diff computation overflows. - """ - return _diff(parent, changed, tokenizer) # type: ignore[return-value] - - -def undiff( - parent: str, - diff: list[int | str], - tokenizer: BuiltinTokenizer = "Word", -) -> str: - """Apply a compact diff to reconstruct the changed text. - - Args: - parent: The original text. - diff: A list of ints and strings (as produced by `diff`). - tokenizer: Tokenization strategy. Defaults to "Word". - - Returns: - The reconstructed text. - - Raises: - ValueError: If the diff format is invalid. - """ - return _undiff(parent, diff, tokenizer) - - -__all__ = [ - "BuiltinTokenizer", - "CursorPosition", - "History", - "SpanWithHistory", - "TextInput", - "TextWithCursors", - "TextWithCursorsAndHistory", - "diff", - "reconcile", - "reconcile_with_history", - "undiff", -] diff --git a/reconcile-python/python/reconcile_text/_native.pyi b/reconcile-python/python/reconcile_text/_native.pyi deleted file mode 100644 index 897513d..0000000 --- a/reconcile-python/python/reconcile_text/_native.pyi +++ /dev/null @@ -1,24 +0,0 @@ -from typing import Any - -def reconcile( - parent: str, - left: Any, - right: Any, - tokenizer: str = "Word", -) -> dict[str, Any]: ... -def reconcile_with_history( - parent: str, - left: Any, - right: Any, - tokenizer: str = "Word", -) -> dict[str, Any]: ... -def diff( - parent: str, - changed: Any, - tokenizer: str = "Word", -) -> list[int | str]: ... -def undiff( - parent: str, - diff: list[int | str], - tokenizer: str = "Word", -) -> str: ... diff --git a/reconcile-python/python/reconcile_text/py.typed b/reconcile-python/python/reconcile_text/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/reconcile-python/src/lib.rs b/reconcile-python/src/lib.rs deleted file mode 100644 index a50366a..0000000 --- a/reconcile-python/src/lib.rs +++ /dev/null @@ -1,235 +0,0 @@ -use pyo3::prelude::*; -use pyo3::types::{PyDict, PyList}; -use reconcile_text::{ - BuiltinTokenizer, CursorPosition, EditedText, NumberOrText, TextWithCursors, -}; - -fn parse_tokenizer(tokenizer: &str) -> PyResult { - match tokenizer { - "Character" => Ok(BuiltinTokenizer::Character), - "Line" => Ok(BuiltinTokenizer::Line), - "Markdown" => Ok(BuiltinTokenizer::Markdown), - "Word" => Ok(BuiltinTokenizer::Word), - _ => Err(pyo3::exceptions::PyValueError::new_err(format!( - "Unknown tokenizer '{tokenizer}', expected Character, Line, Markdown, or Word" - ))), - } -} - -fn extract_text_with_cursors(input: &Bound<'_, PyAny>) -> PyResult { - if let Ok(text) = input.extract::() { - return Ok(TextWithCursors::from(text)); - } - - let dict = input.cast::()?; - - let text: String = dict - .get_item("text")? - .ok_or_else(|| pyo3::exceptions::PyKeyError::new_err("text"))? - .extract()?; - - let cursors = match dict.get_item("cursors")? { - Some(obj) if !obj.is_none() => { - let list = obj.cast::()?; - let mut cursors = Vec::with_capacity(list.len()); - for item in list { - let cursor_dict = item.cast::()?; - let id: usize = cursor_dict - .get_item("id")? - .ok_or_else(|| pyo3::exceptions::PyKeyError::new_err("id"))? - .extract()?; - let position: usize = cursor_dict - .get_item("position")? - .ok_or_else(|| pyo3::exceptions::PyKeyError::new_err("position"))? - .extract()?; - cursors.push(CursorPosition::new(id, position)); - } - cursors - } - _ => Vec::new(), - }; - - Ok(TextWithCursors::new(text, cursors)) -} - -fn text_with_cursors_to_dict<'py>( - py: Python<'py>, - twc: &TextWithCursors, -) -> PyResult> { - let dict = PyDict::new(py); - dict.set_item("text", twc.text())?; - - let cursors = PyList::new( - py, - twc.cursors().iter().map(|c| { - let d = PyDict::new(py); - d.set_item("id", c.id()).unwrap(); - d.set_item("position", c.char_index()).unwrap(); - d - }), - )?; - dict.set_item("cursors", cursors)?; - - Ok(dict) -} - -/// Merge three versions of text using conflict-free resolution. -/// -/// Takes a parent text and two concurrent edits (left and right), returning -/// the merged result with automatically repositioned cursors. -/// -/// Args: -/// parent: The original text that both sides diverged from. -/// left: The left edit, either a string or a dict with "text" and "cursors" keys. -/// right: The right edit, either a string or a dict with "text" and "cursors" keys. -/// tokenizer: Tokenization strategy - "Word" (default), "Character", "Line", or "Markdown". -/// -/// Returns: -/// A dict with "text" (merged string) and "cursors" (list of repositioned cursors). -#[pyfunction] -#[pyo3(signature = (parent, left, right, tokenizer = "Word"))] -fn reconcile<'py>( - py: Python<'py>, - parent: &str, - left: &Bound<'py, PyAny>, - right: &Bound<'py, PyAny>, - tokenizer: &str, -) -> PyResult> { - let tokenizer = parse_tokenizer(tokenizer)?; - let left = extract_text_with_cursors(left)?; - let right = extract_text_with_cursors(right)?; - - let result = reconcile_text::reconcile(parent, &left, &right, &*tokenizer).apply(); - text_with_cursors_to_dict(py, &result) -} - -/// Merge three versions of text and return provenance history. -/// -/// Like `reconcile`, but also returns which source each text span came from. -/// -/// Args: -/// parent: The original text that both sides diverged from. -/// left: The left edit, either a string or a dict with "text" and "cursors" keys. -/// right: The right edit, either a string or a dict with "text" and "cursors" keys. -/// tokenizer: Tokenization strategy - "Word" (default), "Character", "Line", or "Markdown". -/// -/// Returns: -/// A dict with "text", "cursors", and "history" (list of dicts with "text" and "history" keys). -#[pyfunction] -#[pyo3(signature = (parent, left, right, tokenizer = "Word"))] -fn reconcile_with_history<'py>( - py: Python<'py>, - parent: &str, - left: &Bound<'py, PyAny>, - right: &Bound<'py, PyAny>, - tokenizer: &str, -) -> PyResult> { - let tokenizer = parse_tokenizer(tokenizer)?; - let left = extract_text_with_cursors(left)?; - let right = extract_text_with_cursors(right)?; - - let reconciled = reconcile_text::reconcile(parent, &left, &right, &*tokenizer); - let (text_with_cursors, history_spans) = reconciled.apply_with_all(); - - let dict = text_with_cursors_to_dict(py, &text_with_cursors)?; - - let history = PyList::new( - py, - history_spans.iter().map(|span| { - let d = PyDict::new(py); - d.set_item("text", span.text()).unwrap(); - d.set_item("history", format!("{:?}", span.history())) - .unwrap(); - d - }), - )?; - dict.set_item("history", history)?; - - Ok(dict) -} - -/// Generate a compact diff between two texts. -/// -/// Returns a list of retain counts (positive ints), delete counts (negative ints), -/// and inserted strings. -/// -/// Args: -/// parent: The original text. -/// changed: The modified text, either a string or a dict with "text" and "cursors" keys. -/// tokenizer: Tokenization strategy - "Word" (default), "Character", "Line", or "Markdown". -/// -/// Returns: -/// A list of ints and strings representing the diff. -/// -/// Raises: -/// ValueError: If the diff computation overflows. -#[pyfunction] -#[pyo3(signature = (parent, changed, tokenizer = "Word"))] -fn diff<'py>( - py: Python<'py>, - parent: &str, - changed: &Bound<'py, PyAny>, - tokenizer: &str, -) -> PyResult> { - let tokenizer = parse_tokenizer(tokenizer)?; - let changed = extract_text_with_cursors(changed)?; - - let edited = EditedText::from_strings_with_tokenizer(parent, &changed, &*tokenizer); - let diff_result = edited - .to_diff() - .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?; - - let list = PyList::empty(py); - for item in diff_result { - match item { - NumberOrText::Number(n) => list.append(n)?, - NumberOrText::Text(s) => list.append(s)?, - } - } - - Ok(list) -} - -/// Apply a compact diff to a parent text to reconstruct the changed version. -/// -/// Args: -/// parent: The original text. -/// diff: A list of ints and strings (as produced by `diff`). -/// tokenizer: Tokenization strategy - "Word" (default), "Character", "Line", or "Markdown". -/// -/// Returns: -/// The reconstructed text. -/// -/// Raises: -/// ValueError: If the diff format is invalid. -#[pyfunction] -#[pyo3(signature = (parent, diff, tokenizer = "Word"))] -fn undiff(parent: &str, diff: &Bound<'_, PyList>, tokenizer: &str) -> PyResult { - let tokenizer = parse_tokenizer(tokenizer)?; - - let mut parsed: Vec = Vec::with_capacity(diff.len()); - for item in diff { - if let Ok(n) = item.extract::() { - parsed.push(NumberOrText::Number(n)); - } else if let Ok(s) = item.extract::() { - parsed.push(NumberOrText::Text(s)); - } else { - return Err(pyo3::exceptions::PyTypeError::new_err( - "Diff items must be int or str", - )); - } - } - - EditedText::from_diff(parent, parsed, &*tokenizer) - .map(|edited| edited.apply().text()) - .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string())) -} - -#[pymodule] -fn _native(m: &Bound<'_, PyModule>) -> PyResult<()> { - m.add_function(wrap_pyfunction!(reconcile, m)?)?; - m.add_function(wrap_pyfunction!(reconcile_with_history, m)?)?; - m.add_function(wrap_pyfunction!(diff, m)?)?; - m.add_function(wrap_pyfunction!(undiff, m)?)?; - Ok(()) -} diff --git a/reconcile-python/tests/test_reconcile.py b/reconcile-python/tests/test_reconcile.py deleted file mode 100644 index 196e808..0000000 --- a/reconcile-python/tests/test_reconcile.py +++ /dev/null @@ -1,179 +0,0 @@ -from __future__ import annotations - -import subprocess -import sys -from pathlib import Path - -import pytest - -from reconcile_text import diff, reconcile, reconcile_with_history, undiff - -EXAMPLES_DIR = Path(__file__).resolve().parent.parent.parent / "examples" -RESOURCES_DIR = Path(__file__).resolve().parent.parent.parent / "tests" / "resources" - -FILES = ["pride_and_prejudice.txt", "room_with_a_view.txt", "blns.txt"] - - -class TestReconcile: - def test_basic_merge(self) -> None: - result = reconcile("Hello", "Hello world", "Hi world") - assert result["text"] == "Hi world" - - def test_three_way_merge(self) -> None: - parent = "Merging text is hard!" - left = "Merging text is easy!" - right = "With reconcile, merging documents is hard!" - - result = reconcile(parent, left, right) - assert result["text"] == "With reconcile, merging documents is easy!" - - def test_with_cursors(self) -> None: - result = reconcile( - "Hello", - {"text": "Hello world", "cursors": [{"id": 3, "position": 2}]}, - { - "text": "Hi world", - "cursors": [{"id": 4, "position": 0}, {"id": 5, "position": 3}], - }, - ) - - assert result["text"] == "Hi world" - assert result["cursors"] == [ - {"id": 3, "position": 0}, - {"id": 4, "position": 0}, - {"id": 5, "position": 3}, - ] - - def test_character_tokenizer(self) -> None: - result = reconcile("abc", "axc", "abyc", "Character") - assert result["text"] == "axyc" - - def test_line_tokenizer(self) -> None: - parent = "line1\nline2\nline3\n" - left = "line1\nmodified\nline3\n" - right = "line1\nline2\nnew line\n" - - result = reconcile(parent, left, right, "Line") - assert result["text"] == "line1\nmodified\nnew line\n" - - def test_empty_texts(self) -> None: - result = reconcile("", "", "") - assert result["text"] == "" - assert result["cursors"] == [] - - def test_invalid_tokenizer(self) -> None: - with pytest.raises(ValueError, match="Unknown tokenizer"): - reconcile("a", "b", "c", "Invalid") # type: ignore[arg-type] - - -class TestReconcileWithHistory: - def test_returns_history(self) -> None: - result = reconcile_with_history( - "Merging text is hard!", - "Merging text is easy!", - "With reconcile, merging documents is hard!", - ) - - assert result["text"] == "With reconcile, merging documents is easy!" - assert len(result["history"]) > 0 - assert all("text" in span and "history" in span for span in result["history"]) - - def test_history_values(self) -> None: - valid_histories = { - "Unchanged", - "AddedFromLeft", - "AddedFromRight", - "RemovedFromLeft", - "RemovedFromRight", - } - result = reconcile_with_history("Hello", "Hello world", "Hi") - for span in result["history"]: - assert span["history"] in valid_histories - - -class TestDiff: - def test_basic_diff(self) -> None: - result = diff("Hello world", "Hello beautiful world") - assert isinstance(result, list) - assert all(isinstance(item, (int, str)) for item in result) - - def test_no_change(self) -> None: - result = diff("same text", "same text") - # A retain-only diff - assert all(isinstance(item, int) and item > 0 for item in result) - - -class TestUndiff: - def test_roundtrip(self) -> None: - original = "Hello world" - changed = "Hello beautiful world" - - d = diff(original, changed) - reconstructed = undiff(original, d) - assert reconstructed == changed - - def test_empty_roundtrip(self) -> None: - d = diff("", "") - assert undiff("", d) == "" - - def test_invalid_diff(self) -> None: - with pytest.raises(ValueError): - undiff("short", [100]) - - -class TestExamples: - def test_merge_file_stdout(self, tmp_path: Path) -> None: - (tmp_path / "base.txt").write_text("Hello world") - (tmp_path / "mine.txt").write_text("Hello beautiful world") - (tmp_path / "theirs.txt").write_text("Hi world") - - result = subprocess.run( - [ - sys.executable, - str(EXAMPLES_DIR / "merge_file.py"), - str(tmp_path / "mine.txt"), - str(tmp_path / "base.txt"), - str(tmp_path / "theirs.txt"), - ], - capture_output=True, - text=True, - check=True, - ) - - assert result.stdout == "Hi beautiful world" - - def test_merge_file_output_file(self, tmp_path: Path) -> None: - (tmp_path / "base.txt").write_text("Hello world") - (tmp_path / "mine.txt").write_text("Hello beautiful world") - (tmp_path / "theirs.txt").write_text("Hi world") - output = tmp_path / "output.txt" - - subprocess.run( - [ - sys.executable, - str(EXAMPLES_DIR / "merge_file.py"), - str(tmp_path / "mine.txt"), - str(tmp_path / "base.txt"), - str(tmp_path / "theirs.txt"), - str(output), - ], - capture_output=True, - text=True, - check=True, - ) - - assert output.read_text() == "Hi beautiful world" - - -class TestDiffUndiffInverse: - """Verify diff/undiff roundtrip across large real-world texts.""" - - @pytest.mark.parametrize("file1", FILES) - @pytest.mark.parametrize("file2", FILES) - def test_roundtrip_files(self, file1: str, file2: str) -> None: - content1 = (RESOURCES_DIR / file1).read_text()[:50000] - content2 = (RESOURCES_DIR / file2).read_text()[:50000] - - changes = diff(content1, content2) - actual = undiff(content1, changes) - assert actual == content2 diff --git a/reconcile-python/uv.lock b/reconcile-python/uv.lock deleted file mode 100644 index 6871ac5..0000000 --- a/reconcile-python/uv.lock +++ /dev/null @@ -1,279 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.9" -resolution-markers = [ - "python_full_version >= '3.10'", - "python_full_version < '3.10'", -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, -] - -[[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "maturin" -version = "1.12.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0c/18/8b2eebd3ea086a5ec73d7081f95ec64918ceda1900075902fc296ea3ad55/maturin-1.12.6.tar.gz", hash = "sha256:d37be3a811a7f2ee28a0fa0964187efa50e90f21da0c6135c27787fa0b6a89db", size = 269165, upload-time = "2026-03-01T14:54:04.21Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/8b/9ddfde8a485489e3ebdc50ee3042ef1c854f00dfea776b951068f6ffe451/maturin-1.12.6-py3-none-linux_armv6l.whl", hash = "sha256:6892b4176992fcc143f9d1c1c874a816e9a041248eef46433db87b0f0aff4278", size = 9789847, upload-time = "2026-03-01T14:54:09.172Z" }, - { url = "https://files.pythonhosted.org/packages/ef/e8/5f7fd3763f214a77ac0388dbcc71cc30aec5490016bd0c8e6bd729fc7b0a/maturin-1.12.6-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c0c742beeeef7fb93b6a81bd53e75507887e396fd1003c45117658d063812dad", size = 19023833, upload-time = "2026-03-01T14:53:46.743Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7f/706ff3839c8b2046436d4c2bc97596c558728264d18abc298a1ad862a4be/maturin-1.12.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2cb41139295eed6411d3cdafc7430738094c2721f34b7eeb44f33cac516115dc", size = 9821620, upload-time = "2026-03-01T14:54:12.04Z" }, - { url = "https://files.pythonhosted.org/packages/0e/9c/70917fb123c8dd6b595e913616c9c72d730cbf4a2b6cac8077dc02a12586/maturin-1.12.6-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:351f3af1488a7cbdcff3b6d8482c17164273ac981378a13a4a9937a49aec7d71", size = 9849107, upload-time = "2026-03-01T14:53:48.971Z" }, - { url = "https://files.pythonhosted.org/packages/59/ea/f1d6ad95c0a12fbe761a7c28a57540341f188564dbe8ad730a4d1788cd32/maturin-1.12.6-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:6dbddfe4dc7ddee60bbac854870bd7cfec660acb54d015d24597d59a1c828f61", size = 10242855, upload-time = "2026-03-01T14:53:44.605Z" }, - { url = "https://files.pythonhosted.org/packages/93/1b/2419843a4f1d2fb4747f3dc3d9c4a2881cd97a3274dd94738fcdf0835e79/maturin-1.12.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:8fdb0f63e77ee3df0f027a120e9af78dbc31edf0eb0f263d55783c250c33b728", size = 9674972, upload-time = "2026-03-01T14:53:52.763Z" }, - { url = "https://files.pythonhosted.org/packages/71/46/b60ab2fc996d904b40e55bd475599dcdccd8f7ad3e649bf95e87970df466/maturin-1.12.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:fa84b7493a2e80759cacc2e668fa5b444d55b9994e90707c42904f55d6322c1e", size = 9645755, upload-time = "2026-03-01T14:53:58.497Z" }, - { url = "https://files.pythonhosted.org/packages/a4/96/03f2b55a8c226805115232fc23c4a4f33f0c9d39e11efab8166dc440f80d/maturin-1.12.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:e90dc12bc6a38e9495692a36c9e231c4d7e0c9bfde60719468ab7d8673db3c45", size = 12737612, upload-time = "2026-03-01T14:54:05.393Z" }, - { url = "https://files.pythonhosted.org/packages/2b/c2/648667022c5b53cdccefa67c245e8a984970f3045820f00c2e23bdb2aff4/maturin-1.12.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06fc8d089f98623ce924c669b70911dfed30f9a29956c362945f727f9abc546b", size = 10455028, upload-time = "2026-03-01T14:54:07.349Z" }, - { url = "https://files.pythonhosted.org/packages/63/d6/5b5efe3ca0c043357ed3f8d2b2d556169fdbf1ff75e50e8e597708a359d2/maturin-1.12.6-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:75133e56274d43b9227fd49dca9a86e32f1fd56a7b55544910c4ce978c2bb5aa", size = 10014531, upload-time = "2026-03-01T14:53:54.548Z" }, - { url = "https://files.pythonhosted.org/packages/68/d5/39c594c27b1a8b32a0cb95fff9ad60b888c4352d1d1c389ac1bd20dc1e16/maturin-1.12.6-py3-none-win32.whl", hash = "sha256:3f32e0a3720b81423c9d35c14e728cb1f954678124749776dc72d533ea1115e8", size = 8553012, upload-time = "2026-03-01T14:53:50.706Z" }, - { url = "https://files.pythonhosted.org/packages/94/66/b262832a91747e04051e21f986bd01a8af81fbffafacc7d66a11e79aab5f/maturin-1.12.6-py3-none-win_amd64.whl", hash = "sha256:977290159d252db946054a0555263c59b3d0c7957135c69e690f4b1558ee9983", size = 9890470, upload-time = "2026-03-01T14:53:56.659Z" }, - { url = "https://files.pythonhosted.org/packages/e3/47/76b8ca470ddc8d7d36aa8c15f5a6aed1841806bb93a0f4ead8ee61e9a088/maturin-1.12.6-py3-none-win_arm64.whl", hash = "sha256:bae91976cdc8148038e13c881e1e844e5c63e58e026e8b9945aa2d19b3b4ae89", size = 8606158, upload-time = "2026-03-01T14:54:02.423Z" }, -] - -[[package]] -name = "nodeenv" -version = "1.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, -] - -[[package]] -name = "packaging" -version = "26.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, -] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, -] - -[[package]] -name = "pygments" -version = "2.19.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, -] - -[[package]] -name = "pyright" -version = "1.1.408" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "nodeenv" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/74/b2/5db700e52554b8f025faa9c3c624c59f1f6c8841ba81ab97641b54322f16/pyright-1.1.408.tar.gz", hash = "sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684", size = 4400578, upload-time = "2026-01-08T08:07:38.795Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0c/82/a2c93e32800940d9573fb28c346772a14778b84ba7524e691b324620ab89/pyright-1.1.408-py3-none-any.whl", hash = "sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1", size = 6399144, upload-time = "2026-01-08T08:07:37.082Z" }, -] - -[[package]] -name = "pytest" -version = "8.4.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.10'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, - { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "packaging", marker = "python_full_version < '3.10'" }, - { name = "pluggy", marker = "python_full_version < '3.10'" }, - { name = "pygments", marker = "python_full_version < '3.10'" }, - { name = "tomli", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, -] - -[[package]] -name = "pytest" -version = "9.0.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.10'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, - { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "packaging", marker = "python_full_version >= '3.10'" }, - { name = "pluggy", marker = "python_full_version >= '3.10'" }, - { name = "pygments", marker = "python_full_version >= '3.10'" }, - { name = "tomli", marker = "python_full_version == '3.10.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, -] - -[[package]] -name = "reconcile-text" -version = "0.11.0" -source = { editable = "." } - -[package.dev-dependencies] -dev = [ - { name = "maturin" }, - { name = "pyright" }, - { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "ruff" }, -] - -[package.metadata] - -[package.metadata.requires-dev] -dev = [ - { name = "maturin", specifier = ">=1.0,<2.0" }, - { name = "pyright", specifier = ">=1" }, - { name = "pytest", specifier = ">=8" }, - { name = "ruff", specifier = ">=0.15" }, -] - -[[package]] -name = "ruff" -version = "0.15.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/77/9b/840e0039e65fcf12758adf684d2289024d6140cde9268cc59887dc55189c/ruff-0.15.5.tar.gz", hash = "sha256:7c3601d3b6d76dce18c5c824fc8d06f4eef33d6df0c21ec7799510cde0f159a2", size = 4574214, upload-time = "2026-03-05T20:06:34.946Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/47/20/5369c3ce21588c708bcbe517a8fbe1a8dfdb5dfd5137e14790b1da71612c/ruff-0.15.5-py3-none-linux_armv6l.whl", hash = "sha256:4ae44c42281f42e3b06b988e442d344a5b9b72450ff3c892e30d11b29a96a57c", size = 10478185, upload-time = "2026-03-05T20:06:29.093Z" }, - { url = "https://files.pythonhosted.org/packages/44/ed/e81dd668547da281e5dce710cf0bc60193f8d3d43833e8241d006720e42b/ruff-0.15.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6edd3792d408ebcf61adabc01822da687579a1a023f297618ac27a5b51ef0080", size = 10859201, upload-time = "2026-03-05T20:06:32.632Z" }, - { url = "https://files.pythonhosted.org/packages/c4/8f/533075f00aaf19b07c5cd6aa6e5d89424b06b3b3f4583bfa9c640a079059/ruff-0.15.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:89f463f7c8205a9f8dea9d658d59eff49db05f88f89cc3047fb1a02d9f344010", size = 10184752, upload-time = "2026-03-05T20:06:40.312Z" }, - { url = "https://files.pythonhosted.org/packages/66/0e/ba49e2c3fa0395b3152bad634c7432f7edfc509c133b8f4529053ff024fb/ruff-0.15.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba786a8295c6574c1116704cf0b9e6563de3432ac888d8f83685654fe528fd65", size = 10534857, upload-time = "2026-03-05T20:06:19.581Z" }, - { url = "https://files.pythonhosted.org/packages/59/71/39234440f27a226475a0659561adb0d784b4d247dfe7f43ffc12dd02e288/ruff-0.15.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd4b801e57955fe9f02b31d20375ab3a5c4415f2e5105b79fb94cf2642c91440", size = 10309120, upload-time = "2026-03-05T20:06:00.435Z" }, - { url = "https://files.pythonhosted.org/packages/f5/87/4140aa86a93df032156982b726f4952aaec4a883bb98cb6ef73c347da253/ruff-0.15.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391f7c73388f3d8c11b794dbbc2959a5b5afe66642c142a6effa90b45f6f5204", size = 11047428, upload-time = "2026-03-05T20:05:51.867Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f7/4953e7e3287676f78fbe85e3a0ca414c5ca81237b7575bdadc00229ac240/ruff-0.15.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc18f30302e379fe1e998548b0f5e9f4dff907f52f73ad6da419ea9c19d66c8", size = 11914251, upload-time = "2026-03-05T20:06:22.887Z" }, - { url = "https://files.pythonhosted.org/packages/77/46/0f7c865c10cf896ccf5a939c3e84e1cfaeed608ff5249584799a74d33835/ruff-0.15.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc6e7f90087e2d27f98dc34ed1b3ab7c8f0d273cc5431415454e22c0bd2a681", size = 11333801, upload-time = "2026-03-05T20:05:57.168Z" }, - { url = "https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1cb7169f53c1ddb06e71a9aebd7e98fc0fea936b39afb36d8e86d36ecc2636a", size = 11206821, upload-time = "2026-03-05T20:06:03.441Z" }, - { url = "https://files.pythonhosted.org/packages/7a/0d/2132ceaf20c5e8699aa83da2706ecb5c5dcdf78b453f77edca7fb70f8a93/ruff-0.15.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9b037924500a31ee17389b5c8c4d88874cc6ea8e42f12e9c61a3d754ff72f1ca", size = 11133326, upload-time = "2026-03-05T20:06:25.655Z" }, - { url = "https://files.pythonhosted.org/packages/72/cb/2e5259a7eb2a0f87c08c0fe5bf5825a1e4b90883a52685524596bfc93072/ruff-0.15.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65bb414e5b4eadd95a8c1e4804f6772bbe8995889f203a01f77ddf2d790929dd", size = 10510820, upload-time = "2026-03-05T20:06:37.79Z" }, - { url = "https://files.pythonhosted.org/packages/ff/20/b67ce78f9e6c59ffbdb5b4503d0090e749b5f2d31b599b554698a80d861c/ruff-0.15.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d20aa469ae3b57033519c559e9bc9cd9e782842e39be05b50e852c7c981fa01d", size = 10302395, upload-time = "2026-03-05T20:05:54.504Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e5/719f1acccd31b720d477751558ed74e9c88134adcc377e5e886af89d3072/ruff-0.15.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:15388dd28c9161cdb8eda68993533acc870aa4e646a0a277aa166de9ad5a8752", size = 10754069, upload-time = "2026-03-05T20:06:06.422Z" }, - { url = "https://files.pythonhosted.org/packages/c3/9c/d1db14469e32d98f3ca27079dbd30b7b44dbb5317d06ab36718dee3baf03/ruff-0.15.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b30da330cbd03bed0c21420b6b953158f60c74c54c5f4c1dabbdf3a57bf355d2", size = 11304315, upload-time = "2026-03-05T20:06:10.867Z" }, - { url = "https://files.pythonhosted.org/packages/28/3a/950367aee7c69027f4f422059227b290ed780366b6aecee5de5039d50fa8/ruff-0.15.5-py3-none-win32.whl", hash = "sha256:732e5ee1f98ba5b3679029989a06ca39a950cced52143a0ea82a2102cb592b74", size = 10551676, upload-time = "2026-03-05T20:06:13.705Z" }, - { url = "https://files.pythonhosted.org/packages/b8/00/bf077a505b4e649bdd3c47ff8ec967735ce2544c8e4a43aba42ee9bf935d/ruff-0.15.5-py3-none-win_amd64.whl", hash = "sha256:821d41c5fa9e19117616c35eaa3f4b75046ec76c65e7ae20a333e9a8696bc7fe", size = 11678972, upload-time = "2026-03-05T20:06:45.379Z" }, - { url = "https://files.pythonhosted.org/packages/fe/4e/cd76eca6db6115604b7626668e891c9dd03330384082e33662fb0f113614/ruff-0.15.5-py3-none-win_arm64.whl", hash = "sha256:b498d1c60d2fe5c10c45ec3f698901065772730b411f164ae270bb6bfcc4740b", size = 10965572, upload-time = "2026-03-05T20:06:16.984Z" }, -] - -[[package]] -name = "tomli" -version = "2.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, - { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, - { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, - { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, - { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, - { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, - { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, - { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, - { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, - { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, - { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, - { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, - { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, - { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, - { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, - { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, - { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, - { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, - { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, - { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, - { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, - { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, - { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, - { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, - { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, - { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, - { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, - { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, - { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, - { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, - { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, - { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, - { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, - { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, - { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, - { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, - { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, - { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, - { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, - { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, - { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, - { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, - { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, -] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d9db229..0d5c610 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.94.0" +channel = "nightly-2025-06-06" targets = [ "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl" ] profile = "default" diff --git a/rustfmt.toml b/rustfmt.toml index 8a1751f..6640f54 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1,8 @@ +imports_granularity = "crate" +condense_wildcard_suffixes = true +fn_single_line = true +format_strings = true +reorder_impl_items = true +group_imports = "StdExternalCrate" use_field_init_shorthand = true +wrap_comments=true diff --git a/scripts/build-website.sh b/scripts/build-website.sh deleted file mode 100755 index 734d0d4..0000000 --- a/scripts/build-website.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -set -e - -which wasm-pack || cargo install wasm-pack -wasm-pack build --target web --features wasm - -cd reconcile-js -npm ci -npm run build -cd ../examples/website -npm ci -npm run build -cd - diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh index 3298f8b..abd0054 100755 --- a/scripts/bump-version.sh +++ b/scripts/bump-version.sh @@ -2,8 +2,6 @@ set -e -git pull --rebase - if [[ -z $1 ]]; then echo "Usage: $0 {patch|minor|major}" exit 1 @@ -25,33 +23,15 @@ else fi echo "Bumping versions" - -which cargo-set-version || cargo install cargo-edit +cd backend cargo set-version --bump $1 -which wasm-pack || cargo install wasm-pack - wasm-pack build --target web --features wasm -cd reconcile-js -npm version $1 -npm install -NEWVER=$(grep '^version = ' ../Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/') -cd ../reconcile-python -sed -i '' "s/^version = \".*\"/version = \"$NEWVER\"/" Cargo.toml -sed -i '' "s/^version = \".*\"/version = \"$NEWVER\"/" pyproject.toml -cargo update --workspace -uv lock - -cd ../examples/website -npm install - -cd ../.. +# Commit and tag git add . -TAG=$(node -p "require('./reconcile-js/package.json').version") - git commit -m "Bump versions to $TAG" git push diff --git a/scripts/dev-website.sh b/scripts/dev-website.sh index bff09f6..284908b 100755 --- a/scripts/dev-website.sh +++ b/scripts/dev-website.sh @@ -1,13 +1,12 @@ #!/bin/bash -set -e +rm -rf pkg wasm-pack build --target web --features wasm -cd reconcile-js -npm install -npm run build +cp -R pkg/reconcile.js examples/website/ +cp -R pkg/reconcile_bg.wasm examples/website/ -cd ../examples/website -npm install -npm run start +cd examples/website/ + +python3 -m http.server $1 diff --git a/scripts/lint.sh b/scripts/lint.sh deleted file mode 100755 index 6ae4f66..0000000 --- a/scripts/lint.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -set -e - -which cargo-machete || cargo install cargo-machete -cargo machete - -cargo clippy --all-targets --all-features --fix --allow-dirty --allow-staged -cargo fmt --all - -cd reconcile-js -npm ci -npm run format - -cd ../examples/website -npm ci -npm run format - -cd ../../reconcile-python -cp ../README.md . -uv run maturin develop -q -uv run ruff check python/ tests/ -uv run ruff format python/ tests/ -uv run pyright python/ tests/ -cd - - -echo "Success!" diff --git a/scripts/test.sh b/scripts/test.sh deleted file mode 100755 index cdc60e0..0000000 --- a/scripts/test.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -set -e - -which cargo-insta || cargo install cargo-insta -which wasm-pack || cargo install wasm-pack - -node_version=$(node --version | cut -d'.' -f1 | tr -d 'v') -if [ "$node_version" != "22" ]; then - echo "Error: Node.js version 22 is required, but found version $node_version" - exit 1 -fi - -wasm-pack build --target web --features wasm,console_error_panic_hook -cargo test --verbose --features serde -- --include-ignored - -cargo test -cargo test --features serde -cargo test --features wasm -cargo test --features all - -wasm-pack test --node --features wasm,console_error_panic_hook - -cd reconcile-js -npm ci -npm run build -npm run test -cd - - -cd reconcile-python -cp ../README.md . -uv run maturin develop -uv run pytest -v -cd - - -echo "Success!" diff --git a/src/diffs.rs b/src/diffs.rs new file mode 100644 index 0000000..b57139a --- /dev/null +++ b/src/diffs.rs @@ -0,0 +1,2 @@ +pub mod myers; +pub mod raw_operation; diff --git a/src/diffs/myers.rs b/src/diffs/myers.rs new file mode 100644 index 0000000..501eca8 --- /dev/null +++ b/src/diffs/myers.rs @@ -0,0 +1,357 @@ +//! Taken from +//! +//! Myers' diff algorithm. +//! +//! * time: `O((N+M)D)` +//! * space `O(N+M)` +//! +//! See [the original article by Eugene W. Myers](http://www.xmailserver.org/diff2.pdf) +//! describing it. +//! +//! The implementation of this algorithm is based on the implementation by +//! Brandon Williams. +//! +//! # Heuristics +//! +//! At present this implementation of Myers' does not implement any more +//! advanced heuristics that would solve some pathological cases. For instance +//! passing two large and completely distinct sequences to the algorithm will +//! make it spin without making reasonable progress. +//! For potential improvements here see [similar#15](https://github.com/mitsuhiko/similar/issues/15). + +use std::{ + ops::{Index, IndexMut, Range}, + vec, +}; + +use super::raw_operation::RawOperation; +use crate::{ + tokenizer::token::Token, + utils::{common_prefix_len::common_prefix_len, common_suffix_len::common_suffix_len}, +}; + +/// Myers' diff algorithm. +/// +/// Diff `old`, between indices `old_range` and `new` between indices +/// `new_range`. +/// +/// The returned `RawOperations` all have a token count of 1. +pub fn diff(old: &[Token], new: &[Token]) -> Vec> +where + T: PartialEq + Clone + std::fmt::Debug, +{ + let max_d = (old.len() + new.len()).div_ceil(2) + 1; + let mut vb = V::new(max_d); + let mut vf = V::new(max_d); + let mut result: Vec> = vec![]; + + conquer( + old, + 0..old.len(), + new, + 0..new.len(), + &mut vf, + &mut vb, + &mut result, + ); + + debug_assert!( + result.iter().all(|op| op.tokens().len() == 1), + "All operations should be of length 1" + ); + + result +} + +// A D-path is a path which starts at (0,0) that has exactly D non-diagonal +// edges. All D-paths consist of a (D - 1)-path followed by a non-diagonal edge +// and then a possibly empty sequence of diagonal edges called a snake. + +/// `V` contains the endpoints of the furthest reaching `D-paths`. For each +/// recorded endpoint `(x,y)` in diagonal `k`, we only need to retain `x` +/// because `y` can be computed from `x - k`. In other words, `V` is an array of +/// integers where `V[k]` contains the row index of the endpoint of the furthest +/// reaching path in diagonal `k`. +/// +/// We can't use a traditional Vec to represent `V` since we use `k` as an index +/// and it can take on negative values. So instead `V` is represented as a +/// light-weight wrapper around a Vec plus an `offset` which is the maximum +/// value `k` can take on in order to map negative `k`'s back to a value >= 0. +#[derive(Debug)] +struct V { + offset: isize, + v: Vec, // Look into initializing this to -1 and storing isize +} + +impl V { + fn new(max_d: usize) -> Self { + Self { + offset: max_d as isize, + v: vec![0; 2 * max_d], + } + } + + fn len(&self) -> usize { self.v.len() } +} + +impl Index for V { + type Output = usize; + + fn index(&self, index: isize) -> &Self::Output { &self.v[(index + self.offset) as usize] } +} + +impl IndexMut for V { + fn index_mut(&mut self, index: isize) -> &mut Self::Output { + &mut self.v[(index + self.offset) as usize] + } +} + +fn split_at(range: Range, at: usize) -> (Range, Range) { + (range.start..at, at..range.end) +} + +/// A `Snake` is a sequence of diagonal edges in the edit graph. Normally +/// a snake has a start end end point (and it is possible for a snake to have +/// a length of zero, meaning the start and end points are the same) however +/// we do not need the end point which is why it's not implemented here. +/// +/// The divide part of a divide-and-conquer strategy. A D-path has D+1 snakes +/// some of which may be empty. The divide step requires finding the ceil(D/2) + +/// 1 or middle snake of an optimal D-path. The idea for doing so is to +/// simultaneously run the basic algorithm in both the forward and reverse +/// directions until furthest reaching forward and reverse paths starting at +/// opposing corners 'overlap'. +fn find_middle_snake( + old: &[Token], + old_range: Range, + new: &[Token], + new_range: Range, + vf: &mut V, + vb: &mut V, +) -> Option<(usize, usize)> +where + T: PartialEq + Clone + std::fmt::Debug, +{ + let n = old_range.len(); + let m = new_range.len(); + + // By Lemma 1 in the paper, the optimal edit script length is odd or even as + // `delta` is odd or even. + let delta = n as isize - m as isize; + let odd = delta & 1 == 1; + + // The initial point at (0, -1) + vf[1] = 0; + // The initial point at (N, M+1) + vb[1] = 0; + + let d_max = (n + m).div_ceil(2) + 1; + assert!(vf.len() >= d_max); + assert!(vb.len() >= d_max); + + for d in 0..d_max as isize { + // Forward path + for k in (-d..=d).rev().step_by(2) { + let mut x = if k == -d || (k != d && vf[k - 1] < vf[k + 1]) { + vf[k + 1] + } else { + vf[k - 1] + 1 + }; + let y = (x as isize - k) as usize; + + // The coordinate of the start of a snake + let (x0, y0) = (x, y); + // While these sequences are identical, keep moving through the + // graph with no cost + if x < old_range.len() && y < new_range.len() { + let advance = common_prefix_len( + old, + old_range.start + x..old_range.end, + new, + new_range.start + y..new_range.end, + ); + x += advance; + } + + // This is the new best x value + vf[k] = x; + + // Only check for connections from the forward search when N - M is + // odd and when there is a reciprocal k line coming from the other + // direction. + if odd && (k - delta).abs() <= (d - 1) { + // TODO optimize this so we don't have to compare against n + if vf[k] + vb[-(k - delta)] >= n { + // Return the snake + return Some((x0 + old_range.start, y0 + new_range.start)); + } + } + } + + // Backward path + for k in (-d..=d).rev().step_by(2) { + let mut x = if k == -d || (k != d && vb[k - 1] < vb[k + 1]) { + vb[k + 1] + } else { + vb[k - 1] + 1 + }; + let mut y = (x as isize - k) as usize; + + // The coordinate of the start of a snake + if x < n && y < m { + let advance = common_suffix_len( + old, + old_range.start..old_range.start + n - x, + new, + new_range.start..new_range.start + m - y, + ); + x += advance; + y += advance; + } + + // This is the new best x value + vb[k] = x; + + if !odd && (k - delta).abs() <= d { + // TODO optimize this so we don't have to compare against n + if vb[k] + vf[-(k - delta)] >= n { + // Return the snake + return Some((n - x + old_range.start, m - y + new_range.start)); + } + } + } + + // TODO: Maybe there's an opportunity to optimize and bail early? + } + + None +} + +fn conquer( + old: &[Token], + mut old_range: Range, + new: &[Token], + mut new_range: Range, + vf: &mut V, + vb: &mut V, + result: &mut Vec>, +) where + T: PartialEq + Clone + std::fmt::Debug, +{ + // Check for common prefix + let common_prefix_len = common_prefix_len(old, old_range.clone(), new, new_range.clone()); + if common_prefix_len > 0 { + result.extend( + old[old_range.start..old_range.start + common_prefix_len] + .iter() + .map(|token| RawOperation::Equal(vec![token.clone()])), + ); + } + old_range.start += common_prefix_len; + new_range.start += common_prefix_len; + + // Check for common suffix + let common_suffix_len = common_suffix_len(old, old_range.clone(), new, new_range.clone()); + let common_suffix = ( + old_range.end - common_suffix_len, + new_range.end - common_suffix_len, + ); + old_range.end -= common_suffix_len; + new_range.end -= common_suffix_len; + + if old_range.is_empty() && new_range.is_empty() { + // do nothing + } else if new_range.is_empty() { + result.extend( + old[old_range.start..old_range.start + old_range.len()] + .iter() + .map(|token| RawOperation::Delete(vec![token.clone()])), + ); + } else if old_range.is_empty() { + result.extend( + new[new_range.start..new_range.start + new_range.len()] + .iter() + .map(|token| RawOperation::Insert(vec![token.clone()])), + ); + } else if let Some((x_start, y_start)) = + find_middle_snake(old, old_range.clone(), new, new_range.clone(), vf, vb) + { + let (old_a, old_b) = split_at(old_range, x_start); + let (new_a, new_b) = split_at(new_range, y_start); + conquer(old, old_a, new, new_a, vf, vb, result); + conquer(old, old_b, new, new_b, vf, vb, result); + } else { + result.extend( + old[old_range.start..old_range.end] + .iter() + .map(|token| RawOperation::Delete(vec![token.clone()])), + ); + result.extend( + new[new_range.start..new_range.end] + .iter() + .map(|token| RawOperation::Insert(vec![token.clone()])), + ); + } + + if common_suffix_len > 0 { + result.extend( + old[common_suffix.0..common_suffix.0 + common_suffix_len] + .iter() + .map(|token| RawOperation::Equal(vec![token.clone()])), + ); + } +} + +#[cfg(test)] +mod tests { + use insta::assert_debug_snapshot; + + use super::*; + + #[test] + fn test_empty_diff() { + let old: Vec> = vec![]; + let new: Vec> = vec![]; + let result = diff(&old, &new); + assert_eq!(result.len(), 0); + } + + #[test] + fn test_identical_content() { + let content = vec!["a".into(), "b".into(), "c".into()]; + let result = diff(&content, &content); + assert_debug_snapshot!(result); + } + + #[test] + fn test_insert_only() { + let old: Vec> = vec![]; + let new: Vec> = vec!["a".into(), "b".into()]; + let result = diff(&old, &new); + assert_debug_snapshot!(result); + } + + #[test] + fn test_delete_only() { + let old = vec!["a".into(), "b".into()]; + let new: Vec> = vec![]; + let result = diff(&old, &new); + assert_debug_snapshot!(result); + } + + #[test] + fn test_prefix_and_suffix() { + let old = vec!["a".into(), "b".into(), "c".into(), "d".into()]; + let new = vec!["a".into(), "x".into(), "d".into()]; + let result = diff(&old, &new); + assert_debug_snapshot!(result); + } + + #[test] + fn test_complex_diff() { + let old = vec!["a".into(), "b".into(), "c".into(), "d".into()]; + let new = vec!["a".into(), "x".into(), "c".into(), "y".into()]; + let result = diff(&old, &new); + assert_debug_snapshot!(result); + } +} diff --git a/src/raw_operation.rs b/src/diffs/raw_operation.rs similarity index 73% rename from src/raw_operation.rs rename to src/diffs/raw_operation.rs index 331ae81..2e4a0a7 100644 --- a/src/raw_operation.rs +++ b/src/diffs/raw_operation.rs @@ -1,15 +1,9 @@ -use std::fmt::Debug; +use crate::tokenizer::token::Token; -use crate::{tokenizer::token::Token, utils::myers_diff::myers_diff}; - -/// Text editing operation containing the affected tokens. -/// -/// `RawOperation`s can be joined together when the underlying tokens -/// allow for joining subsequent operations. #[derive(Debug, Clone, PartialEq)] pub enum RawOperation where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { Insert(Vec>), Delete(Vec>), @@ -18,13 +12,9 @@ where impl RawOperation where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { - pub fn vec_from(left: &[Token], right: &[Token]) -> Vec { - myers_diff(left, right) - } - - pub fn tokens(&self) -> &[Token] { + pub fn tokens(&self) -> &Vec> { match self { RawOperation::Insert(tokens) | RawOperation::Delete(tokens) @@ -36,9 +26,7 @@ where self.tokens().iter().map(Token::get_original_length).sum() } - pub fn get_original_text(&self) -> String { - self.tokens().iter().map(Token::original).collect() - } + pub fn get_original_text(self) -> String { self.tokens().iter().map(Token::original).collect() } pub fn is_left_joinable(&self) -> bool { let first_token = self.tokens().first(); @@ -53,7 +41,7 @@ where /// Extends the operation with another operation. Only operations of the /// same type as self can be used to extend self, otherwise the function /// will panic. - pub fn join(self, other: RawOperation) -> RawOperation { + pub fn extend(self, other: RawOperation) -> RawOperation { debug_assert!( std::mem::discriminant(&self) == std::mem::discriminant(&other), "Cannot extend operations of different types. This should have been handled before \ diff --git a/src/utils/snapshots/reconcile_text__utils__myers_diff__tests__complex_diff.snap b/src/diffs/snapshots/reconcile__diffs__myers__tests__complex_diff.snap similarity index 83% rename from src/utils/snapshots/reconcile_text__utils__myers_diff__tests__complex_diff.snap rename to src/diffs/snapshots/reconcile__diffs__myers__tests__complex_diff.snap index a28cf05..57ee086 100644 --- a/src/utils/snapshots/reconcile_text__utils__myers_diff__tests__complex_diff.snap +++ b/src/diffs/snapshots/reconcile__diffs__myers__tests__complex_diff.snap @@ -1,5 +1,5 @@ --- -source: src/utils/myers_diff.rs +source: reconcile/src/diffs/myers.rs expression: result snapshot_kind: text --- @@ -7,7 +7,7 @@ snapshot_kind: text Equal( [ Token { - normalized: "a", + normalised: "a", original: "a", is_left_joinable: true, is_right_joinable: true, @@ -17,7 +17,7 @@ snapshot_kind: text Insert( [ Token { - normalized: "x", + normalised: "x", original: "x", is_left_joinable: true, is_right_joinable: true, @@ -27,7 +27,7 @@ snapshot_kind: text Delete( [ Token { - normalized: "b", + normalised: "b", original: "b", is_left_joinable: true, is_right_joinable: true, @@ -37,7 +37,7 @@ snapshot_kind: text Equal( [ Token { - normalized: "c", + normalised: "c", original: "c", is_left_joinable: true, is_right_joinable: true, @@ -47,7 +47,7 @@ snapshot_kind: text Insert( [ Token { - normalized: "y", + normalised: "y", original: "y", is_left_joinable: true, is_right_joinable: true, @@ -57,7 +57,7 @@ snapshot_kind: text Delete( [ Token { - normalized: "d", + normalised: "d", original: "d", is_left_joinable: true, is_right_joinable: true, diff --git a/src/utils/snapshots/reconcile_text__utils__myers_diff__tests__delete_only.snap b/src/diffs/snapshots/reconcile__diffs__myers__tests__delete_only.snap similarity index 80% rename from src/utils/snapshots/reconcile_text__utils__myers_diff__tests__delete_only.snap rename to src/diffs/snapshots/reconcile__diffs__myers__tests__delete_only.snap index d4f02da..93bb529 100644 --- a/src/utils/snapshots/reconcile_text__utils__myers_diff__tests__delete_only.snap +++ b/src/diffs/snapshots/reconcile__diffs__myers__tests__delete_only.snap @@ -1,5 +1,5 @@ --- -source: src/utils/myers_diff.rs +source: reconcile/src/diffs/myers.rs expression: result snapshot_kind: text --- @@ -7,7 +7,7 @@ snapshot_kind: text Delete( [ Token { - normalized: "a", + normalised: "a", original: "a", is_left_joinable: true, is_right_joinable: true, @@ -17,7 +17,7 @@ snapshot_kind: text Delete( [ Token { - normalized: "b", + normalised: "b", original: "b", is_left_joinable: true, is_right_joinable: true, diff --git a/src/utils/snapshots/reconcile_text__utils__myers_diff__tests__identical_content.snap b/src/diffs/snapshots/reconcile__diffs__myers__tests__identical_content.snap similarity index 81% rename from src/utils/snapshots/reconcile_text__utils__myers_diff__tests__identical_content.snap rename to src/diffs/snapshots/reconcile__diffs__myers__tests__identical_content.snap index cf7f0a8..f82d4ac 100644 --- a/src/utils/snapshots/reconcile_text__utils__myers_diff__tests__identical_content.snap +++ b/src/diffs/snapshots/reconcile__diffs__myers__tests__identical_content.snap @@ -1,5 +1,5 @@ --- -source: src/utils/myers_diff.rs +source: reconcile/src/diffs/myers.rs expression: result snapshot_kind: text --- @@ -7,7 +7,7 @@ snapshot_kind: text Equal( [ Token { - normalized: "a", + normalised: "a", original: "a", is_left_joinable: true, is_right_joinable: true, @@ -17,7 +17,7 @@ snapshot_kind: text Equal( [ Token { - normalized: "b", + normalised: "b", original: "b", is_left_joinable: true, is_right_joinable: true, @@ -27,7 +27,7 @@ snapshot_kind: text Equal( [ Token { - normalized: "c", + normalised: "c", original: "c", is_left_joinable: true, is_right_joinable: true, diff --git a/src/utils/snapshots/reconcile_text__utils__myers_diff__tests__insert_only.snap b/src/diffs/snapshots/reconcile__diffs__myers__tests__insert_only.snap similarity index 80% rename from src/utils/snapshots/reconcile_text__utils__myers_diff__tests__insert_only.snap rename to src/diffs/snapshots/reconcile__diffs__myers__tests__insert_only.snap index 3597f64..0f61f3c 100644 --- a/src/utils/snapshots/reconcile_text__utils__myers_diff__tests__insert_only.snap +++ b/src/diffs/snapshots/reconcile__diffs__myers__tests__insert_only.snap @@ -1,5 +1,5 @@ --- -source: src/utils/myers_diff.rs +source: reconcile/src/diffs/myers.rs expression: result snapshot_kind: text --- @@ -7,7 +7,7 @@ snapshot_kind: text Insert( [ Token { - normalized: "a", + normalised: "a", original: "a", is_left_joinable: true, is_right_joinable: true, @@ -17,7 +17,7 @@ snapshot_kind: text Insert( [ Token { - normalized: "b", + normalised: "b", original: "b", is_left_joinable: true, is_right_joinable: true, diff --git a/src/utils/snapshots/reconcile_text__utils__myers_diff__tests__prefix_and_suffix.snap b/src/diffs/snapshots/reconcile__diffs__myers__tests__prefix_and_suffix.snap similarity index 82% rename from src/utils/snapshots/reconcile_text__utils__myers_diff__tests__prefix_and_suffix.snap rename to src/diffs/snapshots/reconcile__diffs__myers__tests__prefix_and_suffix.snap index aeb360e..e50984f 100644 --- a/src/utils/snapshots/reconcile_text__utils__myers_diff__tests__prefix_and_suffix.snap +++ b/src/diffs/snapshots/reconcile__diffs__myers__tests__prefix_and_suffix.snap @@ -1,5 +1,5 @@ --- -source: src/utils/myers_diff.rs +source: reconcile/src/diffs/myers.rs expression: result snapshot_kind: text --- @@ -7,7 +7,7 @@ snapshot_kind: text Equal( [ Token { - normalized: "a", + normalised: "a", original: "a", is_left_joinable: true, is_right_joinable: true, @@ -17,7 +17,7 @@ snapshot_kind: text Delete( [ Token { - normalized: "b", + normalised: "b", original: "b", is_left_joinable: true, is_right_joinable: true, @@ -27,7 +27,7 @@ snapshot_kind: text Delete( [ Token { - normalized: "c", + normalised: "c", original: "c", is_left_joinable: true, is_right_joinable: true, @@ -37,7 +37,7 @@ snapshot_kind: text Insert( [ Token { - normalized: "x", + normalised: "x", original: "x", is_left_joinable: true, is_right_joinable: true, @@ -47,7 +47,7 @@ snapshot_kind: text Equal( [ Token { - normalized: "d", + normalised: "d", original: "d", is_left_joinable: true, is_right_joinable: true, diff --git a/src/lib.rs b/src/lib.rs index f200340..f954c77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,227 +1,13 @@ -//! # Reconcile: conflict-free 3-way text merging -//! -//! A library for merging conflicting text edits without manual intervention. -//! Unlike traditional 3-way merge tools that produce conflict markers, -//! reconcile-text automatically resolves conflicts by applying both sets of -//! changes (while updating cursor positions) using an algorithm inspired by -//! Operational Transformation. -//! -//! ✨ **[Try the interactive demo](https://schmelczer.dev/reconcile)** to see it in action. -//! -//! ## Simple example -//! -//! ``` -//! use reconcile_text::{reconcile, BuiltinTokenizer}; -//! -//! // Start with original text -//! let parent = "Merging text is hard!"; -//! // Two people edit simultaneously -//! let left = "Merging text is easy!"; // Changed "hard" to "easy" -//! let right = "With reconcile, merging documents is hard!"; // Added prefix and changed word -//! -//! // Reconcile combines both changes intelligently -//! let result = reconcile(parent, &left.into(), &right.into(), &*BuiltinTokenizer::Word); -//! assert_eq!(result.apply().text(), "With reconcile, merging documents is easy!"); -//! ``` -//! -//! ## Tokenisation strategies -//! -//! Merging happens at the token level, and the choice of tokeniser -//! significantly affects merge quality and behaviour. -//! -//! ### Built-in tokenisers -//! -//! - **`BuiltinTokenizer::Word`** (recommended): Splits on word boundaries, -//! preserving word integrity -//! - **`BuiltinTokenizer::Character`**: Character-level merging for -//! fine-grained control -//! - **`BuiltinTokenizer::Line`**: Line-based merging, similar to traditional -//! diff tools -//! -//! ``` -//! use reconcile_text::{reconcile, BuiltinTokenizer}; -//! -//! let parent = "The quick brown fox\njumps over the lazy dog"; -//! let left = "The very quick brown fox\njumps over the lazy dog"; // Added "very" -//! let right = "The quick red fox\njumps over the lazy dog"; // Changed "brown" to "red" -//! -//! // Word-level tokenisation (recommended for most text) -//! let result = reconcile(parent, &left.into(), &right.into(), &*BuiltinTokenizer::Word); -//! assert_eq!(result.apply().text(), "The very quick red fox\njumps over the lazy dog"); -//! -//! // Line-level tokenisation (similar to git merge) -//! let result = reconcile(parent, &left.into(), &right.into(), &*BuiltinTokenizer::Line); -//! // Line-level produces different results as it treats each line as atomic -//! assert_eq!(result.apply().text(), "The quick red foxThe very quick brown fox\njumps over the lazy dog"); -//! ``` -//! -//! ### Custom tokenisation -//! -//! For specialised use cases, such as structured languages, custom -//! tokenisation logic can be implemented by providing a function with the -//! signature `Fn(&str) -> Vec>`: -//! -//! ``` -//! use reconcile_text::{reconcile, Token, BuiltinTokenizer}; -//! -//! // Example: sentence-based tokeniser function -//! let sentence_tokeniser = |text: &str| { -//! text.split_inclusive(". ") -//! .map(|sentence| Token::new( -//! sentence.to_string(), -//! sentence.to_string(), -//! false, // don't allow joining with preceding token -//! false, // don't allow joining with following token -//! )) -//! .collect::>() -//! }; -//! -//! let parent = "Hello world. This is a test."; -//! let left = "Hello beautiful world. This is a test."; // Added "beautiful" -//! let right = "Hello world. This is a great test."; // Changed "a" to "great" -//! -//! // For most cases, the built-in word tokeniser works well -//! let result = reconcile(parent, &left.into(), &right.into(), &sentence_tokeniser); -//! assert_eq!(result.apply().text(), "Hello beautiful world. This is a great test."); -//! ``` -//! -//! > **Note**: Setting token joinability to `false` causes insertions to -//! > interleave (LRLRLR) rather than group together (LLLRRR), which often -//! > produces more natural-looking merged text. -//! -//! ## Cursor tracking -//! -//! Automatically repositions cursors and selection ranges during merging, -//! which is essential for collaborative editors: -//! -//! ``` -//! use reconcile_text::{reconcile, BuiltinTokenizer, TextWithCursors, CursorPosition}; -//! -//! let parent = "Hello world"; -//! let left = TextWithCursors::new( -//! "Hello beautiful world".to_string(), -//! vec![CursorPosition::new(1, 6)] // After "Hello " -//! ); -//! let right = TextWithCursors::new( -//! "Hi world".to_string(), -//! vec![CursorPosition::new(2, 0)] // At the beginning -//! ); -//! -//! let result = reconcile(parent, &left, &right, &*BuiltinTokenizer::Word); -//! let merged = result.apply(); -//! -//! assert_eq!(merged.text(), "Hi beautiful world"); -//! // Cursors are automatically repositioned in the merged text -//! assert_eq!(merged.cursors().len(), 2); -//! // Cursor 1 moves from position 6 to position 3 (after "Hi ") -//! // Cursor 2 stays at position 0 (at the beginning) -//! ``` -//! > The `cursors` list is sorted by character position (not IDs). -//! -//! ## Change provenance -//! -//! Track which changes came from where: -//! -//! ```rust -//! use reconcile_text::{History, SpanWithHistory, BuiltinTokenizer, reconcile}; -//! -//! let parent = "Merging text is hard!"; -//! let left = "Merging text is easy!"; // Changed "hard" to "easy" -//! let right = "With reconcile, merging documents is hard!"; // Added prefix and changed word -//! -//! let result = reconcile( -//! parent, -//! &left.into(), -//! &right.into(), -//! &*BuiltinTokenizer::Word, -//! ); -//! -//! assert_eq!( -//! result.apply_with_history(), -//! vec![ -//! SpanWithHistory::new("Merging text".to_string(), History::RemovedFromRight), -//! SpanWithHistory::new( -//! "With reconcile, merging documents".to_string(), -//! History::AddedFromRight -//! ), -//! SpanWithHistory::new(" ".to_string(), History::Unchanged), -//! SpanWithHistory::new("is".to_string(), History::Unchanged), -//! SpanWithHistory::new(" hard!".to_string(), History::RemovedFromLeft), -//! SpanWithHistory::new(" easy!".to_string(), History::AddedFromLeft), -//! ] -//! ); -//! ``` -//! -//! ## Compact change serialization -//! -//! The edits can be serialized into a compact representation without the full -//! original text, making the size depend only on the changes made. -//! -//! ```rust -//! # #[cfg(feature = "serde")] -//! # { -//! use reconcile_text::{EditedText, BuiltinTokenizer}; -//! use serde_yaml; -//! use pretty_assertions::assert_eq; -//! -//! -//! let original = "Merging text is hard!"; -//! let changes = "Merging text is easy with reconcile!"; -//! -//! let result = EditedText::from_strings( -//! original, -//! &changes.into() -//! ); -//! -//! let serialized = serde_yaml::to_string(&result.to_diff().unwrap()).unwrap(); -//! assert_eq!( -//! serialized, -//! concat!( -//! "- 15\n", -//! "- -6\n", -//! "- ' easy with reconcile!'\n" -//! ) -//! ); -//! -//! let deserialized = serde_yaml::from_str(&serialized).unwrap(); -//! let reconstructed = EditedText::from_diff( -//! original, -//! deserialized, -//! &*BuiltinTokenizer::Word -//! ).unwrap(); -//! assert_eq!( -//! reconstructed.apply().text(), -//! "Merging text is easy with reconcile!" -//! ); -//! # } -//! ``` -//! -//! ## Error handling -//! -//! The library is designed to be robust and will always produce a result, even -//! for edge cases. -//! -//! ## Performance -//! -//! Be aware that extremely large diffs may have performance implications. -//! -//! ## Algorithm overview -//! -//! For detailed algorithm explanation, see the -//! [README](https://github.com/schmelczer/reconcile/blob/main/README.md#how-it-works). - +mod diffs; mod operation_transformation; -mod raw_operation; mod tokenizer; -mod types; mod utils; -pub use operation_transformation::{DiffError, EditedText, reconcile}; -pub use tokenizer::{BuiltinTokenizer, Tokenizer, token::Token}; -pub use types::{ - cursor_position::CursorPosition, history::History, number_or_text::NumberOrText, side::Side, - span_with_history::SpanWithHistory, text_with_cursors::TextWithCursors, +pub use operation_transformation::{ + CursorPosition, EditedText, TextWithCursors, reconcile, reconcile_with_cursors, + reconcile_with_tokenizer, }; +pub use tokenizer::{Tokenizer, token::Token, word_tokenizer::word_tokenizer}; #[cfg(feature = "wasm")] pub mod wasm; diff --git a/src/operation_transformation.rs b/src/operation_transformation.rs index bbaf73e..777c2f0 100644 --- a/src/operation_transformation.rs +++ b/src/operation_transformation.rs @@ -1,55 +1,51 @@ -mod diff_error; +mod cursor; mod edited_text; mod operation; mod utils; -use std::fmt::Debug; -pub use diff_error::DiffError; +pub use cursor::{CursorPosition, TextWithCursors}; pub use edited_text::EditedText; pub use operation::Operation; -use crate::{Tokenizer, types::text_with_cursors::TextWithCursors}; +use crate::Tokenizer; -/// Given an `original` document and two concurrent edits to it, -/// return a document containing all changes from both `left` -/// and `right`. -/// -/// If a span has been inserted in either the `left` or `right` -/// versions, it will be present in the return value. If both sides -/// insert the same span with a common prefix, that prefix will only -/// be present once in the output. -/// -/// When both sides delete the same span, it will be deleted in the -/// return value. If one side deletes a span and the other side inserts -/// into that span, the inserted text will be present in the return -/// value. -/// -/// Supports UTF-8. Arguments are tokenized using the provided `tokenizer`. -/// -/// ``` -/// use reconcile_text::{reconcile, BuiltinTokenizer}; -/// -/// let parent = "Merging text is hard!"; -/// let left = "Merging text is easy!"; -/// let right = "With reconcile, merging documents is hard!"; -/// -/// let deconflicted = reconcile(parent, &left.into(), &right.into(), &*BuiltinTokenizer::Word); -/// assert_eq!(deconflicted.apply().text(), "With reconcile, merging documents is easy!"); -/// ``` #[must_use] -pub fn reconcile<'a, T>( +pub fn reconcile(original: &str, left: &str, right: &str) -> String { + reconcile_with_cursors(original, left.into(), right.into()) + .text + .to_string() +} + +#[must_use] +pub fn reconcile_with_cursors<'a>( original: &'a str, - left: &TextWithCursors, - right: &TextWithCursors, + left: TextWithCursors<'a>, + right: TextWithCursors<'a>, +) -> TextWithCursors<'static> { + let left_operations = EditedText::from_strings(original, left); + let right_operations = EditedText::from_strings(original, right); + + let merged_operations = left_operations.merge(right_operations); + + TextWithCursors::new_owned(merged_operations.apply(), merged_operations.cursors) +} + +#[must_use] +pub fn reconcile_with_tokenizer<'a, F, T>( + original: &str, + left: TextWithCursors<'a>, + right: TextWithCursors<'a>, tokenizer: &Tokenizer, -) -> EditedText<'a, T> +) -> TextWithCursors<'static> where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { let left_operations = EditedText::from_strings_with_tokenizer(original, left, tokenizer); let right_operations = EditedText::from_strings_with_tokenizer(original, right, tokenizer); - left_operations.merge(right_operations) + let merged_operations = left_operations.merge(right_operations); + + TextWithCursors::new_owned(merged_operations.apply(), merged_operations.cursors) } #[cfg(test)] @@ -60,13 +56,13 @@ mod test { use test_case::test_matrix; use super::*; - use crate::{BuiltinTokenizer, CursorPosition, types::text_with_cursors::TextWithCursors}; + use crate::CursorPosition; #[test] fn test_cursor_complex() { - let original: &'static str = "this is some complex text to test cursor positions"; + let original = "this is some complex text to test cursor positions"; let left = TextWithCursors::new( - "this is really complex text for testing cursor positions".to_owned(), + "this is really complex text for testing cursor positions", vec![ CursorPosition { id: 0, @@ -79,7 +75,7 @@ mod test { ], ); let right = TextWithCursors::new( - "that was some complex sample to test cursor movements".to_owned(), + "that was some complex sample to test cursor movements", vec![ CursorPosition { id: 2, @@ -92,31 +88,31 @@ mod test { ], ); - let merged = reconcile(original, &left, &right, &*BuiltinTokenizer::Word).apply(); + let merged = reconcile_with_cursors(original, left, right); + assert_eq!( - &merged.text(), - "that was really complex sample for testing cursor movements" - ); - assert_eq!( - merged.cursors(), - vec![ - CursorPosition { - id: 2, - char_index: 5 - }, // unchanged - CursorPosition { - id: 0, - char_index: 9 - }, // before "really" - CursorPosition { - id: 1, - char_index: 23 - }, // inside of "s|ample" because "text" got replaced by "sample" - CursorPosition { - id: 3, - char_index: 30 - }, // after "complex sample" - ] + merged, + TextWithCursors::new( + "that was really complex sample for testing cursor movements", + vec![ + CursorPosition { + id: 2, + char_index: 5 + }, // unchanged + CursorPosition { + id: 0, + char_index: 9 + }, // before "really" + CursorPosition { + id: 1, + char_index: 23 + }, // inside of "s|ample" because "text" got replaced by "sample" + CursorPosition { + id: 3, + char_index: 30 + }, // after "complex sample" + ] + ) ); } @@ -129,11 +125,14 @@ mod test { ], [ "pride_and_prejudice.txt", "room_with_a_view.txt", + "kun_lu.txt", + "blns.txt" ], [ + "pride_and_prejudice.txt", "room_with_a_view.txt", "kun_lu.txt", "blns.txt" - ], [0..10000], [0..10000, 10000..20000], [0..10000, 10000..20000])] + ], [0..10000, 10000..20000], [0..10000, 10000..20000], [0..10000, 10000..20000])] fn test_merge_files_without_panic( file_name_1: &str, file_name_2: &str, @@ -156,16 +155,11 @@ mod test { .unwrap() .chars() .skip(range.start) - .take(range.len()) + .take(range.end) .collect::() }) .collect::>(); - let _ = reconcile( - &contents[0], - &(&contents[1]).into(), - &(&contents[2]).into(), - &*BuiltinTokenizer::Word, - ); + let _ = reconcile(&contents[0], &contents[1], &contents[2]); } } diff --git a/src/operation_transformation/cursor.rs b/src/operation_transformation/cursor.rs new file mode 100644 index 0000000..f145273 --- /dev/null +++ b/src/operation_transformation/cursor.rs @@ -0,0 +1,57 @@ +use std::borrow::Cow; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +// CursorPosition represents the position of an identifiable cursor in a text +// document based on its (UTF-8) character index. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Default)] +pub struct CursorPosition { + pub id: usize, + pub char_index: usize, +} + +impl CursorPosition { + #[must_use] + pub fn with_index(&self, index: usize) -> Self { + CursorPosition { + id: self.id, + char_index: index, + } + } +} + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Default)] +pub struct TextWithCursors<'a> { + pub text: Cow<'a, str>, + pub cursors: Vec, +} + +impl<'a> TextWithCursors<'a> { + #[must_use] + pub fn new(text: &'a str, cursors: Vec) -> Self { + Self { + text: text.into(), + cursors, + } + } + + #[must_use] + pub fn new_owned(text: String, cursors: Vec) -> Self { + Self { + text: text.into(), + cursors, + } + } +} + +impl<'a> From<&'a str> for TextWithCursors<'a> { + fn from(text: &'a str) -> Self { + Self { + text: text.into(), + cursors: Vec::new(), + } + } +} diff --git a/src/operation_transformation/diff_error.rs b/src/operation_transformation/diff_error.rs deleted file mode 100644 index 22e717f..0000000 --- a/src/operation_transformation/diff_error.rs +++ /dev/null @@ -1,26 +0,0 @@ -use thiserror::Error; - -/// Error type for invalid diff operations -#[derive(Error, Debug, Clone, PartialEq)] -pub enum DiffError { - /// The diff references a range that exceeds the original text length - #[error( - "Invalid diff: attempting to access {requested} characters starting at position \ - {position}, but original text only has {available} characters remaining" - )] - LengthExceedsOriginal { - /// The position where the operation starts - position: usize, - /// The number of characters requested - requested: usize, - /// The number of characters available from the position - available: usize, - }, - - /// A character count was too large to represent as i64 - #[error("Integer overflow: value {value} cannot be represented as i64")] - IntegerOverflow { - /// The value that caused the overflow - value: usize, - }, -} diff --git a/src/operation_transformation/edited_text.rs b/src/operation_transformation/edited_text.rs index 2a98259..4e9c7be 100644 --- a/src/operation_transformation/edited_text.rs +++ b/src/operation_transformation/edited_text.rs @@ -1,111 +1,92 @@ -use std::fmt::Debug; - #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use super::{CursorPosition, Operation, TextWithCursors}; use crate::{ - BuiltinTokenizer, CursorPosition, TextWithCursors, Token, - operation_transformation::{ - DiffError, Operation, - utils::{cook_operations::cook_operations, elongate_operations::elongate_operations}, + diffs::{myers::diff, raw_operation::RawOperation}, + operation_transformation::utils::{ + cook_operations::cook_operations, elongate_operations::elongate_operations, }, - raw_operation::RawOperation, - tokenizer::Tokenizer, - types::{ - history::History, number_or_text::NumberOrText, side::Side, - span_with_history::SpanWithHistory, - }, - utils::string_builder::StringBuilder, + tokenizer::{Tokenizer, word_tokenizer::word_tokenizer}, + utils::{side::Side, string_builder::StringBuilder}, }; -/// A text document with a sequence of operations derived from diffing it -/// against an updated version. Supports merging two `EditedText` instances -/// (from the same original) via Operational Transformation. +/// A text document and a sequence of operations that can be applied to the text +/// document. `EditedText` supports merging two sequences of operations using +/// the principles of Operational Transformation. /// -/// Created via `from_strings`, `from_strings_with_tokenizer`, or `from_diff`, -/// then merged with another `EditedText` and applied to get the reconciled -/// text. +/// It's mainly created through the `from_strings` method, then merged with +/// another `EditedText` derived from the same original text and then applied to +/// the original text to get the reconciled text of concurrent edits. /// -/// Also tracks cursor positions from the updated text, repositioning them -/// when operations are applied. +/// In addition to text and operations, it also keeps track of cursor positions +/// in the original text. The cursor positions are updated when the operations +/// are applied, so that the cursor positions can be used to restore the +/// cursor positions in the updated text. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Default)] pub struct EditedText<'a, T> where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { text: &'a str, operations: Vec>, - operation_sides: Vec, - cursors: Vec, + pub(crate) cursors: Vec, } impl<'a> EditedText<'a, String> { - /// Create an `EditedText` from the given original and updated strings. - /// Uses the default word tokenizer (splits on word boundaries). + /// Create an `EditedText` from the given original (old) and updated (new) + /// strings. The returned `EditedText` represents the changes from the + /// original to the updated text. When the return value is applied to + /// the original text, it will result in the updated text. The default + /// word tokenizer is used to tokenize the text which splits the text on + /// whitespaces. #[must_use] - pub fn from_strings(original: &'a str, updated: &TextWithCursors) -> Self { - Self::from_strings_with_tokenizer(original, updated, &*BuiltinTokenizer::Word) + pub fn from_strings(original: &'a str, updated: TextWithCursors<'a>) -> Self { + Self::from_strings_with_tokenizer(original, updated, &word_tokenizer) } } impl<'a, T> EditedText<'a, T> where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { - /// Create an `EditedText` from the given original and updated strings - /// using the provided tokenizer - #[must_use] + /// Create an `EditedText` from the given original (old) and updated (new) + /// strings. The returned `EditedText` represents the changes from the + /// original to the updated text. When the return value is applied to + /// the original text, it will result in the updated text. The tokenizer + /// function is used to tokenize the text. pub fn from_strings_with_tokenizer( original: &'a str, - updated: &TextWithCursors, + updated: TextWithCursors<'a>, tokenizer: &Tokenizer, ) -> Self { let original_tokens = (tokenizer)(original); - let updated_tokens = (tokenizer)(&updated.text()); + let updated_tokens = (tokenizer)(&updated.text); - let diff: Vec> = RawOperation::vec_from(&original_tokens, &updated_tokens); - let operations: Vec> = cook_operations(elongate_operations(diff)).collect(); - let operation_count = operations.len(); + let diff: Vec> = diff(&original_tokens, &updated_tokens); Self::new( original, - operations, - vec![Side::Left; operation_count], - updated.cursors(), + cook_operations(elongate_operations(diff)).collect(), + updated.cursors, ) } /// Create a new `EditedText` with the given operations. /// The operations must be in the order in which they are meant to be /// applied. The operations must not overlap. - fn new( - text: &'a str, - operations: Vec>, - operation_sides: Vec, - mut cursors: Vec, - ) -> Self { + fn new(text: &'a str, operations: Vec>, mut cursors: Vec) -> Self { cursors.sort_by_key(|cursor| cursor.char_index); Self { text, operations, - operation_sides, cursors, } } - /// Merge two `EditedText` instances. The two instances must be derived - /// from the same original text. The operations are merged using the - /// principles of Operational Transformation. The cursors are updated - /// accordingly to reflect the changes made by the merged operations. - /// - /// # Panics - /// - /// Panics if there's an integer overflow (in isize) when calculating new - /// cursor positions. #[must_use] - #[allow(clippy::too_many_lines)] pub fn merge(self, other: Self) -> Self { debug_assert_eq!( self.text, other.text, @@ -118,8 +99,6 @@ where let mut merged_operations: Vec> = Vec::with_capacity(self.operations.len() + other.operations.len()); - let mut merged_operation_sides: Vec = - Vec::with_capacity(self.operations.len() + other.operations.len()); let mut left_iter = self.operations.into_iter(); let mut right_iter = other.operations.into_iter(); @@ -135,96 +114,83 @@ where let mut last_right_op = None; loop { - let (side, operation) = match (maybe_left_op.as_ref(), maybe_right_op.as_ref()) { - (Some(left_op), Some(right_op)) => { - if left_op.cmp_priority(seen_left_length, right_op, seen_right_length) - == std::cmp::Ordering::Less - { - (Side::Left, maybe_left_op.take().unwrap()) - } else { - (Side::Right, maybe_right_op.take().unwrap()) + let (side, operation, mut last_other_op) = + match (maybe_left_op.clone(), maybe_right_op.clone()) { + (Some(left_op), Some(right_op)) => { + if left_op + .get_sort_key(seen_left_length) + .partial_cmp(&right_op.get_sort_key(seen_right_length)) + == Some(std::cmp::Ordering::Less) + { + (Side::Left, left_op, last_right_op.clone()) + } else { + (Side::Right, right_op, last_left_op.clone()) + } } - } - (Some(_), None) => (Side::Left, maybe_left_op.take().unwrap()), - (None, Some(_)) => (Side::Right, maybe_right_op.take().unwrap()), - (None, None) => break, - }; + (Some(left_op), None) => (Side::Left, left_op, last_right_op.clone()), + (None, Some(right_op)) => (Side::Right, right_op, last_left_op.clone()), + (None, None) => break, + }; let is_advancing_operation = matches!( operation, Operation::Insert { .. } | Operation::Equal { .. } ); - let original_length = operation.len(); - let (side, result) = match side { + let original_length = operation.len() as i64; + let result = match side { Side::Left => { - let result = operation.merge_operations(last_right_op.as_ref()); + let result = operation.merge_operations(&mut last_other_op); if let ref op @ (Operation::Insert { .. } | Operation::Equal { .. }) = result { - let merged_length_signed = isize::try_from(merged_length) - .expect("merged_length must fit in isize"); - let seen_left_length_signed = isize::try_from(seen_left_length) - .expect("seen_left_length must fit in isize"); - let op_len_signed = - isize::try_from(op.len()).expect("op.len() must fit in isize"); - let original_length_signed = isize::try_from(original_length) - .expect("original_length must fit in isize"); - - let shift = merged_length_signed - seen_left_length_signed + op_len_signed - - original_length_signed; + let shift = merged_length as i64 - seen_left_length as i64 + + op.len() as i64 + - original_length; while let Some(cursor) = left_cursors.next_if(|cursor| { - cursor.char_index <= seen_left_length + original_length + cursor.char_index <= seen_left_length + original_length as usize }) { merged_cursors.push( - cursor.with_index(cursor.char_index.saturating_add_signed(shift)), + cursor.with_index((cursor.char_index as i64 + shift) as usize), ); } } if is_advancing_operation { - seen_left_length += original_length; + seen_left_length += original_length as usize; } maybe_left_op = left_iter.next(); last_left_op = Some(result.clone()); - (Side::Left, result) + result } Side::Right => { - let result = operation.merge_operations(last_left_op.as_ref()); + let result = operation.merge_operations(&mut last_other_op); if let ref op @ (Operation::Insert { .. } | Operation::Equal { .. }) = result { - let merged_length_signed = isize::try_from(merged_length) - .expect("merged_length must fit in isize"); - let seen_right_length_signed = isize::try_from(seen_right_length) - .expect("seen_right_length must fit in isize"); - let op_len_signed = - isize::try_from(op.len()).expect("op.len() must fit in isize"); - let original_length_signed = isize::try_from(original_length) - .expect("original_length must fit in isize"); - - let shift = merged_length_signed - seen_right_length_signed + op_len_signed - - original_length_signed; + let shift = merged_length as i64 - seen_right_length as i64 + + op.len() as i64 + - original_length; while let Some(cursor) = right_cursors.next_if(|cursor| { - cursor.char_index <= seen_right_length + original_length + cursor.char_index <= seen_right_length + original_length as usize }) { merged_cursors.push( - cursor.with_index(cursor.char_index.saturating_add_signed(shift)), + cursor.with_index((cursor.char_index as i64 + shift) as usize), ); } } if is_advancing_operation { - seen_right_length += original_length; + seen_right_length += original_length as usize; } maybe_right_op = right_iter.next(); last_right_op = Some(result.clone()); - (Side::Right, result) + result } }; @@ -237,306 +203,25 @@ where } merged_operations.push(result); - merged_operation_sides.push(side); } for cursor in left_cursors.chain(right_cursors) { merged_cursors.push(cursor.with_index(merged_length)); } - debug_assert_eq!(merged_operations.len(), merged_operation_sides.len()); - - Self::new( - self.text, - merged_operations, - merged_operation_sides, - merged_cursors, - ) + Self::new(self.text, merged_operations, merged_cursors) } - /// Apply the operations to the text and return the resulting text + /// Apply the operations to the text and return the resulting text. #[must_use] - pub fn apply(&self) -> TextWithCursors { + pub fn apply(&self) -> String { let mut builder: StringBuilder<'_> = StringBuilder::new(self.text); for operation in &self.operations { builder = operation.apply(builder); } - TextWithCursors::new(builder.take(), self.cursors.clone()) - } - - /// Apply the operations to the text and return the resulting text in chunks - /// together with the provenance describing where each chunk came from. - /// - /// Returns all spans including deletions (not present in the merged text). - /// - /// ``` - /// use reconcile_text::{History, SpanWithHistory, BuiltinTokenizer, reconcile}; - /// - /// let parent = "Merging text is hard!"; - /// let left = "Merging text is easy!"; // Changed "hard" to "easy" - /// let right = "With reconcile, merging documents is hard!"; // Added prefix and changed word - /// - /// let result = reconcile( - /// parent, - /// &left.into(), - /// &right.into(), - /// &*BuiltinTokenizer::Word, - /// ); - /// - /// assert_eq!( - /// result.apply_with_history(), - /// vec![ - /// SpanWithHistory::new("Merging text".to_string(), History::RemovedFromRight,), - /// SpanWithHistory::new( - /// "With reconcile, merging documents".to_string(), - /// History::AddedFromRight, - /// ), - /// SpanWithHistory::new(" ".to_string(), History::Unchanged,), - /// SpanWithHistory::new("is".to_string(), History::Unchanged,), - /// SpanWithHistory::new(" hard!".to_string(), History::RemovedFromLeft,), - /// SpanWithHistory::new(" easy!".to_string(), History::AddedFromLeft,), - /// ] - /// ); - /// ``` - #[must_use] - pub fn apply_with_history(&self) -> Vec { - let chars: Vec = self.text.chars().collect(); - let mut builder: StringBuilder<'_> = StringBuilder::new(self.text); - - let mut history = Vec::with_capacity(self.operations.len()); - - for (operation, side) in self.operations.iter().zip(self.operation_sides.iter()) { - builder = operation.apply(builder); - - match operation { - Operation::Equal { .. } => { - history.push(SpanWithHistory::new(builder.take(), History::Unchanged)); - } - Operation::Insert { .. } => { - let h = match side { - Side::Left => History::AddedFromLeft, - Side::Right => History::AddedFromRight, - }; - history.push(SpanWithHistory::new(builder.take(), h)); - } - Operation::Delete { - deleted_character_count, - order, - .. - } => { - let deleted: String = chars[*order..*order + *deleted_character_count] - .iter() - .collect(); - let h = match side { - Side::Left => History::RemovedFromLeft, - Side::Right => History::RemovedFromRight, - }; - history.push(SpanWithHistory::new(deleted, h)); - } - } - } - - history - } - - /// Apply the operations and return both the merged text with cursors and - /// the provenance history in a single pass - #[must_use] - pub fn apply_with_all(&self) -> (TextWithCursors, Vec) { - let chars: Vec = self.text.chars().collect(); - let mut builder: StringBuilder<'_> = StringBuilder::new(self.text); - let mut history = Vec::with_capacity(self.operations.len()); - let mut full_text = String::new(); - - for (operation, side) in self.operations.iter().zip(self.operation_sides.iter()) { - builder = operation.apply(builder); - - match operation { - Operation::Equal { .. } => { - let span = builder.take(); - full_text.push_str(&span); - history.push(SpanWithHistory::new(span, History::Unchanged)); - } - Operation::Insert { .. } => { - let span = builder.take(); - full_text.push_str(&span); - let h = match side { - Side::Left => History::AddedFromLeft, - Side::Right => History::AddedFromRight, - }; - history.push(SpanWithHistory::new(span, h)); - } - Operation::Delete { - deleted_character_count, - order, - .. - } => { - let deleted: String = chars[*order..*order + *deleted_character_count] - .iter() - .collect(); - let h = match side { - Side::Left => History::RemovedFromLeft, - Side::Right => History::RemovedFromRight, - }; - history.push(SpanWithHistory::new(deleted, h)); - } - } - } - - ( - TextWithCursors::new(full_text, self.cursors.clone()), - history, - ) - } - - /// Convert the `EditedText` into a terse representation ready for - /// serialization. The result omits cursor positions and the original text. - /// This is useful for sending text diffs over the network if there's a - /// clear consensus on the original text. - /// - /// Inserts are strings, deletes are negative integers (character count), - /// and retained spans are positive integers (character count). - /// - /// # Errors - /// - /// Returns `DiffError::IntegerOverflow` if a character count exceeds - /// `i64::MAX`. - pub fn to_diff(&self) -> Result, DiffError> { - let mut result: Vec = Vec::with_capacity(self.operations.len()); - let mut previous_equal: Option = None; - - for operation in &self.operations { - match operation { - Operation::Equal { length, .. } => { - if let Some(prev_length) = previous_equal { - previous_equal = Some(prev_length + *length); - } else { - previous_equal = Some(*length); - } - } - - Operation::Insert { text, .. } => { - if let Some(prev_length) = previous_equal { - result - .push(NumberOrText::Number(i64::try_from(prev_length).map_err( - |_| DiffError::IntegerOverflow { value: prev_length }, - )?)); - previous_equal = None; - } - - let text: String = text.iter().map(Token::original).collect(); - result.push(NumberOrText::Text(text)); - } - - Operation::Delete { - deleted_character_count, - .. - } => { - if let Some(prev_length) = previous_equal { - result - .push(NumberOrText::Number(i64::try_from(prev_length).map_err( - |_| DiffError::IntegerOverflow { value: prev_length }, - )?)); - previous_equal = None; - } - - let count = i64::try_from(*deleted_character_count).map_err(|_| { - DiffError::IntegerOverflow { - value: *deleted_character_count, - } - })?; - result.push(NumberOrText::Number(-count)); - } - } - } - - if let Some(prev_length) = previous_equal { - result - .push(NumberOrText::Number(i64::try_from(prev_length).map_err( - |_| DiffError::IntegerOverflow { value: prev_length }, - )?)); - } - - Ok(result) - } - - /// Reconstruct an `EditedText` from a diff and the original text. - /// - /// # Errors - /// - /// Returns `DiffError::LengthExceedsOriginal` if the diff references a - /// range that exceeds the original text length. - /// - /// # Panics - /// - /// Panics if there's an integer overflow in i64. - pub fn from_diff( - original_text: &'a str, - diff: Vec, - tokenizer: &Tokenizer, - ) -> Result, DiffError> { - let mut operations: Vec> = Vec::with_capacity(diff.len()); - let mut order = 0; - let chars: Vec = original_text.chars().collect(); - let text_length = chars.len(); - - for item in diff { - match item { - NumberOrText::Number(length) => { - if length >= 0 { - let length = usize::try_from(length).expect("length must fit in usize"); - - // Validate that the range doesn't exceed the original text - if order + length > text_length { - return Err(DiffError::LengthExceedsOriginal { - position: order, - requested: length, - available: text_length.saturating_sub(order), - }); - } - - let original_characters: String = - chars[order..order + length].iter().collect(); - - let original_tokens = tokenizer(&original_characters); - for token in original_tokens { - operations - .push(Operation::create_equal(order, token.get_original_length())); - order += token.get_original_length(); - } - } else { - let length = - usize::try_from(-length).expect("negative length must fit in usize"); - - // Validate that the delete range doesn't exceed the original text - if order + length > text_length { - return Err(DiffError::LengthExceedsOriginal { - position: order, - requested: length, - available: text_length.saturating_sub(order), - }); - } - - operations.push(Operation::create_delete(order, length)); - order += length; - } - } - NumberOrText::Text(text) => { - let tokens = tokenizer(&text); - operations.push(Operation::create_insert(order, tokens)); - } - } - } - - let operation_count = operations.len(); - Ok(EditedText::new( - original_text, - operations, - vec![Side::Left; operation_count], - vec![], - )) + builder.build() } } @@ -552,24 +237,24 @@ mod tests { let left = "hello world! How are you? Adam"; let right = "Hello, my friend! How are you doing? Albert"; - let operations = EditedText::from_strings(left, &right.into()); + let operations = EditedText::from_strings(left, right.into()); insta::assert_debug_snapshot!(operations); let new_right = operations.apply(); - assert_eq!(new_right.text(), right); + assert_eq!(new_right.to_string(), right); } #[test] fn test_calculate_operations_with_no_diff() { let text = "hello world!"; - let operations = EditedText::from_strings(text, &text.into()); + let operations = EditedText::from_strings(text, text.into()); assert_debug_snapshot!(operations); let new_right = operations.apply(); - assert_eq!(new_right.text(), text); + assert_eq!(new_right.to_string(), text); } #[test] @@ -579,98 +264,10 @@ mod tests { let right = "Hello world! How are you?"; let expected = "Hello world! How are you? I'm Andras."; - let operations_1 = EditedText::from_strings(original, &left.into()); - let operations_2 = EditedText::from_strings(original, &right.into()); + let operations_1 = EditedText::from_strings(original, left.into()); + let operations_2 = EditedText::from_strings(original, right.into()); let operations = operations_1.merge(operations_2); - assert_eq!(operations.apply().text(), expected); - } - - #[test] - fn test_from_diff_length_exceeds_original() { - let result = EditedText::from_diff( - "hello", - vec![ - 10.into(), // too large equal span - should error - " world".into(), - ], - &*BuiltinTokenizer::Word, - ); - - assert!(result.is_err()); - match result { - Err(DiffError::LengthExceedsOriginal { - position, - requested, - available, - }) => { - assert_eq!(position, 0); - assert_eq!(requested, 10); - assert_eq!(available, 5); - } - _ => panic!("Expected LengthExceedsOriginal error"), - } - } - - #[test] - fn test_from_diff_valid() { - let edited_text = EditedText::from_diff( - "hello", - vec![ - 5.into(), // exact length - " world".into(), - ], - &*BuiltinTokenizer::Word, - ) - .unwrap(); - - let content = edited_text.apply().text(); - - assert_eq!(content, "hello world"); - } - - #[cfg(feature = "serde")] - #[test] - fn test_changes_deserialisation() { - let original = "Merging text is hard!"; - let changes = "Merging text is easy with reconcile!"; - let result = EditedText::from_strings(original, &changes.into()); - let serialized = serde_yaml::to_string(&result.to_diff().unwrap()).unwrap(); - - let expected = concat!("- 15\n", "- -6\n", "- ' easy with reconcile!'\n",); - assert_eq!(serialized, expected); - } - - #[test] - fn test_apply_with_history_utf8() { - let parent = "こんにちは世界"; // "Hello World" in Japanese (7 chars, 21 bytes) - let left = "こんにちは宇宙"; // Changed 世界 to 宇宙 - let right = parent; - - let result = crate::reconcile( - parent, - &left.into(), - &right.into(), - &*BuiltinTokenizer::Word, - ); - - let history = result.apply_with_history(); - assert!(!history.is_empty()); - assert_eq!(result.apply().text(), "こんにちは宇宙"); - } - - #[cfg(feature = "serde")] - #[test] - fn test_changes_serialization() { - let original = "The quick brown fox jumps over the lazy dog."; - let updated = "The quick red fox jumped over the very lazy dog!"; - - let edited_text = EditedText::from_strings(original, &updated.into()); - - let changes = edited_text.to_diff().unwrap(); - let deserialized_edited_text = - EditedText::from_diff(original, changes, &*BuiltinTokenizer::Word).unwrap(); - - assert_eq!(deserialized_edited_text.apply().text(), updated); + assert_eq!(operations.apply(), expected); } } diff --git a/src/operation_transformation/operation.rs b/src/operation_transformation/operation.rs index a6e2522..6efaa91 100644 --- a/src/operation_transformation/operation.rs +++ b/src/operation_transformation/operation.rs @@ -11,19 +11,18 @@ use crate::{ }, }; -/// Represents a change that can be applied on a `StringBuilder` +/// Represents a change that can be applied on a `StringBuilder`. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, PartialEq)] pub enum Operation where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { Equal { order: usize, length: usize, #[cfg(debug_assertions)] - #[cfg_attr(feature = "serde", serde(skip_serializing))] text: Option, }, @@ -37,17 +36,17 @@ where deleted_character_count: usize, #[cfg(debug_assertions)] - #[cfg_attr(feature = "serde", serde(skip_serializing))] deleted_text: Option, }, } impl Operation where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { - /// Creates an equal (retain) operation starting at the given character - /// offset in the original text + /// Creates an equal operation with the given index. + /// This operation is used to indicate that the text at the given index + /// is unchanged. pub fn create_equal(order: usize, length: usize) -> Self { Operation::Equal { order, @@ -68,14 +67,13 @@ where } } - /// Creates an insert operation at the given character offset with the - /// given tokens + /// Creates an insert operation with the given index and text. pub fn create_insert(order: usize, text: Vec>) -> Self { Operation::Insert { order, text } } - /// Creates a delete operation at the given character offset for the - /// specified number of characters + /// Creates a delete operation with the given index and number of + /// to-be-deleted characters. pub fn create_delete(order: usize, deleted_character_count: usize) -> Self { Operation::Delete { order, @@ -104,55 +102,28 @@ where } } - fn type_priority(&self) -> u8 { - match self { - Operation::Delete { .. } => 1, - Operation::Insert { .. } => 2, - Operation::Equal { .. } => 3, - } - } - - /// Compare two operations for processing order during merging. Uses - /// (order, type, `insertion_index`) with a deterministic content - /// tiebreaker that avoids allocating. - pub fn cmp_priority( - &self, - self_index: usize, - other: &Self, - other_index: usize, - ) -> std::cmp::Ordering { - self.order() - .cmp(&other.order()) - .then_with(|| self.type_priority().cmp(&other.type_priority())) - .then_with(|| self_index.cmp(&other_index)) - .then_with(|| self.deterministic_content_cmp(other)) - } - - /// Deterministic tiebreaker based on operation content, so that merge - /// results are identical regardless of which side is left vs right - fn deterministic_content_cmp(&self, other: &Self) -> std::cmp::Ordering { - match (self, other) { - (Operation::Insert { text: t1, .. }, Operation::Insert { text: t2, .. }) => { - let s1 = t1.iter().flat_map(|t| t.original().chars()); - let s2 = t2.iter().flat_map(|t| t.original().chars()); - s1.cmp(s2) - } - (Operation::Equal { length: l1, .. }, Operation::Equal { length: l2, .. }) => { - l1.cmp(l2) - } - ( + pub fn get_sort_key(&self, insertion_index: usize) -> (usize, usize, usize, String) { + ( + self.order(), + match self { + Operation::Delete { .. } => 1, + Operation::Insert { .. } => 2, + Operation::Equal { .. } => 3, + }, + insertion_index, + // Make sure that the ordering is deterministic regardless of which text + // is left or right. + match self { + Operation::Equal { length, .. } => length.to_string(), + Operation::Insert { text, .. } => { + text.iter().map(Token::original).collect::() + } Operation::Delete { - deleted_character_count: c1, + deleted_character_count, .. - }, - Operation::Delete { - deleted_character_count: c2, - .. - }, - ) => c1.cmp(c2), - // Different types are already ordered by type_priority - _ => std::cmp::Ordering::Equal, - } + } => deleted_character_count.to_string(), + }, + ) } /// Applies the operation to the given `StringBuilder`, returning the @@ -206,8 +177,8 @@ where builder } - /// Returns the number of affected characters. May be 0 after - /// `merge_operations`. + /// Returns the number of affected characters. It is always greater than 0 + /// because empty operations cannot be created. pub fn len(&self) -> usize { match self { Operation::Equal { length, .. } => *length, @@ -219,10 +190,12 @@ where } } - /// Adjusts this operation based on `previous_operation` from the other side - /// to avoid duplicating or conflicting changes + /// Merges the operation with the given context, producing a new operation + /// and updating the context. This implements a comples FSM that handles + /// the merging of operations in a way that is consistent with the text. + /// The contexts are updated in-place. #[allow(clippy::too_many_lines)] - pub fn merge_operations(self, previous_operation: Option<&Self>) -> Operation { + pub fn merge_operations(self, previous_operation: &mut Option) -> Operation { let operation = self; match (operation, previous_operation) { @@ -261,7 +234,7 @@ where *last_delete_order + *last_delete_deleted_character_count; let new_length = deleted_character_count - .min(operation_end_index.saturating_sub(last_delete_end_index)); + .min(0.max(operation_end_index as i64 - last_delete_end_index as i64) as usize); let overlap = deleted_character_count - new_length; @@ -301,57 +274,43 @@ where let last_delete_end_index = *last_delete_order + *last_delete_deleted_character_count; - let overlap = length.min(last_delete_end_index.saturating_sub(order)); + let overlap = + 0.max((length as i64).min(last_delete_end_index as i64 - order as i64)); #[cfg(debug_assertions)] let updated_equal = text.as_ref().map_or_else( - || Operation::create_equal(order + overlap, length - overlap), + || { + Operation::create_equal( + order + overlap as usize, + (length as i64 - overlap) as usize, + ) + }, |text| { Operation::create_equal_with_text( - order + overlap, - text.chars().skip(overlap).collect::(), + order + overlap as usize, + text.chars().skip(overlap as usize).collect::(), ) }, ); #[cfg(not(debug_assertions))] - let updated_equal = Operation::create_equal(order + overlap, length - overlap); + let updated_equal = Operation::create_equal( + order + overlap as usize, + (length as i64 - overlap) as usize, + ); updated_equal } ( - ref operation @ Operation::Equal { - ref order, - #[cfg(debug_assertions)] - ref text, - .. - }, + ref operation @ Operation::Equal { ref order, .. }, Some(Operation::Equal { order: last_equal_order, length: last_equal_length, - #[cfg(debug_assertions)] - text: last_equal_text, .. }), ) => { if operation.len() == *last_equal_length && *order == *last_equal_order { - // Both sides retained the same span from the original text, - // so we deduplicate by zeroing one out. This is safe because - // both EditedTexts are derived from the same original, and - // matching (order, length) means they cover the same substring - #[cfg(debug_assertions)] - debug_assert_eq!( - text, - last_equal_text, - "Equal operations with same order and length should have the same text, \ - but got {operation:?} vs {:?}", - Operation::::Equal { - order: *last_equal_order, - length: *last_equal_length, - text: last_equal_text.clone(), - }, - ); Operation::create_equal(*order, 0) } else { operation.clone() @@ -365,7 +324,7 @@ where impl Display for Operation where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { @@ -375,25 +334,22 @@ where #[cfg(debug_assertions)] text, - .. } => { #[cfg(debug_assertions)] - { - write!( - f, - "", - text.as_ref() - .map(|text| format!("'{}'", text.replace('\n', "\\n"))) - .unwrap_or(format!("{length} characters")), - ) - } + write!( + f, + "", + text.as_ref() + .map(|text| format!("'{}'", text.replace('\n', "\\n"))) + .unwrap_or(format!("{length} characters")), + )?; #[cfg(not(debug_assertions))] - { - write!(f, "") - } + write!(f, "")?; + + Ok(()) } - Operation::Insert { order, text, .. } => { + Operation::Insert { order, text } => { write!( f, "", @@ -409,27 +365,24 @@ where #[cfg(debug_assertions)] deleted_text, - .. } => { #[cfg(debug_assertions)] - { - write!( - f, - "", - deleted_text - .as_ref() - .map(|text| format!("'{}'", text.replace('\n', "\\n"))) - .unwrap_or(format!("{deleted_character_count} characters")), - ) - } + write!( + f, + "", + deleted_text + .as_ref() + .map(|text| format!("'{}'", text.replace('\n', "\\n"))) + .unwrap_or(format!("{deleted_character_count} characters")), + )?; #[cfg(not(debug_assertions))] - { - write!( - f, - "", - ) - } + write!( + f, + "", + )?; + + Ok(()) } } } @@ -437,11 +390,9 @@ where impl Debug for Operation where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{self}") - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "{self}") } } #[cfg(test)] @@ -459,7 +410,7 @@ mod tests { let mut builder = delete_operation.apply(builder); builder = retain_operation.apply(builder); - assert_eq!(builder.take(), "world"); + assert_eq!(builder.build(), "world"); } #[test] @@ -472,6 +423,6 @@ mod tests { let mut builder = retain_operation.apply(builder); builder = insert_operation.apply(builder); - assert_eq!(builder.take(), "hello my friend"); + assert_eq!(builder.build(), "hello my friend"); } } diff --git a/src/operation_transformation/snapshots/reconcile_text__operation_transformation__edited_text__tests__calculate_operations.snap b/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations.snap similarity index 76% rename from src/operation_transformation/snapshots/reconcile_text__operation_transformation__edited_text__tests__calculate_operations.snap rename to src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations.snap index 0096f0e..abbabbd 100644 --- a/src/operation_transformation/snapshots/reconcile_text__operation_transformation__edited_text__tests__calculate_operations.snap +++ b/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations.snap @@ -1,6 +1,7 @@ --- source: src/operation_transformation/edited_text.rs expression: operations +snapshot_kind: text --- EditedText { text: "hello world! How are you? Adam", @@ -14,15 +15,5 @@ EditedText { , , ], - operation_sides: [ - Left, - Left, - Left, - Left, - Left, - Left, - Left, - Left, - ], cursors: [], } diff --git a/src/operation_transformation/snapshots/reconcile_text__operation_transformation__edited_text__tests__calculate_operations_with_no_diff.snap b/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations_with_no_diff.snap similarity index 78% rename from src/operation_transformation/snapshots/reconcile_text__operation_transformation__edited_text__tests__calculate_operations_with_no_diff.snap rename to src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations_with_no_diff.snap index cf6a674..275a552 100644 --- a/src/operation_transformation/snapshots/reconcile_text__operation_transformation__edited_text__tests__calculate_operations_with_no_diff.snap +++ b/src/operation_transformation/snapshots/reconcile__operation_transformation__edited_text__tests__calculate_operations_with_no_diff.snap @@ -1,6 +1,7 @@ --- source: src/operation_transformation/edited_text.rs expression: operations +snapshot_kind: text --- EditedText { text: "hello world!", @@ -9,10 +10,5 @@ EditedText { , , ], - operation_sides: [ - Left, - Left, - Left, - ], cursors: [], } diff --git a/src/operation_transformation/snapshots/reconcile__operations__edited_text__tests__calculate_operations.snap b/src/operation_transformation/snapshots/reconcile__operations__edited_text__tests__calculate_operations.snap new file mode 100644 index 0000000..02956ef --- /dev/null +++ b/src/operation_transformation/snapshots/reconcile__operations__edited_text__tests__calculate_operations.snap @@ -0,0 +1,61 @@ +--- +source: reconcile/src/operations/edited_text.rs +expression: operations +snapshot_kind: text +--- +EditedText { + text: "hello world! How are you? Adam", + operations: [ + OrderedOperation { + order: 0, + operation: Insert { + index: 0, + text: "Hello, my friend! ", + }, + }, + OrderedOperation { + order: 0, + operation: Delete { + index: 18, + deleted_character_count: 13, + deleted_text: Some( + "hello world! ", + ), + }, + }, + OrderedOperation { + order: 21, + operation: Delete { + index: 26, + deleted_character_count: 5, + deleted_text: Some( + "you? ", + ), + }, + }, + OrderedOperation { + order: 26, + operation: Delete { + index: 26, + deleted_character_count: 5, + deleted_text: Some( + " Adam", + ), + }, + }, + OrderedOperation { + order: 31, + operation: Insert { + index: 26, + text: "you ", + }, + }, + OrderedOperation { + order: 31, + operation: Insert { + index: 30, + text: "doing? Albert", + }, + }, + ], +} diff --git a/src/operation_transformation/snapshots/reconcile__operations__operation_sequence__tests__calculate_operations.snap b/src/operation_transformation/snapshots/reconcile__operations__operation_sequence__tests__calculate_operations.snap new file mode 100644 index 0000000..1ba5156 --- /dev/null +++ b/src/operation_transformation/snapshots/reconcile__operations__operation_sequence__tests__calculate_operations.snap @@ -0,0 +1,60 @@ +--- +source: reconcile/src/operations/operation_sequence.rs +expression: operations +snapshot_kind: text +--- +EditedText { + operations: [ + OrderedOperation { + order: 0, + operation: Insert { + index: 0, + text: "Hello, my friend! ", + }, + }, + OrderedOperation { + order: 0, + operation: Delete { + index: 18, + deleted_character_count: 13, + deleted_text: Some( + "hello world! ", + ), + }, + }, + OrderedOperation { + order: 21, + operation: Delete { + index: 26, + deleted_character_count: 5, + deleted_text: Some( + "you? ", + ), + }, + }, + OrderedOperation { + order: 26, + operation: Delete { + index: 26, + deleted_character_count: 5, + deleted_text: Some( + " Adam", + ), + }, + }, + OrderedOperation { + order: 31, + operation: Insert { + index: 26, + text: "you ", + }, + }, + OrderedOperation { + order: 31, + operation: Insert { + index: 30, + text: "doing? Albert", + }, + }, + ], +} diff --git a/src/operation_transformation/utils/cook_operations.rs b/src/operation_transformation/utils/cook_operations.rs index 192dae1..7d5f85e 100644 --- a/src/operation_transformation/utils/cook_operations.rs +++ b/src/operation_transformation/utils/cook_operations.rs @@ -1,13 +1,11 @@ -use std::fmt::Debug; - -use crate::{operation_transformation::Operation, raw_operation::RawOperation}; +use crate::{diffs::raw_operation::RawOperation, operation_transformation::Operation}; /// Turn raw operations into ordered operations while keeping track of the -/// original token's indexes +/// original token's indexes. pub fn cook_operations(raw_operations: I) -> impl Iterator> where I: IntoIterator>, - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { let mut original_text_index = 0; // this is the start index of the operation on the original text diff --git a/src/operation_transformation/utils/elongate_operations.rs b/src/operation_transformation/utils/elongate_operations.rs index fd388be..1a218c6 100644 --- a/src/operation_transformation/utils/elongate_operations.rs +++ b/src/operation_transformation/utils/elongate_operations.rs @@ -1,7 +1,6 @@ use core::iter; -use std::fmt::Debug; -use crate::raw_operation::RawOperation; +use crate::diffs::raw_operation::RawOperation; /// Elongates the operations by merging adjacent insertions and deletions that /// can be joined. This makes the subsequent merging of operations more @@ -9,7 +8,7 @@ use crate::raw_operation::RawOperation; pub fn elongate_operations(raw_operations: I) -> Vec> where I: IntoIterator>, - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { // This might look bad, but this makes sense. The inserts and deltes can be // interleaved, such as: IDIDID and we need to turn this into IIIDDD. @@ -25,7 +24,7 @@ where .flat_map(|next| match next { RawOperation::Insert(..) => match maybe_previous_insert.take() { Some(prev) if prev.is_right_joinable() && next.is_left_joinable() => { - maybe_previous_insert = Some(prev.join(next)); + maybe_previous_insert = Some(prev.extend(next)); Box::new(iter::empty()) as Box>> } prev => { @@ -35,7 +34,7 @@ where }, RawOperation::Delete(..) => match maybe_previous_delete.take() { Some(prev) if prev.is_right_joinable() && next.is_left_joinable() => { - maybe_previous_delete = Some(prev.join(next)); + maybe_previous_delete = Some(prev.extend(next)); Box::new(iter::empty()) as Box>> } prev => { @@ -79,7 +78,7 @@ mod tests { } fn ins_custom(text: &str, lj: bool, rj: bool) -> RawOperation { - RawOperation::Insert(vec![Token::new(text.to_owned(), text.to_owned(), lj, rj)]) + RawOperation::Insert(vec![Token::new(text.to_string(), text.to_string(), lj, rj)]) } #[test] @@ -89,10 +88,7 @@ mod tests { assert_eq!(result.len(), 1); match &result[0] { RawOperation::Insert(tokens) => { - let originals: String = tokens - .iter() - .map(crate::tokenizer::token::Token::original) - .collect(); + let originals: String = tokens.iter().map(|t| t.original()).collect(); assert_eq!(originals, "abc"); } _ => panic!("Expected single Insert operation"), diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 4507c9a..608fe93 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1,53 +1,7 @@ -mod character_tokenizer; -mod line_tokenizer; -mod markdown_tokenizer; -mod word_tokenizer; - -use std::ops::Deref; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; use token::Token; -#[cfg(feature = "wasm")] -use wasm_bindgen::prelude::*; pub mod token; +pub mod word_tokenizer; -/// Type alias for tokenizer functions that split a string into tokens +/// A trait for tokenizers that take a string and return a list of tokens. pub type Tokenizer = dyn Fn(&str) -> Vec>; - -#[cfg_attr(feature = "wasm", wasm_bindgen)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg(feature = "wasm")] -pub enum BuiltinTokenizer { - Character = "Character", - Line = "Line", - Markdown = "Markdown", - Word = "Word", -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg(not(feature = "wasm"))] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum BuiltinTokenizer { - Character, - Line, - Markdown, - Word, -} - -impl Deref for BuiltinTokenizer { - type Target = Tokenizer; - - fn deref(&self) -> &Self::Target { - match self { - BuiltinTokenizer::Character => &character_tokenizer::character_tokenizer, - BuiltinTokenizer::Line => &line_tokenizer::line_tokenizer, - BuiltinTokenizer::Markdown => &markdown_tokenizer::markdown_tokenizer, - BuiltinTokenizer::Word => &word_tokenizer::word_tokenizer, - #[cfg(feature = "wasm")] - BuiltinTokenizer::__Invalid => panic!("Unexpected tokenizer type"), - } - } -} diff --git a/src/tokenizer/character_tokenizer.rs b/src/tokenizer/character_tokenizer.rs deleted file mode 100644 index 7f12cac..0000000 --- a/src/tokenizer/character_tokenizer.rs +++ /dev/null @@ -1,26 +0,0 @@ -use super::token::Token; - -/// Splits text into UTF-8 characters -/// -/// ```not_rust -/// "Hey!" -> ["H", "e", "y", "!"] -/// ``` -pub fn character_tokenizer(text: &str) -> Vec> { - text.chars() - .map(|char| Token::new(char.to_string(), char.to_string(), true, true)) - .collect() -} - -#[cfg(test)] -mod tests { - use insta::assert_debug_snapshot; - - use super::*; - - #[test] - fn test_with_snapshots() { - assert_debug_snapshot!(character_tokenizer("")); - - assert_debug_snapshot!(character_tokenizer(" hello, \nwhere are you?")); - } -} diff --git a/src/tokenizer/line_tokenizer.rs b/src/tokenizer/line_tokenizer.rs deleted file mode 100644 index 64e337b..0000000 --- a/src/tokenizer/line_tokenizer.rs +++ /dev/null @@ -1,80 +0,0 @@ -use super::token::Token; - -/// Splits text into lines, preserving line endings as separate tokens -/// -/// ## Example -/// -/// ```not_rust -/// "Hello\nWorld!" -> ["Hello", "\n", "World!"] -/// "Line 1\r\nLine 2" -> ["Line 1", "\r\n", "Line 2"] -/// ``` -pub fn line_tokenizer(text: &str) -> Vec> { - let mut result = Vec::new(); - let mut line_start = 0; - - let mut chars = text.char_indices().peekable(); - while let Some((i, c)) = chars.next() { - if c == '\n' { - // Add line content if any - if i > line_start { - result.push(text[line_start..i].into()); - } - // Add newline - result.push("\n".into()); - line_start = i + 1; - } else if c == '\r' { - if i > line_start { - result.push(text[line_start..i].into()); - } - if chars.peek() == Some(&(i + 1, '\n')) { - // Handle \r\n - chars.next(); // consume \n - result.push("\r\n".into()); - line_start = i + 2; - } else { - // Handle bare \r - result.push("\r".into()); - line_start = i + 1; - } - } - } - - // Add final line if any - if line_start < text.len() { - result.push(text[line_start..].into()); - } - - result -} - -#[cfg(test)] -mod tests { - use insta::assert_debug_snapshot; - - use super::*; - - #[test] - fn test_with_snapshots() { - assert_debug_snapshot!(line_tokenizer("")); - - assert_debug_snapshot!(line_tokenizer("Hello")); - - assert_debug_snapshot!(line_tokenizer("Hello\nWorld")); - - assert_debug_snapshot!(line_tokenizer("Hello\nWorld\n")); - - assert_debug_snapshot!(line_tokenizer("Line 1\r\nLine 2")); - - assert_debug_snapshot!(line_tokenizer("Multi\nLine\nText\nHere")); - - assert_debug_snapshot!(line_tokenizer("\n")); - - assert_debug_snapshot!(line_tokenizer("\n\n")); - - assert_debug_snapshot!(line_tokenizer("Start\n\nEnd")); - - assert_debug_snapshot!(line_tokenizer("Old\rMac\rStyle")); - - assert_debug_snapshot!(line_tokenizer("Mixed\r\nand\rbare")); - } -} diff --git a/src/tokenizer/markdown_tokenizer.rs b/src/tokenizer/markdown_tokenizer.rs deleted file mode 100644 index b69df8f..0000000 --- a/src/tokenizer/markdown_tokenizer.rs +++ /dev/null @@ -1,290 +0,0 @@ -use super::{token::Token, word_tokenizer::split_words}; - -/// Splits markdown text into tokens that respect markdown formatting structure -/// -/// Builds on word-level tokenization with markdown-specific handling: -/// - Newlines are non-joinable tokens (preserves block structure) -/// - Block-level prefixes (headings, list markers, blockquotes) attach to the -/// first word of their line so they can't be split apart during merge -/// - Intra-line whitespace uses the same normalization as the word tokenizer -/// -/// This prevents merges from breaking lists, headings, or other structural -/// markdown elements. Inline formatting like `**bold**` is already preserved -/// by word-level splitting since formatting markers contain no whitespace. -/// -/// ## Example -/// -/// ```not_rust -/// "# Hello\n- item" -> ["# Hello", "\n", "- item"] -/// ``` -pub fn markdown_tokenizer(text: &str) -> Vec> { - let mut result = Vec::new(); - let segments = split_preserving_newlines(text); - - for segment in &segments { - if *segment == "\n" || *segment == "\r\n" { - let s = (*segment).to_owned(); - result.push(Token::new(s.clone(), s, false, false)); - continue; - } - - let prefix_len = block_prefix_len(segment); - let mut line_tokens = split_words(&segment[prefix_len..]); - - if prefix_len > 0 { - let prefix = &segment[..prefix_len]; - if line_tokens.is_empty() { - let s = prefix.to_owned(); - result.push(Token::new(s.clone(), s, false, false)); - } else { - let first = &line_tokens[0]; - let combined_original = format!("{prefix}{}", first.original()); - let combined_normalized = format!("{prefix}{}", first.normalized()); - line_tokens[0] = Token::new( - combined_normalized, - combined_original, - false, - first.is_right_joinable, - ); - } - } - - result.extend(line_tokens); - } - - // Normalize non-newline whitespace tokens by appending the next token's - // original text (same trick as the word tokenizer so each space is unique - // in the diff based on what follows it) - if !result.is_empty() { - for i in 0..result.len() - 1 { - if result[i] - .original() - .chars() - .all(|c| c.is_whitespace() && c != '\n' && c != '\r') - { - let normalized = result[i].normalized().to_owned() + result[i + 1].original(); - result[i].set_normalized(normalized); - } - } - } - - result -} - -/// Splits text into alternating segments of line content and newline separators -fn split_preserving_newlines(text: &str) -> Vec<&str> { - let mut segments = Vec::new(); - let mut line_start = 0; - let bytes = text.as_bytes(); - let mut i = 0; - - while i < bytes.len() { - if bytes[i] == b'\r' && i + 1 < bytes.len() && bytes[i + 1] == b'\n' { - if i > line_start { - segments.push(&text[line_start..i]); - } - segments.push(&text[i..i + 2]); - i += 2; - line_start = i; - } else if bytes[i] == b'\n' { - if i > line_start { - segments.push(&text[line_start..i]); - } - segments.push(&text[i..=i]); - i += 1; - line_start = i; - } else { - i += 1; - } - } - - if line_start < text.len() { - segments.push(&text[line_start..]); - } - - segments -} - -/// Returns the byte length of a markdown block-level prefix at the start of a -/// line, or 0 if none is found -/// -/// All recognized prefix characters are ASCII, so byte offsets are always -/// valid UTF-8 boundaries. -/// -/// Recognized prefixes: -/// - ATX headings: `# ` through `###### ` -/// - Blockquotes: `> ` (single level) -/// - Unordered lists: `- `, `* `, `+ ` (with optional leading whitespace) -/// - Ordered lists: `1. `, `2) ` etc (with optional leading whitespace) -/// - Task lists: `- [ ] `, `- [x] `, `- [X] ` etc (checkbox included in prefix) -fn block_prefix_len(line: &str) -> usize { - let trimmed = line.trim_start_matches([' ', '\t']); - let indent_len = line.len() - trimmed.len(); - - // ATX heading: #{1,6} followed by a space - if trimmed.starts_with('#') { - let hash_count = trimmed.bytes().take_while(|&b| b == b'#').count(); - if hash_count <= 6 && trimmed.as_bytes().get(hash_count) == Some(&b' ') { - return indent_len + hash_count + 1; - } - } - - // Blockquote: > followed by optional space - if trimmed.starts_with("> ") { - return indent_len + 2; - } - if trimmed.starts_with('>') && (trimmed.len() == 1 || trimmed.as_bytes()[1] == b'>') { - return indent_len + 1; - } - - // Unordered list: [-*+] followed by a space, optionally with task checkbox - if trimmed.len() >= 2 { - let first_byte = trimmed.as_bytes()[0]; - if matches!(first_byte, b'-' | b'*' | b'+') && trimmed.as_bytes()[1] == b' ' { - return indent_len + 2 + task_checkbox_len(&line[indent_len + 2..]); - } - } - - // Ordered list: digits followed by [.)] and a space, optionally with task - // checkbox - let digit_count = trimmed.bytes().take_while(u8::is_ascii_digit).count(); - if digit_count > 0 && indent_len + digit_count + 2 <= line.len() { - let after_digits = trimmed.as_bytes()[digit_count]; - let after_marker = trimmed.as_bytes().get(digit_count + 1); - if matches!(after_digits, b'.' | b')') && after_marker == Some(&b' ') { - return indent_len - + digit_count - + 2 - + task_checkbox_len(&line[indent_len + digit_count + 2..]); - } - } - - 0 -} - -/// Returns the byte length of a task list checkbox (`[ ] `, `[x] `, `[X] `) -/// at the start of `rest`, or 0 if none is found -fn task_checkbox_len(rest: &str) -> usize { - if rest.len() >= 4 - && rest.as_bytes()[0] == b'[' - && matches!(rest.as_bytes()[1], b' ' | b'x' | b'X') - && rest.as_bytes()[2] == b']' - && rest.as_bytes()[3] == b' ' - { - 4 - } else { - 0 - } -} - -#[cfg(test)] -mod tests { - use insta::assert_debug_snapshot; - - use super::*; - - #[test] - fn test_plain_text() { - assert_debug_snapshot!(markdown_tokenizer("Hello world")); - } - - #[test] - fn test_empty() { - assert_debug_snapshot!(markdown_tokenizer("")); - } - - #[test] - fn test_headings() { - assert_debug_snapshot!(markdown_tokenizer("# Hello world")); - assert_debug_snapshot!(markdown_tokenizer("## Sub heading")); - assert_debug_snapshot!(markdown_tokenizer("###### Deep heading")); - } - - #[test] - fn test_unordered_list() { - assert_debug_snapshot!(markdown_tokenizer("- item one\n- item two\n- item three")); - } - - #[test] - fn test_ordered_list() { - assert_debug_snapshot!(markdown_tokenizer("1. first\n2. second\n3. third")); - } - - #[test] - fn test_blockquote() { - assert_debug_snapshot!(markdown_tokenizer("> quoted text\n> more quoted")); - } - - #[test] - fn test_inline_formatting() { - assert_debug_snapshot!(markdown_tokenizer("Some **bold** and *italic* text")); - } - - #[test] - fn test_mixed_content() { - assert_debug_snapshot!(markdown_tokenizer( - "# Title\n\nSome text with **bold**.\n\n- list item\n- another item" - )); - } - - #[test] - fn test_indented_list() { - assert_debug_snapshot!(markdown_tokenizer(" - nested item\n - deeper")); - } - - #[test] - fn test_crlf() { - assert_debug_snapshot!(markdown_tokenizer("Line 1\r\nLine 2")); - } - - #[test] - fn test_code_fence() { - assert_debug_snapshot!(markdown_tokenizer("```rust\nlet x = 1;\n```")); - } - - #[test] - fn test_heading_only() { - assert_debug_snapshot!(markdown_tokenizer("# ")); - } - - #[test] - fn test_link() { - assert_debug_snapshot!(markdown_tokenizer("Click [here](https://example.com) now")); - } - - #[test] - fn test_multiline_paragraph() { - assert_debug_snapshot!(markdown_tokenizer( - "First line\nSecond line\n\nNew paragraph" - )); - } - - #[test] - fn test_list_with_star_marker() { - assert_debug_snapshot!(markdown_tokenizer("* item one\n* item two")); - } - - #[test] - fn test_bold_not_confused_with_list() { - assert_debug_snapshot!(markdown_tokenizer("**bold text**")); - } - - #[test] - fn test_task_list() { - assert_debug_snapshot!(markdown_tokenizer( - "- [ ] todo\n- [x] done\n- [X] also done" - )); - } - - #[test] - fn test_ordered_task_list() { - assert_debug_snapshot!(markdown_tokenizer("1. [ ] first task\n2. [x] second task")); - } - - #[test] - fn test_unicode() { - assert_debug_snapshot!(markdown_tokenizer( - "# \u{1F600} Héllo\n- \u{00E9}lément\n> \u{4F60}\u{597D} world" - )); - } -} diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots-2.snap b/src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-2.snap similarity index 56% rename from src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots-2.snap rename to src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-2.snap index f61f901..892e524 100644 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots-2.snap +++ b/src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-2.snap @@ -1,5 +1,5 @@ --- -source: src/tokenizer/word_tokenizer.rs +source: reconcile/src/tokenizer/word_tokenizer.rs expression: "word_tokenizer(\"\")" snapshot_kind: text --- diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots-3.snap b/src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-3.snap similarity index 74% rename from src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots-3.snap rename to src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-3.snap index 6db03d3..d1c94e1 100644 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots-3.snap +++ b/src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-3.snap @@ -1,23 +1,23 @@ --- -source: src/tokenizer/word_tokenizer.rs +source: reconcile/src/tokenizer/word_tokenizer.rs expression: "word_tokenizer(\" what? \")" snapshot_kind: text --- [ Token { - normalized: " what?", + normalised: " what?", original: " ", is_left_joinable: true, is_right_joinable: true, }, Token { - normalized: "what?", + normalised: "what?", original: "what?", is_left_joinable: true, is_right_joinable: true, }, Token { - normalized: " ", + normalised: " ", original: " ", is_left_joinable: true, is_right_joinable: true, diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots-4.snap b/src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-4.snap similarity index 77% rename from src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots-4.snap rename to src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-4.snap index ed332f1..6740dbc 100644 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots-4.snap +++ b/src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-4.snap @@ -1,53 +1,53 @@ --- -source: src/tokenizer/word_tokenizer.rs +source: reconcile/src/tokenizer/word_tokenizer.rs expression: "word_tokenizer(\" hello, \\nwhere are you?\")" snapshot_kind: text --- [ Token { - normalized: " hello,", + normalised: " hello,", original: " ", is_left_joinable: true, is_right_joinable: true, }, Token { - normalized: "hello,", + normalised: "hello,", original: "hello,", is_left_joinable: true, is_right_joinable: true, }, Token { - normalized: " \nwhere", + normalised: " \nwhere", original: " \n", is_left_joinable: true, is_right_joinable: true, }, Token { - normalized: "where", + normalised: "where", original: "where", is_left_joinable: true, is_right_joinable: true, }, Token { - normalized: " are", + normalised: " are", original: " ", is_left_joinable: true, is_right_joinable: true, }, Token { - normalized: "are", + normalised: "are", original: "are", is_left_joinable: true, is_right_joinable: true, }, Token { - normalized: " you?", + normalised: " you?", original: " ", is_left_joinable: true, is_right_joinable: true, }, Token { - normalized: "you?", + normalised: "you?", original: "you?", is_left_joinable: true, is_right_joinable: true, diff --git a/src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-5.snap b/src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-5.snap new file mode 100644 index 0000000..832147e --- /dev/null +++ b/src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots-5.snap @@ -0,0 +1,39 @@ +--- +source: reconcile/src/tokenizer/word_tokenizer.rs +expression: "word_tokenizer(\" hello, \\nwhere are you?\")" +snapshot_kind: text +--- +[ + Token { + normalised: " ", + original: " ", + }, + Token { + normalised: "hello,", + original: "hello,", + }, + Token { + normalised: " \n", + original: " \n", + }, + Token { + normalised: "where", + original: "where", + }, + Token { + normalised: " ", + original: " ", + }, + Token { + normalised: "are", + original: "are", + }, + Token { + normalised: " ", + original: " ", + }, + Token { + normalised: "you?", + original: "you?", + }, +] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots.snap b/src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots.snap similarity index 74% rename from src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots.snap rename to src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots.snap index 8a2b0ec..95c8db5 100644 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__word_tokenizer__tests__with_snapshots.snap +++ b/src/tokenizer/snapshots/reconcile__tokenizer__word_tokenizer__tests__with_snapshots.snap @@ -1,23 +1,23 @@ --- -source: src/tokenizer/word_tokenizer.rs +source: reconcile/src/tokenizer/word_tokenizer.rs expression: "word_tokenizer(\"Hi there!\")" snapshot_kind: text --- [ Token { - normalized: "Hi", + normalised: "Hi", original: "Hi", is_left_joinable: true, is_right_joinable: true, }, Token { - normalized: " there!", + normalised: " there!", original: " ", is_left_joinable: true, is_right_joinable: true, }, Token { - normalized: "there!", + normalised: "there!", original: "there!", is_left_joinable: true, is_right_joinable: true, diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__character_tokenizer__tests__with_snapshots-2.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__character_tokenizer__tests__with_snapshots-2.snap deleted file mode 100644 index 6c45107..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__character_tokenizer__tests__with_snapshots-2.snap +++ /dev/null @@ -1,145 +0,0 @@ ---- -source: src/tokenizer/character_tokenizer.rs -expression: "character_tokenizer(\" hello, \\nwhere are you?\")" -snapshot_kind: text ---- -[ - Token { - normalized: " ", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "h", - original: "h", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "e", - original: "e", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "l", - original: "l", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "l", - original: "l", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "o", - original: "o", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: ",", - original: ",", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " ", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "w", - original: "w", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "h", - original: "h", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "e", - original: "e", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "r", - original: "r", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "e", - original: "e", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " ", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "a", - original: "a", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "r", - original: "r", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "e", - original: "e", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " ", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "y", - original: "y", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "o", - original: "o", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "u", - original: "u", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "?", - original: "?", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__character_tokenizer__tests__with_snapshots.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__character_tokenizer__tests__with_snapshots.snap deleted file mode 100644 index c7763ed..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__character_tokenizer__tests__with_snapshots.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: src/tokenizer/character_tokenizer.rs -expression: "character_tokenizer(\"\")" -snapshot_kind: text ---- -[] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-10.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-10.snap deleted file mode 100644 index 22daf9f..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-10.snap +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: src/tokenizer/line_tokenizer.rs -expression: "line_tokenizer(\"Old\\rMac\\rStyle\")" ---- -[ - Token { - normalized: "Old", - original: "Old", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\r", - original: "\r", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "Mac", - original: "Mac", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\r", - original: "\r", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "Style", - original: "Style", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-11.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-11.snap deleted file mode 100644 index c48bca5..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-11.snap +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: src/tokenizer/line_tokenizer.rs -expression: "line_tokenizer(\"Mixed\\r\\nand\\rbare\")" ---- -[ - Token { - normalized: "Mixed", - original: "Mixed", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\r\n", - original: "\r\n", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "and", - original: "and", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\r", - original: "\r", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "bare", - original: "bare", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-2.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-2.snap deleted file mode 100644 index 78e67ff..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-2.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: src/tokenizer/line_tokenizer.rs -expression: "line_tokenizer(\"Hello\")" -snapshot_kind: text ---- -[ - Token { - normalized: "Hello", - original: "Hello", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-3.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-3.snap deleted file mode 100644 index 648073a..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-3.snap +++ /dev/null @@ -1,25 +0,0 @@ ---- -source: src/tokenizer/line_tokenizer.rs -expression: "line_tokenizer(\"Hello\\nWorld\")" -snapshot_kind: text ---- -[ - Token { - normalized: "Hello", - original: "Hello", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "World", - original: "World", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-4.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-4.snap deleted file mode 100644 index 5fed364..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-4.snap +++ /dev/null @@ -1,31 +0,0 @@ ---- -source: src/tokenizer/line_tokenizer.rs -expression: "line_tokenizer(\"Hello\\nWorld\\n\")" -snapshot_kind: text ---- -[ - Token { - normalized: "Hello", - original: "Hello", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "World", - original: "World", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-5.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-5.snap deleted file mode 100644 index 2a5cac0..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-5.snap +++ /dev/null @@ -1,25 +0,0 @@ ---- -source: src/tokenizer/line_tokenizer.rs -expression: "line_tokenizer(\"Line 1\\r\\nLine 2\")" -snapshot_kind: text ---- -[ - Token { - normalized: "Line 1", - original: "Line 1", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\r\n", - original: "\r\n", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "Line 2", - original: "Line 2", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-6.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-6.snap deleted file mode 100644 index f42abb2..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-6.snap +++ /dev/null @@ -1,49 +0,0 @@ ---- -source: src/tokenizer/line_tokenizer.rs -expression: "line_tokenizer(\"Multi\\nLine\\nText\\nHere\")" -snapshot_kind: text ---- -[ - Token { - normalized: "Multi", - original: "Multi", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "Line", - original: "Line", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "Text", - original: "Text", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "Here", - original: "Here", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-7.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-7.snap deleted file mode 100644 index fbbd511..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-7.snap +++ /dev/null @@ -1,13 +0,0 @@ ---- -source: src/tokenizer/line_tokenizer.rs -expression: "line_tokenizer(\"\\n\")" -snapshot_kind: text ---- -[ - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-8.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-8.snap deleted file mode 100644 index 0e55867..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-8.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: src/tokenizer/line_tokenizer.rs -expression: "line_tokenizer(\"\\n\\n\")" -snapshot_kind: text ---- -[ - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-9.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-9.snap deleted file mode 100644 index bb717c4..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots-9.snap +++ /dev/null @@ -1,31 +0,0 @@ ---- -source: src/tokenizer/line_tokenizer.rs -expression: "line_tokenizer(\"Start\\n\\nEnd\")" -snapshot_kind: text ---- -[ - Token { - normalized: "Start", - original: "Start", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "End", - original: "End", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots.snap deleted file mode 100644 index a525ea3..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__line_tokenizer__tests__with_snapshots.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: src/tokenizer/line_tokenizer.rs -expression: "line_tokenizer(\"\")" -snapshot_kind: text ---- -[] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__blockquote.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__blockquote.snap deleted file mode 100644 index 6bbf317..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__blockquote.snap +++ /dev/null @@ -1,48 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"> quoted text\\n> more quoted\")" ---- -[ - Token { - normalized: "> quoted", - original: "> quoted", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " text", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "text", - original: "text", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "> more", - original: "> more", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " quoted", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "quoted", - original: "quoted", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__bold_not_confused_with_list.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__bold_not_confused_with_list.snap deleted file mode 100644 index 92a0ffa..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__bold_not_confused_with_list.snap +++ /dev/null @@ -1,24 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"**bold text**\")" ---- -[ - Token { - normalized: "**bold", - original: "**bold", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " text**", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "text**", - original: "text**", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__code_fence.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__code_fence.snap deleted file mode 100644 index a951f58..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__code_fence.snap +++ /dev/null @@ -1,72 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"```rust\\nlet x = 1;\\n```\")" ---- -[ - Token { - normalized: "```rust", - original: "```rust", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "let", - original: "let", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " x", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "x", - original: "x", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " =", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "=", - original: "=", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " 1;", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "1;", - original: "1;", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "```", - original: "```", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__crlf.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__crlf.snap deleted file mode 100644 index d1817c2..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__crlf.snap +++ /dev/null @@ -1,48 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"Line 1\\r\\nLine 2\")" ---- -[ - Token { - normalized: "Line", - original: "Line", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " 1", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "1", - original: "1", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\r\n", - original: "\r\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "Line", - original: "Line", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " 2", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "2", - original: "2", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__empty.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__empty.snap deleted file mode 100644 index aa65c19..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__empty.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"\")" ---- -[] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__heading_only.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__heading_only.snap deleted file mode 100644 index 0d10e0f..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__heading_only.snap +++ /dev/null @@ -1,12 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"# \")" ---- -[ - Token { - normalized: "# ", - original: "# ", - is_left_joinable: false, - is_right_joinable: false, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__headings-2.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__headings-2.snap deleted file mode 100644 index e255319..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__headings-2.snap +++ /dev/null @@ -1,24 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"## Sub heading\")" ---- -[ - Token { - normalized: "## Sub", - original: "## Sub", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " heading", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "heading", - original: "heading", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__headings-3.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__headings-3.snap deleted file mode 100644 index aa4387c..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__headings-3.snap +++ /dev/null @@ -1,24 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"###### Deep heading\")" ---- -[ - Token { - normalized: "###### Deep", - original: "###### Deep", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " heading", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "heading", - original: "heading", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__headings.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__headings.snap deleted file mode 100644 index 0c3e98e..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__headings.snap +++ /dev/null @@ -1,24 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"# Hello world\")" ---- -[ - Token { - normalized: "# Hello", - original: "# Hello", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " world", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "world", - original: "world", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__indented_list.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__indented_list.snap deleted file mode 100644 index 5ea77f7..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__indented_list.snap +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\" - nested item\\n - deeper\")" ---- -[ - Token { - normalized: " - nested", - original: " - nested", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " item", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "item", - original: "item", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: " - deeper", - original: " - deeper", - is_left_joinable: false, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__inline_formatting.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__inline_formatting.snap deleted file mode 100644 index 6744a2b..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__inline_formatting.snap +++ /dev/null @@ -1,60 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"Some **bold** and *italic* text\")" ---- -[ - Token { - normalized: "Some", - original: "Some", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " **bold**", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "**bold**", - original: "**bold**", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " and", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "and", - original: "and", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " *italic*", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "*italic*", - original: "*italic*", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " text", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "text", - original: "text", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__link.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__link.snap deleted file mode 100644 index 79e3afb..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__link.snap +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"Click [here](https://example.com) now\")" ---- -[ - Token { - normalized: "Click", - original: "Click", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " [here](https://example.com)", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "[here](https://example.com)", - original: "[here](https://example.com)", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " now", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "now", - original: "now", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__list_with_star_marker.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__list_with_star_marker.snap deleted file mode 100644 index 3273a24..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__list_with_star_marker.snap +++ /dev/null @@ -1,48 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"* item one\\n* item two\")" ---- -[ - Token { - normalized: "* item", - original: "* item", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " one", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "one", - original: "one", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "* item", - original: "* item", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " two", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "two", - original: "two", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__mixed_content.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__mixed_content.snap deleted file mode 100644 index 78458af..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__mixed_content.snap +++ /dev/null @@ -1,120 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"# Title\\n\\nSome text with **bold**.\\n\\n- list item\\n- another item\")" ---- -[ - Token { - normalized: "# Title", - original: "# Title", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "Some", - original: "Some", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " text", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "text", - original: "text", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " with", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "with", - original: "with", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " **bold**.", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "**bold**.", - original: "**bold**.", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "- list", - original: "- list", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " item", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "item", - original: "item", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "- another", - original: "- another", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " item", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "item", - original: "item", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__multiline_paragraph.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__multiline_paragraph.snap deleted file mode 100644 index 244f515..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__multiline_paragraph.snap +++ /dev/null @@ -1,78 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"First line\\nSecond line\\n\\nNew paragraph\")" ---- -[ - Token { - normalized: "First", - original: "First", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " line", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "line", - original: "line", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "Second", - original: "Second", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " line", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "line", - original: "line", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "New", - original: "New", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " paragraph", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "paragraph", - original: "paragraph", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__ordered_list.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__ordered_list.snap deleted file mode 100644 index c465053..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__ordered_list.snap +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"1. first\\n2. second\\n3. third\")" ---- -[ - Token { - normalized: "1. first", - original: "1. first", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "2. second", - original: "2. second", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "3. third", - original: "3. third", - is_left_joinable: false, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__ordered_task_list.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__ordered_task_list.snap deleted file mode 100644 index 55a8001..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__ordered_task_list.snap +++ /dev/null @@ -1,48 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"1. [ ] first task\\n2. [x] second task\")" ---- -[ - Token { - normalized: "1. [ ] first", - original: "1. [ ] first", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " task", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "task", - original: "task", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "2. [x] second", - original: "2. [x] second", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " task", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "task", - original: "task", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__plain_text.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__plain_text.snap deleted file mode 100644 index 0785a2f..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__plain_text.snap +++ /dev/null @@ -1,24 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"Hello world\")" ---- -[ - Token { - normalized: "Hello", - original: "Hello", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: " world", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "world", - original: "world", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__task_list.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__task_list.snap deleted file mode 100644 index 9789b13..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__task_list.snap +++ /dev/null @@ -1,48 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"- [ ] todo\\n- [x] done\\n- [X] also done\")" ---- -[ - Token { - normalized: "- [ ] todo", - original: "- [ ] todo", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "- [x] done", - original: "- [x] done", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "- [X] also", - original: "- [X] also", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " done", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "done", - original: "done", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__unicode.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__unicode.snap deleted file mode 100644 index e5d1beb..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__unicode.snap +++ /dev/null @@ -1,60 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"# \\u{1F600} Héllo\\n- \\u{00E9}lément\\n> \\u{4F60}\\u{597D} world\")" ---- -[ - Token { - normalized: "# 😀", - original: "# 😀", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " Héllo", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "Héllo", - original: "Héllo", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "- élément", - original: "- élément", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "> 你好", - original: "> 你好", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " world", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "world", - original: "world", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__unordered_list.snap b/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__unordered_list.snap deleted file mode 100644 index b50f920..0000000 --- a/src/tokenizer/snapshots/reconcile_text__tokenizer__markdown_tokenizer__tests__unordered_list.snap +++ /dev/null @@ -1,72 +0,0 @@ ---- -source: src/tokenizer/markdown_tokenizer.rs -expression: "markdown_tokenizer(\"- item one\\n- item two\\n- item three\")" ---- -[ - Token { - normalized: "- item", - original: "- item", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " one", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "one", - original: "one", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "- item", - original: "- item", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " two", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "two", - original: "two", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "\n", - original: "\n", - is_left_joinable: false, - is_right_joinable: false, - }, - Token { - normalized: "- item", - original: "- item", - is_left_joinable: false, - is_right_joinable: true, - }, - Token { - normalized: " three", - original: " ", - is_left_joinable: true, - is_right_joinable: true, - }, - Token { - normalized: "three", - original: "three", - is_left_joinable: true, - is_right_joinable: true, - }, -] diff --git a/src/tokenizer/token.rs b/src/tokenizer/token.rs index 0f45d41..0c12770 100644 --- a/src/tokenizer/token.rs +++ b/src/tokenizer/token.rs @@ -1,94 +1,68 @@ -use std::{ - fmt::Debug, - hash::{Hash, Hasher}, -}; - #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -/// A token with a normalized form (used for diffing) and an original form -/// (used when applying operations). Joinability flags control whether -/// adjacent insertions interleave or group. +/// A token is a string that has been normalised in some way. /// -/// UTF-8 compatible. +/// A token consists of the normalised form is used for comparison, and the +/// original form used for subsequently applying `Operation`-s to a text +/// document. +/// +/// It's UTF-8 compatible. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone)] pub struct Token where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { - /// The normalized form of the token used deriving the diff - normalized: T, + /// The normalised form of the token used deriving the diff. + normalised: T, - /// The original string, that should be inserted or deleted in the document + /// The original string, that should be inserted or deleted in the document. original: String, - /// Whether the token is semantically joinable with the previous token + /// Whether the token is semantically joinable with the previous token. pub is_left_joinable: bool, - /// Whether the token is semantically joinable with the next token + /// Whether the token is semantically joinable with the next token. pub is_right_joinable: bool, } -/// Trivial implementation of Token when the normalized form is the same as the -/// original string +/// Trivial implementation of Token when the normalised form is the same as the +/// original string. impl From<&str> for Token { - fn from(text: &str) -> Self { - Token::new(text.to_owned(), text.to_owned(), true, true) - } + fn from(text: &str) -> Self { Token::new(text.to_owned(), text.to_owned(), true, true) } } impl Token where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { pub fn new( - normalized: T, + normalised: T, original: String, is_left_joinable: bool, is_right_joinable: bool, ) -> Self { Token { - normalized, + normalised, original, is_left_joinable, is_right_joinable, } } - pub fn original(&self) -> &str { - &self.original - } + pub fn original(&self) -> &str { &self.original } - pub fn set_normalized(&mut self, normalized: T) { - self.normalized = normalized; - } + pub fn set_normalised(&mut self, normalised: T) { self.normalised = normalised; } - pub fn normalized(&self) -> &T { - &self.normalized - } + pub fn normalised(&self) -> &T { &self.normalised } - pub fn get_original_length(&self) -> usize { - self.original.chars().count() - } + pub fn get_original_length(&self) -> usize { self.original.chars().count() } } impl PartialEq for Token where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { - fn eq(&self, other: &Self) -> bool { - self.normalized == other.normalized - } -} - -/// Hashes based on the `normalized` field only, consistent with the -/// [`PartialEq`] implementation. -impl Hash for Token -where - T: PartialEq + Clone + Debug + Hash, -{ - fn hash(&self, state: &mut H) { - self.normalized.hash(state); - } + fn eq(&self, other: &Self) -> bool { self.normalised == other.normalised } } diff --git a/src/tokenizer/word_tokenizer.rs b/src/tokenizer/word_tokenizer.rs index f3f146b..61c3fa3 100644 --- a/src/tokenizer/word_tokenizer.rs +++ b/src/tokenizer/word_tokenizer.rs @@ -1,7 +1,7 @@ use super::token::Token; -/// Splits text on word boundaries, creating tokens of alternating words and -/// whitespace with the whitespace getting unique IDs +/// Splits text on word boundaries creating tokens of alternating words and +/// whitespaces with the whitespaces getting unique IDs. /// /// ## Example /// @@ -9,27 +9,7 @@ use super::token::Token; /// "Hi there!" -> ["Hi", " ", "there!"] /// ``` pub fn word_tokenizer(text: &str) -> Vec> { - let mut result = split_words(text); - - if result.is_empty() { - return result; - } - - // normalize whitespace tokens by concatenating with the following token - for i in 0..result.len() - 1 { - if result[i].original().chars().all(char::is_whitespace) { - let normalized = result[i].normalized().to_owned() + result[i + 1].original(); - result[i].set_normalized(normalized); - } - } - - result -} - -/// Splits text into alternating word and whitespace tokens without any -/// normalization. Shared by `word_tokenizer` and `markdown_tokenizer`. -pub(super) fn split_words(text: &str) -> Vec> { - let mut result = Vec::new(); + let mut result: Vec> = Vec::new(); let mut previous_boundary_index = 0; let mut previous_char_is_whitespace = text.chars().next().is_none_or(char::is_whitespace); @@ -48,6 +28,17 @@ pub(super) fn split_words(text: &str) -> Vec> { result.push(text[previous_boundary_index..].into()); } + if result.is_empty() { + return result; + } + + for i in 0..result.len() - 1 { + if result[i].original().chars().all(char::is_whitespace) { + let normalised = result[i].normalised().to_owned() + result[i + 1].original(); + result[i].set_normalised(normalised); + } + } + result } diff --git a/src/types.rs b/src/types.rs deleted file mode 100644 index 03bb7b6..0000000 --- a/src/types.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod cursor_position; -pub mod history; -pub mod number_or_text; -pub mod side; -pub mod span_with_history; -pub mod text_with_cursors; diff --git a/src/types/cursor_position.rs b/src/types/cursor_position.rs deleted file mode 100644 index c109b5d..0000000 --- a/src/types/cursor_position.rs +++ /dev/null @@ -1,43 +0,0 @@ -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "wasm")] -use wasm_bindgen::prelude::*; - -/// `CursorPosition` represents the position of an identifiable cursor in a text -/// document based on its (UTF-8) character index -#[allow(clippy::unsafe_derive_deserialize)] -#[cfg_attr(feature = "wasm", wasm_bindgen)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Default)] -pub struct CursorPosition { - pub(crate) id: usize, - pub(crate) char_index: usize, -} - -#[cfg_attr(feature = "wasm", wasm_bindgen)] -impl CursorPosition { - #[cfg_attr(feature = "wasm", wasm_bindgen(constructor))] - #[must_use] - pub fn new(id: usize, char_index: usize) -> Self { - Self { id, char_index } - } - - #[must_use] - pub fn with_index(&self, index: usize) -> Self { - CursorPosition { - id: self.id, - char_index: index, - } - } - - #[must_use] - pub fn id(&self) -> usize { - self.id - } - - #[cfg_attr(feature = "wasm", wasm_bindgen(js_name = characterIndex))] - #[must_use] - pub fn char_index(&self) -> usize { - self.char_index - } -} diff --git a/src/types/history.rs b/src/types/history.rs deleted file mode 100644 index 45e3c4e..0000000 --- a/src/types/history.rs +++ /dev/null @@ -1,28 +0,0 @@ -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "wasm")] -use wasm_bindgen::prelude::*; - -#[cfg_attr(feature = "wasm", wasm_bindgen)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg(feature = "wasm")] -pub enum History { - Unchanged = "Unchanged", - AddedFromLeft = "AddedFromLeft", - AddedFromRight = "AddedFromRight", - RemovedFromLeft = "RemovedFromLeft", - RemovedFromRight = "RemovedFromRight", -} - -/// Provenance label for each span returned by `apply_with_history` -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg(not(feature = "wasm"))] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum History { - Unchanged, - AddedFromLeft, - AddedFromRight, - RemovedFromLeft, - RemovedFromRight, -} diff --git a/src/types/number_or_text.rs b/src/types/number_or_text.rs deleted file mode 100644 index 63b7a18..0000000 --- a/src/types/number_or_text.rs +++ /dev/null @@ -1,119 +0,0 @@ -use std::{borrow::Cow, fmt::Debug}; - -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "wasm")] -use wasm_bindgen::prelude::*; - -#[cfg(feature = "wasm")] -#[allow(clippy::cast_precision_loss)] -const INTEGRAL_LIMIT: f64 = (1u64 << 53) as f64; - -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde", serde(untagged))] -#[derive(Debug, Clone, PartialEq)] -pub enum NumberOrText { - Number(i64), - Text(String), -} - -#[cfg(feature = "wasm")] -impl TryFrom for NumberOrText { - type Error = DeserialisationError; - - fn try_from(value: JsValue) -> Result { - if let Ok(num) = value.clone().try_into() { - return Ok(NumberOrText::Number(num)); - } - - if let Some(num) = value.clone().as_f64() { - if num.is_nan() { - return Err(DeserialisationError::new("NaN is not a valid number")); - } - - if num.abs() > INTEGRAL_LIMIT { - return Err(DeserialisationError::new( - "Floating-point number exceeds safe integer limit, use BigInt instead", - )); - } - - #[allow(clippy::cast_possible_truncation)] - return Ok(NumberOrText::Number(num.round() as i64)); - } - - if let Ok(text) = value.try_into() { - return Ok(NumberOrText::Text(text)); - } - - Err(DeserialisationError::new( - "Could not parse JsValue as either number or string", - )) - } -} - -#[cfg(feature = "wasm")] -impl From for JsValue { - fn from(value: NumberOrText) -> Self { - match value { - NumberOrText::Number(num) => JsValue::from(num), - NumberOrText::Text(text) => JsValue::from(text), - } - } -} - -impl From for NumberOrText { - fn from(value: i64) -> Self { - NumberOrText::Number(value) - } -} - -impl From for NumberOrText { - fn from(value: String) -> Self { - NumberOrText::Text(value) - } -} - -impl From<&str> for NumberOrText { - fn from(value: &str) -> Self { - NumberOrText::Text(value.to_owned()) - } -} - -impl<'a> From> for NumberOrText { - fn from(value: Cow<'a, str>) -> Self { - NumberOrText::Text(value.into_owned()) - } -} - -/// Error type for deserialisation failures -#[cfg(feature = "wasm")] -#[derive(Debug, Clone)] -pub struct DeserialisationError { - pub message: String, -} - -#[cfg(feature = "wasm")] -impl DeserialisationError { - pub fn new(message: impl Into) -> Self { - Self { - message: message.into(), - } - } -} - -#[cfg(feature = "wasm")] -impl std::fmt::Display for DeserialisationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Deserialisation error: {}", self.message) - } -} - -#[cfg(feature = "wasm")] -impl std::error::Error for DeserialisationError {} - -#[cfg(feature = "wasm")] -impl From for JsValue { - fn from(error: DeserialisationError) -> Self { - JsValue::from_str(&error.message) - } -} diff --git a/src/types/span_with_history.rs b/src/types/span_with_history.rs deleted file mode 100644 index 1481218..0000000 --- a/src/types/span_with_history.rs +++ /dev/null @@ -1,34 +0,0 @@ -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "wasm")] -use wasm_bindgen::prelude::*; - -use crate::types::history::History; - -/// A text span annotated with its origin in a merge result -#[allow(clippy::unsafe_derive_deserialize)] -#[cfg_attr(feature = "wasm", wasm_bindgen)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq)] -pub struct SpanWithHistory { - text: String, - history: History, -} - -#[cfg_attr(feature = "wasm", wasm_bindgen)] -impl SpanWithHistory { - #[must_use] - pub fn new(text: String, history: History) -> Self { - SpanWithHistory { text, history } - } - - #[must_use] - pub fn history(&self) -> History { - self.history - } - - #[must_use] - pub fn text(&self) -> String { - self.text.clone() - } -} diff --git a/src/types/text_with_cursors.rs b/src/types/text_with_cursors.rs deleted file mode 100644 index d796335..0000000 --- a/src/types/text_with_cursors.rs +++ /dev/null @@ -1,78 +0,0 @@ -#[cfg(feature = "wasm")] -use wasm_bindgen::prelude::*; - -use crate::types::cursor_position::CursorPosition; - -#[cfg_attr(feature = "wasm", wasm_bindgen)] -#[derive(Debug, Clone, PartialEq, Default)] -pub struct TextWithCursors { - text: String, // wasm-pack doesn't support generics so we can't use Cow here - cursors: Vec, -} - -#[cfg_attr(feature = "wasm", wasm_bindgen)] -impl TextWithCursors { - /// # Panics - /// - /// Panics if any cursor's `char_index` exceeds the text's character length. - #[cfg_attr(feature = "wasm", wasm_bindgen(constructor))] - #[must_use] - pub fn new(text: String, cursors: Vec) -> Self { - let length = text.chars().count(); - for cursor in &cursors { - assert!( - cursor.char_index <= length, - // cursor.char_index == length means that the cursor is at the end - "Cursor positions ({}) must be contained within the text (of length {length}) or \ - just after the end", - cursor.char_index - ); - } - - Self { text, cursors } - } - - #[must_use] - pub fn text(&self) -> String { - self.text.clone() - } - - #[must_use] - pub fn cursors(&self) -> Vec { - self.cursors.clone() - } -} - -impl TextWithCursors { - #[must_use] - pub fn text_ref(&self) -> &str { - &self.text - } -} - -impl<'a> From<&'a str> for TextWithCursors { - fn from(text: &'a str) -> Self { - Self { - text: text.into(), - cursors: Vec::new(), - } - } -} - -impl From<&String> for TextWithCursors { - fn from(text: &String) -> Self { - Self { - text: text.to_owned(), - cursors: Vec::new(), - } - } -} - -impl From for TextWithCursors { - fn from(text: String) -> Self { - Self { - text, - cursors: Vec::new(), - } - } -} diff --git a/src/utils.rs b/src/utils.rs index 2e05a70..91330ca 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,5 @@ pub mod common_prefix_len; pub mod common_suffix_len; pub mod find_longest_prefix_contained_within; -pub mod myers_diff; +pub mod side; pub mod string_builder; diff --git a/src/utils/find_longest_prefix_contained_within.rs b/src/utils/find_longest_prefix_contained_within.rs index 69516b4..a04da4e 100644 --- a/src/utils/find_longest_prefix_contained_within.rs +++ b/src/utils/find_longest_prefix_contained_within.rs @@ -1,9 +1,7 @@ -use std::fmt::Debug; - use crate::Token; -/// Given two lists of tokens, returns `length` where the `old` list -/// somewhere within contains the `length` prefix of the `new` list +/// Given two lists of tokens, returns `length` where `old` list somewhere +/// within contains the `length` prefix of the `new` list. /// /// ## Example /// @@ -27,7 +25,7 @@ use crate::Token; /// > results in a length of 1 pub fn find_longest_prefix_contained_within(old: &[Token], new: &[Token]) -> usize where - T: PartialEq + Clone + Debug, + T: PartialEq + Clone + std::fmt::Debug, { let max_possible = new.len().min(old.len()); diff --git a/src/utils/myers_diff.rs b/src/utils/myers_diff.rs deleted file mode 100644 index f04df56..0000000 --- a/src/utils/myers_diff.rs +++ /dev/null @@ -1,440 +0,0 @@ -//! Taken from -//! -//! Myers' diff algorithm. -//! -//! * time: `O((N+M)D)` -//! * space `O(N+M)` -//! -//! See [the original article by Eugene W. Myers](http://www.xmailserver.org/diff2.pdf) -//! describing it. -//! -//! The implementation of this algorithm is based on the implementation by -//! Brandon Williams. -//! -//! # Complexity -//! -//! The worst case (completely dissimilar inputs) is `O((N+M)²)` time. In -//! practice the divide-and-conquer strategy with prefix/suffix stripping keeps -//! subproblems small for typical text. - -use std::{ - fmt::Debug, - ops::{Index, IndexMut, Range}, - vec, -}; - -use crate::{ - raw_operation::RawOperation, - tokenizer::token::Token, - utils::{common_prefix_len::common_prefix_len, common_suffix_len::common_suffix_len}, -}; - -/// Myers' diff algorithm. -/// -/// Diff `old`, between indices `old_range` and `new` between indices -/// `new_range`. -/// -/// The returned `RawOperations` each wrap a single token. -pub fn myers_diff(old: &[Token], new: &[Token]) -> Vec> -where - T: PartialEq + Clone + Debug, -{ - let max_edit_distance = (old.len() + new.len()).div_ceil(2) + 1; - let mut backward_endpoints = FurthestEndpoints::new(max_edit_distance); - let mut forward_endpoints = FurthestEndpoints::new(max_edit_distance); - let mut result = Vec::with_capacity(old.len() + new.len()); - - conquer( - old, - 0..old.len(), - new, - 0..new.len(), - &mut forward_endpoints, - &mut backward_endpoints, - &mut result, - ); - - result -} - -// A D-path is a path which starts at (0,0) that has exactly D non-diagonal -// edges. All D-paths consist of a (D - 1)-path followed by a non-diagonal edge -// and then a possibly empty sequence of diagonal edges called a snake. - -/// Contains the endpoints of the furthest reaching `D-paths`. For each -/// recorded endpoint `(x, y)` on diagonal `k`, we only need to retain `x` -/// because `y` can be computed from `x - k`. In other words, this is an array -/// of integers where `endpoints[k]` contains the row index of the endpoint of -/// the furthest reaching path on diagonal `k`. -/// -/// We can't use a traditional Vec since we use `k` as an index and it can take -/// on negative values. So instead this is a light-weight wrapper around a Vec -/// plus an `offset` which is the maximum value `k` can take on, used to map -/// negative `k`'s back to a value >= 0. -#[derive(Debug)] -struct FurthestEndpoints { - offset: isize, - endpoints: Vec, -} - -impl FurthestEndpoints { - fn new(max_edit_distance: usize) -> Self { - let offset = - isize::try_from(max_edit_distance).expect("max_edit_distance must fit in isize"); - Self { - offset, - endpoints: vec![0; 2 * max_edit_distance + 1], - } - } - - fn len(&self) -> usize { - self.endpoints.len() - } -} - -impl Index for FurthestEndpoints { - type Output = usize; - - fn index(&self, diagonal: isize) -> &Self::Output { - let idx = - usize::try_from(diagonal + self.offset).expect("diagonal + offset must fit in usize"); - &self.endpoints[idx] - } -} - -impl IndexMut for FurthestEndpoints { - fn index_mut(&mut self, diagonal: isize) -> &mut Self::Output { - let idx = - usize::try_from(diagonal + self.offset).expect("diagonal + offset must fit in usize"); - &mut self.endpoints[idx] - } -} - -fn split_at(range: Range, at: usize) -> (Range, Range) { - (range.start..at, at..range.end) -} - -/// Adjust a lower diagonal bound so it has the same parity as `edit_distance`. -/// Diagonals are visited in steps of 2, so `lower` must share `edit_distance`'s -/// parity. -fn align_lower_bound(lower: isize, edit_distance: isize) -> isize { - if (lower & 1) == (edit_distance & 1) { - lower - } else { - lower + 1 - } -} - -/// Adjust an upper diagonal bound so it has the same parity as `edit_distance`. -fn align_upper_bound(upper: isize, edit_distance: isize) -> isize { - if (upper & 1) == (edit_distance & 1) { - upper - } else { - upper - 1 - } -} - -/// A `Snake` is a sequence of diagonal edges in the edit graph. Normally -/// a snake has a start end end point (and it is possible for a snake to have -/// a length of zero, meaning the start and end points are the same) however -/// we do not need the end point which is why it's not implemented here. -/// -/// The divide part of a divide-and-conquer strategy. A D-path has D+1 snakes -/// some of which may be empty. The divide step requires finding the ceil(D/2) + -/// 1 or middle snake of an optimal D-path. The idea for doing so is to -/// simultaneously run the basic algorithm in both the forward and reverse -/// directions until furthest reaching forward and reverse paths starting at -/// opposing corners 'overlap'. -fn find_middle_snake( - old: &[Token], - old_range: Range, - new: &[Token], - new_range: Range, - forward_endpoints: &mut FurthestEndpoints, - backward_endpoints: &mut FurthestEndpoints, -) -> Option<(usize, usize)> -where - T: PartialEq + Clone + Debug, -{ - let old_len = old_range.len(); - let new_len = new_range.len(); - - let old_len_signed = isize::try_from(old_len).expect("old_len must fit in isize"); - let new_len_signed = isize::try_from(new_len).expect("new_len must fit in isize"); - - // By Lemma 1 in the paper, the optimal edit script length is odd or even as - // `delta` is odd or even. - let delta = old_len_signed - new_len_signed; - let delta_is_odd = delta & 1 == 1; - - // The initial point at (0, -1) - forward_endpoints[1] = 0; - // The initial point at (N, M+1) - backward_endpoints[1] = 0; - - let max_edit_distance = (old_len + new_len).div_ceil(2) + 1; - assert!(forward_endpoints.len() >= max_edit_distance); - assert!(backward_endpoints.len() >= max_edit_distance); - - let max_edit_distance_signed = - isize::try_from(max_edit_distance).expect("max_edit_distance must fit in isize"); - - for edit_distance in 0..max_edit_distance_signed { - // Tighter diagonal bounds: on diagonal k = x - y the constraints - // 0 <= x <= old_len and 0 <= y <= new_len give k in [-new_len, old_len]. - // Intersect with the algorithm's [-edit_distance, edit_distance] - // range and snap to the correct parity (k advances in steps of 2). - let forward_diagonal_lo = - align_lower_bound((-edit_distance).max(-new_len_signed), edit_distance); - let forward_diagonal_hi = - align_upper_bound(edit_distance.min(old_len_signed), edit_distance); - - // Forward path - for diagonal in (forward_diagonal_lo..=forward_diagonal_hi).rev().step_by(2) { - let mut old_idx = if diagonal == -edit_distance - || (diagonal != edit_distance - && forward_endpoints[diagonal - 1] < forward_endpoints[diagonal + 1]) - { - forward_endpoints[diagonal + 1] - } else { - forward_endpoints[diagonal - 1] + 1 - }; - let new_idx = usize::try_from( - isize::try_from(old_idx).expect("old_idx must fit in isize") - diagonal, - ) - .expect("old_idx - diagonal must be non-negative and fit in usize"); - - // The coordinate of the start of a snake - let (snake_start_old, snake_start_new) = (old_idx, new_idx); - - // While these sequences are identical, keep moving through the - // graph with no cost - if old_idx < old_range.len() && new_idx < new_range.len() { - let advance = common_prefix_len( - old, - old_range.start + old_idx..old_range.end, - new, - new_range.start + new_idx..new_range.end, - ); - old_idx += advance; - } - - // This is the new best x value - forward_endpoints[diagonal] = old_idx; - - // Only check for connections from the forward search when N - M is - // odd and when there is a reciprocal k line coming from the other - // direction. Forward diagonal k maps to backward diagonal - // (delta - k). Overlap occurs when the combined forward + backward - // reach covers the full width: - // forward_endpoints[k] + backward_endpoints[delta - k] >= old_len. - if delta_is_odd - && (diagonal - delta).abs() <= (edit_distance - 1) - && forward_endpoints[diagonal] + backward_endpoints[-(diagonal - delta)] >= old_len - { - return Some(( - snake_start_old + old_range.start, - snake_start_new + new_range.start, - )); - } - } - - let backward_diagonal_lo = - align_lower_bound((-edit_distance).max(-new_len_signed), edit_distance); - let backward_diagonal_hi = - align_upper_bound(edit_distance.min(old_len_signed), edit_distance); - - // Backward path - for diagonal in (backward_diagonal_lo..=backward_diagonal_hi) - .rev() - .step_by(2) - { - let mut old_idx = if diagonal == -edit_distance - || (diagonal != edit_distance - && backward_endpoints[diagonal - 1] < backward_endpoints[diagonal + 1]) - { - backward_endpoints[diagonal + 1] - } else { - backward_endpoints[diagonal - 1] + 1 - }; - let mut new_idx = usize::try_from( - isize::try_from(old_idx).expect("old_idx must fit in isize") - diagonal, - ) - .expect("old_idx - diagonal must be non-negative and fit in usize"); - - // Extend the snake backward (matching suffix) - if old_idx < old_len && new_idx < new_len { - let advance = common_suffix_len( - old, - old_range.start..old_range.start + old_len - old_idx, - new, - new_range.start..new_range.start + new_len - new_idx, - ); - old_idx += advance; - new_idx += advance; - } - - // This is the new best x value - backward_endpoints[diagonal] = old_idx; - - if !delta_is_odd - && (diagonal - delta).abs() <= edit_distance - && backward_endpoints[diagonal] + forward_endpoints[-(diagonal - delta)] >= old_len - { - return Some(( - old_len - old_idx + old_range.start, - new_len - new_idx + new_range.start, - )); - } - } - } - - None -} - -fn conquer( - old: &[Token], - mut old_range: Range, - new: &[Token], - mut new_range: Range, - forward_endpoints: &mut FurthestEndpoints, - backward_endpoints: &mut FurthestEndpoints, - result: &mut Vec>, -) where - T: PartialEq + Clone + Debug, -{ - // Check for common prefix - let prefix_len = common_prefix_len(old, old_range.clone(), new, new_range.clone()); - if prefix_len > 0 { - result.extend( - old[old_range.start..old_range.start + prefix_len] - .iter() - .map(|token| RawOperation::Equal(vec![token.clone()])), - ); - } - old_range.start += prefix_len; - new_range.start += prefix_len; - - // Check for common suffix - let suffix_len = common_suffix_len(old, old_range.clone(), new, new_range.clone()); - let suffix_start = old_range.end - suffix_len; - old_range.end -= suffix_len; - new_range.end -= suffix_len; - - if old_range.is_empty() && new_range.is_empty() { - // do nothing - } else if new_range.is_empty() { - result.extend( - old[old_range.start..old_range.end] - .iter() - .map(|token| RawOperation::Delete(vec![token.clone()])), - ); - } else if old_range.is_empty() { - result.extend( - new[new_range.start..new_range.end] - .iter() - .map(|token| RawOperation::Insert(vec![token.clone()])), - ); - } else if let Some((split_old, split_new)) = find_middle_snake( - old, - old_range.clone(), - new, - new_range.clone(), - forward_endpoints, - backward_endpoints, - ) { - let (old_before, old_after) = split_at(old_range, split_old); - let (new_before, new_after) = split_at(new_range, split_new); - conquer( - old, - old_before, - new, - new_before, - forward_endpoints, - backward_endpoints, - result, - ); - conquer( - old, - old_after, - new, - new_after, - forward_endpoints, - backward_endpoints, - result, - ); - } else { - result.extend( - old[old_range.start..old_range.end] - .iter() - .map(|token| RawOperation::Delete(vec![token.clone()])), - ); - result.extend( - new[new_range.start..new_range.end] - .iter() - .map(|token| RawOperation::Insert(vec![token.clone()])), - ); - } - - if suffix_len > 0 { - result.extend( - old[suffix_start..suffix_start + suffix_len] - .iter() - .map(|token| RawOperation::Equal(vec![token.clone()])), - ); - } -} - -#[cfg(test)] -mod tests { - use insta::assert_debug_snapshot; - - use super::*; - - #[test] - fn test_empty_diff() { - let old: Vec> = vec![]; - let new: Vec> = vec![]; - let result = myers_diff(&old, &new); - assert_eq!(result.len(), 0); - } - - #[test] - fn test_identical_content() { - let content = vec!["a".into(), "b".into(), "c".into()]; - let result = myers_diff(&content, &content); - assert_debug_snapshot!(result); - } - - #[test] - fn test_insert_only() { - let old: Vec> = vec![]; - let new: Vec> = vec!["a".into(), "b".into()]; - let result = myers_diff(&old, &new); - assert_debug_snapshot!(result); - } - - #[test] - fn test_delete_only() { - let old = vec!["a".into(), "b".into()]; - let new: Vec> = vec![]; - let result = myers_diff(&old, &new); - assert_debug_snapshot!(result); - } - - #[test] - fn test_prefix_and_suffix() { - let old = vec!["a".into(), "b".into(), "c".into(), "d".into()]; - let new = vec!["a".into(), "x".into(), "d".into()]; - let result = myers_diff(&old, &new); - assert_debug_snapshot!(result); - } - - #[test] - fn test_complex_diff() { - let old = vec!["a".into(), "b".into(), "c".into(), "d".into()]; - let new = vec!["a".into(), "x".into(), "c".into(), "y".into()]; - let result = myers_diff(&old, &new); - assert_debug_snapshot!(result); - } -} diff --git a/src/types/side.rs b/src/utils/side.rs similarity index 59% rename from src/types/side.rs rename to src/utils/side.rs index 5716719..825fa9e 100644 --- a/src/types/side.rs +++ b/src/utils/side.rs @@ -1,11 +1,5 @@ use std::fmt::Display; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; - -/// Pretty-printable flag to tell which conflicting edit (side) -/// an operation is associated with -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Side { Left, diff --git a/src/utils/string_builder.rs b/src/utils/string_builder.rs index 3fe42c3..af24d82 100644 --- a/src/utils/string_builder.rs +++ b/src/utils/string_builder.rs @@ -1,32 +1,21 @@ -use std::{fmt, str::Chars}; +use std::iter::Iterator; -/// A helper for building a string sequentially from an original string via -/// insertions, deletions, and copies. All operations use character counts, -/// safe for UTF-8. Methods must be called in-order. +/// A helper for building a string in-order based on an original string and a +/// series of insertions, deletions, and copies applied to it. It is safe to use +/// with UTF-8 strings as all operations are based on character indices. The +/// methods must be called in-order. pub struct StringBuilder<'a> { - original: Chars<'a>, + original: Box + 'a>, buffer: String, #[cfg(debug_assertions)] remaining: String, } -impl fmt::Debug for StringBuilder<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut debug_struct = f.debug_struct("StringBuilder"); - debug_struct.field("buffer", &self.buffer); - - #[cfg(debug_assertions)] - debug_struct.field("remaining", &self.remaining); - - debug_struct.finish_non_exhaustive() - } -} - impl StringBuilder<'_> { pub fn new(original: &str) -> StringBuilder<'_> { StringBuilder { - original: original.chars(), + original: Box::new(original.chars()), buffer: String::with_capacity(original.len()), #[cfg(debug_assertions)] @@ -34,13 +23,11 @@ impl StringBuilder<'_> { } } - /// Insert a string at the end of the built buffer - pub fn insert(&mut self, text: &str) { - self.buffer.push_str(text); - } + /// Insert a string at the end of the built buffer. + pub fn insert(&mut self, text: &str) { self.buffer.push_str(text); } /// Skip copying `length` characters from the original string to the built - /// buffer + /// buffer. pub fn delete(&mut self, length: usize) { if length == 0 { return; @@ -48,32 +35,29 @@ impl StringBuilder<'_> { self.original.nth(length - 1); - #[cfg(debug_assertions)] - { + if cfg!(debug_assertions) { self.remaining = self.remaining.chars().skip(length).collect(); } } - /// Copy `length` characters from the original string to the built buffer + /// Copy `length` characters from the original string to the built buffer. pub fn retain(&mut self, length: usize) { self.buffer.extend(self.original.by_ref().take(length)); - #[cfg(debug_assertions)] - { + if cfg!(debug_assertions) { self.remaining = self.remaining.chars().skip(length).collect(); } } - /// Returns the currently built buffer and clears it to allow consuming - /// the result incrementally. - pub fn take(&mut self) -> String { - std::mem::take(&mut self.buffer) - } + /// Finish building the string after copying the remaining original string + /// since the last insertion or deletion. + pub fn build(self) -> String { self.buffer } + #[cfg(debug_assertions)] /// Get a slice of the remaining original string. The slice starts from /// where the next delete/retain operation would start and is of length - /// `length`. - #[cfg(debug_assertions)] + /// `length`. The implementation is quite suboptimal but it's only used + /// for debugging. pub fn get_slice_from_remaining(&self, length: usize) -> String { let result = self.remaining.chars().take(length).collect::(); @@ -99,7 +83,7 @@ mod tests { builder.retain(8); builder.insert(" eee"); - assert_eq!(builder.take(), "ddd bbb ccc eee"); + assert_eq!(builder.build(), "ddd bbb ccc eee"); let original = "abcde"; let mut builder = StringBuilder::new(original); @@ -108,7 +92,7 @@ mod tests { builder.delete(3); builder.retain(1); - assert_eq!(builder.take(), "ae"); + assert_eq!(builder.build(), "ae"); } #[test] @@ -117,7 +101,7 @@ mod tests { let mut builder = StringBuilder::new(original); builder.insert("test"); - assert_eq!(builder.take(), "test"); + assert_eq!(builder.build(), "test"); } #[test] @@ -129,7 +113,7 @@ mod tests { builder.insert("世界, "); // Insert "World, " builder.retain(2); - assert_eq!(builder.take(), "こんに世界, ちは"); + assert_eq!(builder.build(), "こんに世界, ちは"); } #[test] @@ -152,7 +136,7 @@ mod tests { let mut builder = StringBuilder::new(original); builder.retain(original.len()); - assert_eq!(builder.take(), original); + assert_eq!(builder.build(), original); } #[test] @@ -162,6 +146,6 @@ mod tests { builder.delete(original.len()); builder.insert("Hi"); - assert_eq!(builder.take(), "Hi"); + assert_eq!(builder.build(), "Hi"); } } diff --git a/src/wasm.rs b/src/wasm.rs index 06cccbe..43f39f5 100644 --- a/src/wasm.rs +++ b/src/wasm.rs @@ -1,182 +1,2 @@ -//! Expose the `reconcile` crate's functionality to WebAssembly. -use core::str; - -use wasm_bindgen::prelude::*; - -use crate::{BuiltinTokenizer, CursorPosition, EditedText, SpanWithHistory, TextWithCursors}; - -/// WASM wrapper around `crate::reconcile` for merging text -#[wasm_bindgen(js_name = reconcile)] -#[must_use] -pub fn reconcile( - parent: &str, - left: &TextWithCursors, - right: &TextWithCursors, - tokenizer: BuiltinTokenizer, -) -> TextWithCursors { - set_panic_hook(); - - crate::reconcile(parent, left, right, &*tokenizer).apply() -} - -/// WASM wrapper around `crate::reconcile` that also returns provenance history -#[wasm_bindgen(js_name = reconcileWithHistory)] -#[must_use] -pub fn reconcile_with_history( - parent: &str, - left: &TextWithCursors, - right: &TextWithCursors, - tokenizer: BuiltinTokenizer, -) -> TextWithCursorsAndHistory { - set_panic_hook(); - - let reconciled = crate::reconcile(parent, left, right, &*tokenizer); - let (text_with_cursors, history) = reconciled.apply_with_all(); - - TextWithCursorsAndHistory { - text_with_cursors, - history, - } -} - -/// Merge two documents with a common parent. Relies on `reconcile::reconcile` -/// for texts and returns the right document as-is if either of the updated -/// documents is binary. -/// -/// # Arguments -/// -/// - `parent`: The common parent document -/// - `left`: The left document updated by one user -/// - `right`: The right document updated by another user -/// -/// # Returns -/// -/// The merged document -#[wasm_bindgen(js_name = genericReconcile)] -#[must_use] -pub fn generic_reconcile( - parent: &[u8], - left: &[u8], - right: &[u8], - tokenizer: BuiltinTokenizer, -) -> Vec { - set_panic_hook(); - - if let (Some(parent), Some(left), Some(right)) = ( - string_or_nothing(parent), - string_or_nothing(left), - string_or_nothing(right), - ) { - crate::reconcile(&parent, &left.into(), &right.into(), &*tokenizer) - .apply() - .text() - .into_bytes() - } else { - right.to_vec() - } -} - -/// WASM wrapper around getting a compact diff representation of two texts as a -/// list of numbers and strings -/// -/// # Errors -/// -/// Returns a JS error if integer overflow occurs during diff computation. -#[wasm_bindgen(js_name = diff)] -pub fn diff( - parent: &str, - changed: &TextWithCursors, - tokenizer: BuiltinTokenizer, -) -> Result, JsValue> { - set_panic_hook(); - - let edited_text = EditedText::from_strings_with_tokenizer(parent, changed, &*tokenizer); - edited_text - .to_diff() - .map(|diff| diff.into_iter().map(std::convert::Into::into).collect()) - .map_err(|e| JsValue::from_str(&e.to_string())) -} - -/// Inverse of `diff`, applies a compact diff representation to a parent text -/// -/// # Errors -/// -/// Returns a JS error if the diff format is invalid or references ranges -/// exceeding the original text length. -#[wasm_bindgen(js_name = undiff)] -pub fn undiff( - parent: &str, - diff: Vec, - tokenizer: BuiltinTokenizer, -) -> Result { - set_panic_hook(); - - let parsed_diff: Vec<_> = diff - .into_iter() - .map(std::convert::TryInto::try_into) - .collect::>() - .map_err(|e: crate::types::number_or_text::DeserialisationError| -> JsValue { e.into() })?; - - EditedText::from_diff(parent, parsed_diff, &*tokenizer) - .map(|edited_text| edited_text.apply().text()) - .map_err(|e| JsValue::from_str(&e.to_string())) -} - -fn set_panic_hook() { - // https://github.com/rustwasm/console_error_panic_hook#readme - #[cfg(feature = "console_error_panic_hook")] - console_error_panic_hook::set_once(); -} - -/// WASM wrapper type for the return value of `reconcile_with_history` -#[wasm_bindgen] -#[derive(Debug, Clone, PartialEq, Default)] -pub struct TextWithCursorsAndHistory { - text_with_cursors: TextWithCursors, - history: Vec, -} - -#[wasm_bindgen] -impl TextWithCursorsAndHistory { - #[must_use] - pub fn text(&self) -> String { - self.text_with_cursors.text() - } - - #[must_use] - pub fn cursors(&self) -> Vec { - self.text_with_cursors.cursors() - } - - #[must_use] - pub fn history(&self) -> Vec { - self.history.clone() - } -} - -/// Returns the UTF8 parsed string if it's a text, or `None` if it's likely -/// binary. -#[must_use] -fn string_or_nothing(data: &[u8]) -> Option { - if data.contains(&0) { - // Even though the NUL character is valid in UTF-8, it's highly suspicious in - // human-readable text. - return None; - } - - std::str::from_utf8(data) - .map(std::borrow::ToOwned::to_owned) - .ok() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_string_or_nothing() { - assert_eq!(string_or_nothing(&[0, 159, 146, 150]), None); - assert_eq!(string_or_nothing(&[0, 12]), None); - assert_eq!(string_or_nothing(b"hello"), Some("hello".into())); - } -} +pub mod cursor; +pub mod lib; diff --git a/src/wasm/cursor.rs b/src/wasm/cursor.rs new file mode 100644 index 0000000..9e72095 --- /dev/null +++ b/src/wasm/cursor.rs @@ -0,0 +1,88 @@ +use wasm_bindgen::prelude::*; + +/// Wrapper type to expose `TextWithCursors` to JS. +#[wasm_bindgen] +#[derive(Debug, Clone, PartialEq)] +pub struct JsTextWithCursors { + text: String, + cursors: Vec, +} + +#[wasm_bindgen] +impl JsTextWithCursors { + #[wasm_bindgen(constructor)] + #[must_use] + pub fn new(text: String, cursors: Vec) -> Self { Self { text, cursors } } + + #[must_use] + pub fn text(&self) -> String { self.text.clone() } + + #[must_use] + pub fn cursors(&self) -> Vec { self.cursors.clone() } +} + +impl From for crate::TextWithCursors<'_> { + fn from(owned: JsTextWithCursors) -> Self { + crate::TextWithCursors::new_owned( + owned.text.to_string(), + owned + .cursors + .into_iter() + .map(std::convert::Into::into) + .collect(), + ) + } +} + +impl From> for JsTextWithCursors { + fn from(text_with_cursors: crate::TextWithCursors<'_>) -> Self { + JsTextWithCursors { + text: text_with_cursors.text.into_owned(), + cursors: text_with_cursors + .cursors + .into_iter() + .map(std::convert::Into::into) + .collect(), + } + } +} + +/// Wrapper type to expose `CursorPosition` to JS. +#[wasm_bindgen] +#[derive(Debug, Clone, PartialEq)] +pub struct JsCursorPosition { + id: usize, + char_index: usize, +} + +#[wasm_bindgen] +impl JsCursorPosition { + #[wasm_bindgen(constructor)] + #[must_use] + pub fn new(id: usize, char_index: usize) -> Self { Self { id, char_index } } + + #[must_use] + pub fn id(&self) -> usize { self.id } + + #[wasm_bindgen(js_name = characterPosition)] + #[must_use] + pub fn char_index(&self) -> usize { self.char_index } +} + +impl From for crate::CursorPosition { + fn from(owned: JsCursorPosition) -> Self { + crate::CursorPosition { + id: owned.id, + char_index: owned.char_index, + } + } +} + +impl From for JsCursorPosition { + fn from(cursor: crate::CursorPosition) -> Self { + JsCursorPosition { + id: cursor.id, + char_index: cursor.char_index, + } + } +} diff --git a/src/wasm/lib.rs b/src/wasm/lib.rs new file mode 100644 index 0000000..3188e1f --- /dev/null +++ b/src/wasm/lib.rs @@ -0,0 +1,105 @@ +//! This crate provides utilities for easily communicating between backend & +//! frontend and ensuring the same logic for encoding and decoding binary data, +//! and 3-way-merging documents in Rust and JavaScript. +//! +//! The crate is designed to be used as a Rust library and as a +//! TypeScript/JavaScript package through WebAssembly (WASM). +//! +//! # Modules +//! +//! - `errors`: Contains error types used in this crate. + +use core::str; + +use wasm_bindgen::prelude::*; + +use crate::wasm::cursor::JsTextWithCursors; + +/// Merge two documents with a common parent. Relies on `reconcile::reconcile` +/// for texts and returns the right document as-is if either of the updated +/// documents is binary. +/// +/// # Arguments +/// +/// - `parent`: The common parent document. +/// - `left`: The left document updated by one user. +/// - `right`: The right document updated by another user. +/// +/// # Returns +/// +/// The merged document. +/// +/// # Panics +/// +/// If any of the input documents are not valid UTF-8 strings. +#[wasm_bindgen] +#[must_use] +pub fn merge(parent: &[u8], left: &[u8], right: &[u8]) -> Vec { + set_panic_hook(); + + if is_binary(parent) || is_binary(left) || is_binary(right) { + right.to_vec() + } else { + crate::reconcile( + str::from_utf8(parent).expect("parent must be valid UTF-8 because it's not binary"), + str::from_utf8(left).expect("left must be valid UTF-8 because it's not binary"), + str::from_utf8(right).expect("right must be valid UTF-8 because it's not binary"), + ) + .into_bytes() + } +} + +/// WASM wrapper around `crate::reconcile` for merging text. +#[wasm_bindgen(js_name = mergeText)] +#[must_use] +pub fn merge_text(parent: &str, left: &str, right: &str) -> String { + set_panic_hook(); + + crate::reconcile(parent, left, right) +} + +/// WASM wrapper around `reconcile::reconcile_with_cursors` for merging text. +#[wasm_bindgen(js_name = mergeTextWithCursors)] +#[must_use] +pub fn merge_text_with_cursors( + parent: &str, + left: JsTextWithCursors, + right: JsTextWithCursors, +) -> JsTextWithCursors { + set_panic_hook(); + + crate::reconcile_with_cursors(parent, left.into(), right.into()).into() +} + +/// Heuristically determine if the given data is a binary or a text file's +/// content. +#[wasm_bindgen(js_name = isBinary)] +#[must_use] +pub fn is_binary(data: &[u8]) -> bool { + set_panic_hook(); + + if data.contains(&0) { + // Even though the NUL character is valid in UTF-8, it's highly suspicious in + // human-readable text. + return true; + } + + std::str::from_utf8(data).is_err() +} + +/// We don't want to support merging structured data like JSON, YAML, etc. +#[wasm_bindgen(js_name = isFileTypeMergable)] +#[must_use] +pub fn is_file_type_mergable(path_or_file_name: &str) -> bool { + set_panic_hook(); + + let file_extension = path_or_file_name.split('.').next_back().unwrap_or_default(); + + matches!(file_extension.to_lowercase().as_str(), "md" | "txt") +} + +fn set_panic_hook() { + // https://github.com/rustwasm/console_error_panic_hook#readme + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); +} diff --git a/tests/example_document.rs b/tests/example_document.rs index b16cd44..277e49e 100644 --- a/tests/example_document.rs +++ b/tests/example_document.rs @@ -1,5 +1,5 @@ use pretty_assertions::assert_eq; -use reconcile_text::{CursorPosition, EditedText, TextWithCursors}; +use reconcile::{CursorPosition, TextWithCursors}; use serde::Deserialize; /// `ExampleDocument` represents a test case for the reconciliation process. @@ -18,17 +18,15 @@ pub struct ExampleDocument { impl ExampleDocument { #[must_use] - pub fn parent(&self) -> String { - self.parent.clone() - } + pub fn parent(&self) -> String { self.parent.clone() } #[must_use] - pub fn left(&self) -> TextWithCursors { + pub fn left(&self) -> TextWithCursors<'static> { ExampleDocument::string_to_text_with_cursors(&self.left) } #[must_use] - pub fn right(&self) -> TextWithCursors { + pub fn right(&self) -> TextWithCursors<'static> { ExampleDocument::string_to_text_with_cursors(&self.right) } @@ -39,7 +37,7 @@ impl ExampleDocument { /// /// If the result string does not match the expected string, the program /// will panic. - pub fn assert_eq(&self, result: &EditedText<'_, String>) { + pub fn assert_eq(&self, result: &TextWithCursors<'static>) { let result_str = ExampleDocument::text_with_cursors_to_string(result); assert_eq!( self.expected, result_str, @@ -55,29 +53,27 @@ impl ExampleDocument { /// If the result string does not match the expected string, the program /// will panic. pub fn assert_eq_without_cursors(&self, result: &str) { - let expected = ExampleDocument::string_to_text_with_cursors(&self.expected).text(); + let expected = ExampleDocument::string_to_text_with_cursors(&self.expected).text; assert_eq!( expected, result, "Left (expected) isn't equal to right (actual), Actual: ```\n{result}```", ); } - fn text_with_cursors_to_string(document: &EditedText<'_, String>) -> String { - let merged = document.apply(); - let mut result = merged.text(); - for (i, cursor) in merged.cursors().iter().enumerate() { + fn text_with_cursors_to_string(text: &TextWithCursors<'_>) -> String { + let mut result = text.text.clone().into_owned(); + for (i, cursor) in text.cursors.iter().enumerate() { assert!( - cursor.char_index() <= result.len(), // equals in case of insert at the end - "Cursor index out of bounds: {} > {} when testing for '{}.'", - cursor.char_index(), - result.len(), - result + cursor.char_index <= result.len(), // equals in case of insert at the end + "Cursor index out of bounds: {} > {} when testing for '{result}'", + cursor.char_index, + result.len() ); result.insert( result .char_indices() - .nth(cursor.char_index() + i) + .nth(cursor.char_index + i) .map_or_else(|| result.len(), |(byte_index, _)| byte_index), /* find the utf8 char index of the insert * in byte index */ '|', @@ -86,17 +82,20 @@ impl ExampleDocument { result } - fn string_to_text_with_cursors(text: &str) -> TextWithCursors { + fn string_to_text_with_cursors(text: &str) -> TextWithCursors<'static> { let cursors = Self::parse_cursors(text); let text = text.replace('|', ""); - TextWithCursors::new(text, cursors) + TextWithCursors::new_owned(text, cursors) } fn parse_cursors(text: &str) -> Vec { let mut cursors = Vec::new(); for (i, c) in text.chars().enumerate() { if c == '|' { - cursors.push(CursorPosition::new(0, i - cursors.len())); + cursors.push(CursorPosition { + id: 0, + char_index: i - cursors.len(), + }); } } cursors diff --git a/tests/examples/README.md b/tests/examples/README.md index 15332ed..f5fafa7 100644 --- a/tests/examples/README.md +++ b/tests/examples/README.md @@ -1,16 +1 @@ -# Test Examples - -This directory contains comprehensive YAML test cases that demonstrate various text reconciliation scenarios and edge cases. These examples serve both as regression tests and as documentation of the library's behaviour in different situations. - -## Test Structure - -Each YAML file contains test cases with the following structure: -- `parent`: The original text that both sides diverged from -- `left`: One version of the edited text -- `right`: Another version of the edited text -- `expected`: The expected merged result -- `description`: Human-readable explanation of what the test demonstrates - -## Cursor Position Notation - -In some test cases, the `|` character is used to denote cursor positions within the text. These characters are stripped before the actual reconcile logic is run, making it easier to visualise where cursors should be positioned in the test inputs and expected outputs. +The `|` characters denote cursor positions which are stripped before the actual reconcile logic is run diff --git a/tests/examples/deletes.yml b/tests/examples/deletes.yml index 9aece4c..3476e95 100644 --- a/tests/examples/deletes.yml +++ b/tests/examples/deletes.yml @@ -25,10 +25,10 @@ right: long with big and small expected: long small --- -parent: long run of text where one barely has changes but has cursors -left: long| run of tex|t where one barely has |changes but has |cursors -right: long run one barely has changes cursors -expected: long| ru|n one barely has |changes |cursors +parent: long run of text where one barely has no changes but has cursors +left: long| run of tex|t where one barely has no |changes but has |cursors +right: long run one barely has no changes cursors +expected: long| ru|n one barely has no |changes |cursors --- parent: long text where the cursor has to be clamped after delete diff --git a/tests/examples/various.yml b/tests/examples/various.yml index 4370178..4576edc 100644 --- a/tests/examples/various.yml +++ b/tests/examples/various.yml @@ -9,12 +9,6 @@ left: Party C shall pay Party B right: Party A shall receive from Party B expected: Party C shall receive from Party B ---- -parent: hello -left: hel|lo -right: hi -expected: "|hi" - --- parent: left: hi my friend| diff --git a/tests/test.rs b/tests/test.rs index 68d8762..088a93e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -3,99 +3,49 @@ mod example_document; use std::{fs, path::Path}; use example_document::ExampleDocument; -use reconcile_text::{BuiltinTokenizer, reconcile}; +use reconcile::{reconcile, reconcile_with_cursors}; use serde::Deserialize; #[test] fn test_document_one_way_without_cursors() { for doc in &get_all_documents() { - doc.assert_eq_without_cursors( - &reconcile( - &doc.parent(), - &doc.left().text().into(), - &doc.right().text().into(), - &*BuiltinTokenizer::Word, - ) - .apply() - .text(), - ); + doc.assert_eq_without_cursors(&reconcile( + &doc.parent(), + &doc.left().text, + &doc.right().text, + )); } } #[test] fn test_document_one_way_with_cursors() { for doc in &get_all_documents() { - doc.assert_eq(&reconcile( + doc.assert_eq(&reconcile_with_cursors( &doc.parent(), - &doc.left(), - &doc.right(), - &*BuiltinTokenizer::Word, + doc.left(), + doc.right(), )); } } -#[cfg(feature = "serde")] -#[test] -fn test_document_one_way_with_serialisation() { - use reconcile_text::EditedText; - - for doc in &get_all_documents() { - let parent = doc.parent(); - let left_operations = - EditedText::from_strings_with_tokenizer(&parent, &doc.left(), &*BuiltinTokenizer::Word); - let right_operations = EditedText::from_strings_with_tokenizer( - &parent, - &doc.right(), - &*BuiltinTokenizer::Word, - ); - - let serialised_left = serde_yaml::from_str( - &serde_yaml::to_string(&left_operations.to_diff().unwrap()).unwrap(), - ) - .unwrap(); - let serialised_right = serde_yaml::from_str( - &serde_yaml::to_string(&right_operations.to_diff().unwrap()).unwrap(), - ) - .unwrap(); - - let restored_left_operations = - EditedText::from_diff(&parent, serialised_left, &*BuiltinTokenizer::Word).unwrap(); - let restored_right_operations = - EditedText::from_diff(&parent, serialised_right, &*BuiltinTokenizer::Word).unwrap(); - - doc.assert_eq_without_cursors( - &restored_left_operations - .merge(restored_right_operations) - .apply() - .text(), - ); - } -} - #[test] fn test_document_inverse_way_without_cursors() { for doc in &get_all_documents() { - doc.assert_eq_without_cursors( - &reconcile( - &doc.parent(), - &doc.right().text().into(), - &doc.left().text().into(), - &*BuiltinTokenizer::Word, - ) - .apply() - .text(), - ); + doc.assert_eq_without_cursors(&reconcile( + &doc.parent(), + &doc.right().text, + &doc.left().text, + )); } } #[test] fn test_document_inverse_way_with_cursors() { for doc in &get_all_documents() { - doc.assert_eq(&reconcile( + doc.assert_eq(&reconcile_with_cursors( &doc.parent(), - &doc.right(), - &doc.left(), - &*BuiltinTokenizer::Word, + doc.right(), + doc.left(), )); } } diff --git a/tests/wasm.rs b/tests/wasm.rs deleted file mode 100644 index 82af503..0000000 --- a/tests/wasm.rs +++ /dev/null @@ -1,70 +0,0 @@ -#![cfg(feature = "wasm")] - -use reconcile_text::{BuiltinTokenizer, CursorPosition, TextWithCursors, wasm::*}; -use wasm_bindgen_test::*; - -#[wasm_bindgen_test(unsupported = test)] -fn test_merge() { - let left = b"hello "; - let right = b"world"; - let result = generic_reconcile(b"", left, right, BuiltinTokenizer::Word); - assert_eq!(result, b"hello world"); - - let left = b"\0binary"; - let right = b"other"; - let result = generic_reconcile(b"", left, right, BuiltinTokenizer::Word); - assert_eq!(result, right); -} - -#[wasm_bindgen_test(unsupported = test)] -fn test_merge_text() { - let left = "hello "; - let right = "world"; - let result = reconcile("", &left.into(), &right.into(), BuiltinTokenizer::Word).text(); - assert_eq!(result, "hello world"); -} - -#[wasm_bindgen_test(unsupported = test)] -fn test_merge_text_with_cursors() { - let result = reconcile( - "hi", - &TextWithCursors::new("hi world".to_owned(), vec![]), - &TextWithCursors::new( - "hi".to_owned(), - vec![CursorPosition::new(0, 1), CursorPosition::new(1, 2)], - ), - BuiltinTokenizer::Word, - ); - - assert_eq!( - result, - TextWithCursors::new( - "hi world".to_owned(), - vec![CursorPosition::new(0, 1), CursorPosition::new(1, 2)] - ), - ); -} - -#[wasm_bindgen_test(unsupported = test)] -fn test_merge_binary() { - let left = [0, 1, 2]; - let right = [3, 4, 5]; - assert_eq!( - generic_reconcile(b"", &left, &right, BuiltinTokenizer::Word), - right - ); -} - -#[wasm_bindgen_test] // JsValue isn't supported outside of wasm -fn test_diff() { - let parent = "hello "; - let changed = "world"; - - let result = diff(parent, &changed.into(), BuiltinTokenizer::Word).unwrap(); - - assert_eq!(result.len(), 2); - let first: i64 = result[0].clone().try_into().unwrap(); - let second: String = result[1].clone().try_into().unwrap(); - assert_eq!(first, -6); - assert_eq!(second, "world"); -} diff --git a/tests/web.rs b/tests/web.rs new file mode 100644 index 0000000..a992051 --- /dev/null +++ b/tests/web.rs @@ -0,0 +1,80 @@ +#![cfg(feature = "wasm")] + +use reconcile::wasm::{ + cursor::{JsCursorPosition, JsTextWithCursors}, + lib::{is_binary, is_file_type_mergable, merge, merge_text, merge_text_with_cursors}, +}; +use wasm_bindgen_test::*; + +#[wasm_bindgen_test(unsupported = test)] +fn test_merge() { + let left = b"hello "; + let right = b"world"; + let result = merge(b"", left, right); + assert_eq!(result, b"hello world"); + + let left = b"\0binary"; + let right = b"other"; + let result = merge(b"", left, right); + assert_eq!(result, right); +} + +#[wasm_bindgen_test(unsupported = test)] +fn test_merge_text() { + let left = "hello "; + let right = "world"; + let result = merge_text("", left, right); + assert_eq!(result, "hello world"); +} + +#[wasm_bindgen_test(unsupported = test)] +fn test_merge_text_with_cursors() { + let result = merge_text_with_cursors( + "hi", + JsTextWithCursors::new("hi world".to_owned(), vec![]), + JsTextWithCursors::new( + "hi".to_owned(), + vec![JsCursorPosition::new(0, 1), JsCursorPosition::new(1, 2)], + ), + ); + + assert_eq!( + result, + JsTextWithCursors::new( + "hi world".to_owned(), + vec![JsCursorPosition::new(0, 1), JsCursorPosition::new(1, 2)] + ), + ); +} + +#[wasm_bindgen_test(unsupported = test)] +fn merge_binary() { + let left = [0, 1, 2]; + let right = [3, 4, 5]; + assert_eq!(merge(b"", &left, &right), right); +} + +#[wasm_bindgen_test(unsupported = test)] +fn test_is_binary() { + assert!(is_binary(&[0, 159, 146, 150])); + assert!(is_binary(&[0, 12])); + assert!(!is_binary(b"hello")); +} + +#[wasm_bindgen_test(unsupported = test)] +fn test_is_binary_empty() { + assert!(!is_binary(b"")); +} + +#[wasm_bindgen_test(unsupported = test)] +fn test_is_file_type_mergable() { + assert!(is_file_type_mergable(".md")); + assert!(is_file_type_mergable("hi.md")); + assert!(is_file_type_mergable("my/path/to/my/document.md")); + assert!(is_file_type_mergable("hi.MD")); + assert!(is_file_type_mergable("my/path/to/my/DOCUMENT.MD")); + + assert!(!is_file_type_mergable(".json")); + assert!(!is_file_type_mergable("HELLO.JSON")); + assert!(!is_file_type_mergable("my/config.yml")); +}