From 058ca89561a9e1236040782f3acab9ccbcd5c95f Mon Sep 17 00:00:00 2001 From: Elijah Morgan Date: Wed, 1 Jan 2025 10:30:55 -0500 Subject: [PATCH 01/28] feat add basic opfs support and tests --- Cargo.lock | 15 + bindings/wasm/.gitignore | 2 + bindings/wasm/Cargo.toml | 2 + bindings/wasm/browser-its/package-lock.json | 2125 +++++++++++++++ bindings/wasm/browser-its/package.json | 15 + bindings/wasm/browser-its/tests/test.js | 84 + bindings/wasm/index.html | 82 + bindings/wasm/lib.rs | 7 +- bindings/wasm/limbo-opfs-test.html | 62 + bindings/wasm/limbo-test.html | 11 + bindings/wasm/package-lock.json | 2674 +++++++++++++++++++ bindings/wasm/package.json | 17 +- bindings/wasm/playwright.config.js | 13 + bindings/wasm/scripts/build | 2 +- bindings/wasm/src/limbo-worker.js | 55 + bindings/wasm/src/opfs-interface.js | 67 + bindings/wasm/src/opfs-sync-proxy.js | 116 + bindings/wasm/src/opfs-worker.js | 101 + bindings/wasm/src/opfs.js | 134 + bindings/wasm/src/web-vfs.js | 32 + bindings/wasm/test/limbo.test.js | 62 + bindings/wasm/test/opfs.test.js | 144 + bindings/wasm/test/setup.js | 33 + bindings/wasm/test/test.html | 37 + bindings/wasm/vite.config.js | 34 + core/lib.rs | 3 + 26 files changed, 5925 insertions(+), 4 deletions(-) create mode 100644 bindings/wasm/.gitignore create mode 100644 bindings/wasm/browser-its/package-lock.json create mode 100644 bindings/wasm/browser-its/package.json create mode 100644 bindings/wasm/browser-its/tests/test.js create mode 100644 bindings/wasm/index.html create mode 100644 bindings/wasm/limbo-opfs-test.html create mode 100644 bindings/wasm/limbo-test.html create mode 100644 bindings/wasm/package-lock.json create mode 100644 bindings/wasm/playwright.config.js create mode 100644 bindings/wasm/src/limbo-worker.js create mode 100644 bindings/wasm/src/opfs-interface.js create mode 100644 bindings/wasm/src/opfs-sync-proxy.js create mode 100644 bindings/wasm/src/opfs-worker.js create mode 100644 bindings/wasm/src/opfs.js create mode 100644 bindings/wasm/src/web-vfs.js create mode 100644 bindings/wasm/test/limbo.test.js create mode 100644 bindings/wasm/test/opfs.test.js create mode 100644 bindings/wasm/test/setup.js create mode 100644 bindings/wasm/test/test.html create mode 100644 bindings/wasm/vite.config.js diff --git a/Cargo.lock b/Cargo.lock index e3370cd59..94be7bf9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1120,6 +1120,8 @@ dependencies = [ "js-sys", "limbo_core", "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] @@ -2337,6 +2339,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.99" diff --git a/bindings/wasm/.gitignore b/bindings/wasm/.gitignore new file mode 100644 index 000000000..0f8678b46 --- /dev/null +++ b/bindings/wasm/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +*.wasm diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 1a45707e0..041bcef82 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -15,3 +15,5 @@ console_error_panic_hook = "0.1.7" js-sys = "0.3.72" limbo_core = { path = "../../core", default-features = false } wasm-bindgen = "0.2" +wasm-bindgen-futures = "0.4" +web-sys = "0.3" diff --git a/bindings/wasm/browser-its/package-lock.json b/bindings/wasm/browser-its/package-lock.json new file mode 100644 index 000000000..d614997bf --- /dev/null +++ b/bindings/wasm/browser-its/package-lock.json @@ -0,0 +1,2125 @@ +{ + "name": "limbo-wasm-integration-tests", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "limbo-wasm-integration-tests", + "dependencies": { + "better-sqlite3": "^8.4.0", + "limbo-wasm": "../pkg" + }, + "devDependencies": { + "ava": "^5.3.0" + } + }, + "../pkg": { + "version": "0.0.6", + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aggregate-error": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", + "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", + "dev": true, + "dependencies": { + "clean-stack": "^4.0.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "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, + "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, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrgv": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", + "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/arrify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", + "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ava": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ava/-/ava-5.3.1.tgz", + "integrity": "sha512-Scv9a4gMOXB6+ni4toLuhAm9KYWEjsgBglJl+kMGI5+IVDt120CCDZyB5HNU9DjmLI2t4I0GbnxGLmmRfGTJGg==", + "dev": true, + "dependencies": { + "acorn": "^8.8.2", + "acorn-walk": "^8.2.0", + "ansi-styles": "^6.2.1", + "arrgv": "^1.0.2", + "arrify": "^3.0.0", + "callsites": "^4.0.0", + "cbor": "^8.1.0", + "chalk": "^5.2.0", + "chokidar": "^3.5.3", + "chunkd": "^2.0.1", + "ci-info": "^3.8.0", + "ci-parallel-vars": "^1.0.1", + "clean-yaml-object": "^0.1.0", + "cli-truncate": "^3.1.0", + "code-excerpt": "^4.0.0", + "common-path-prefix": "^3.0.0", + "concordance": "^5.0.4", + "currently-unhandled": "^0.4.1", + "debug": "^4.3.4", + "emittery": "^1.0.1", + "figures": "^5.0.0", + "globby": "^13.1.4", + "ignore-by-default": "^2.1.0", + "indent-string": "^5.0.0", + "is-error": "^2.2.2", + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "matcher": "^5.0.0", + "mem": "^9.0.2", + "ms": "^2.1.3", + "p-event": "^5.0.1", + "p-map": "^5.5.0", + "picomatch": "^2.3.1", + "pkg-conf": "^4.0.0", + "plur": "^5.1.0", + "pretty-ms": "^8.0.0", + "resolve-cwd": "^3.0.0", + "stack-utils": "^2.0.6", + "strip-ansi": "^7.0.1", + "supertap": "^3.0.1", + "temp-dir": "^3.0.0", + "write-file-atomic": "^5.0.1", + "yargs": "^17.7.2" + }, + "bin": { + "ava": "entrypoints/cli.mjs" + }, + "engines": { + "node": ">=14.19 <15 || >=16.15 <17 || >=18" + }, + "peerDependencies": { + "@ava/typescript": "*" + }, + "peerDependenciesMeta": { + "@ava/typescript": { + "optional": true + } + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/better-sqlite3": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-8.7.0.tgz", + "integrity": "sha512-99jZU4le+f3G6aIl6PmmV0cxUIWqKieHxsiF7G34CVFiE+/UabpYqkU0NJIkY/96mQKikHeBjtR27vFfs5JpEw==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + } + }, + "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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "dev": true + }, + "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, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/callsites": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.2.0.tgz", + "integrity": "sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cbor": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", + "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", + "dev": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "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, + "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/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/chunkd": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", + "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", + "dev": true + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/ci-parallel-vars": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", + "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", + "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clean-yaml-object": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/clean-yaml-object/-/clean-yaml-object-0.1.0.tgz", + "integrity": "sha512-3yONmlN9CSAkzNwnRCiJQ7Q2xK5mWuEfL3PuTZcAUzhObbXsfsnMptJzXwz93nc5zn9V9TwCVMmV7w4xsm43dw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "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, + "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 + }, + "node_modules/cliui/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, + "engines": { + "node": ">=8" + } + }, + "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, + "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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "dev": true, + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "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, + "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 + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true + }, + "node_modules/concordance": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dev": true, + "dependencies": { + "date-time": "^3.1.0", + "esutils": "^2.0.3", + "fast-diff": "^1.2.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "md5-hex": "^3.0.1", + "semver": "^7.3.2", + "well-known-symbols": "^2.0.0" + }, + "engines": { + "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" + } + }, + "node_modules/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "dependencies": { + "time-zone": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "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 + }, + "node_modules/emittery": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.0.3.tgz", + "integrity": "sha512-tJdCJitoy2lrC2ldJcqN4vkqJ00lT+tOWNT1hBJjO/3FDMJa5TTIiYGCKGkn/WfCyOzUMObeohbVTj00fhiLiA==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "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 + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", + "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^5.0.0", + "is-unicode-supported": "^1.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "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, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.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, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, + "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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", + "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", + "dev": true, + "engines": { + "node": ">=10 <11 || >=12 <13 || >=14" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "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==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/irregular-plurals": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", + "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-error": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.2.tgz", + "integrity": "sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==", + "dev": true + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/limbo-wasm": { + "resolved": "../pkg", + "link": true + }, + "node_modules/load-json-file": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", + "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "dependencies": { + "p-defer": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/matcher": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", + "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "dependencies": { + "blueimp-md5": "^2.10.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mem": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mem/-/mem-9.0.2.tgz", + "integrity": "sha512-F2t4YIv9XQUBHt6AOJ0y7lSmP1+cY7Fm1DRh9GClTGzKST7UWLMx6ly9WZdLH/G/ppM5RL4MlQfRT71ri9t19A==", + "dev": true, + "dependencies": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sindresorhus/mem?sponsor=1" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "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, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "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 + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, + "node_modules/node-abi": { + "version": "3.71.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", + "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "engines": { + "node": ">=12.19" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-event": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz", + "integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==", + "dev": true, + "dependencies": { + "p-timeout": "^5.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", + "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", + "dev": true, + "dependencies": { + "aggregate-error": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", + "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-3.0.0.tgz", + "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-conf": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-4.0.0.tgz", + "integrity": "sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w==", + "dev": true, + "dependencies": { + "find-up": "^6.0.0", + "load-json-file": "^7.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/plur": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", + "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", + "dev": true, + "dependencies": { + "irregular-plurals": "^3.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pretty-ms": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz", + "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", + "dev": true, + "dependencies": { + "parse-ms": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "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==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "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 + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=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==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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, + "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/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supertap": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", + "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", + "dev": true, + "dependencies": { + "indent-string": "^5.0.0", + "js-yaml": "^3.14.1", + "serialize-error": "^7.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==" + }, + "node_modules/well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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, + "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/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/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 + }, + "node_modules/wrap-ansi/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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, + "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/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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "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, + "engines": { + "node": ">=10" + } + }, + "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, + "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", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "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, + "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 + }, + "node_modules/yargs/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, + "engines": { + "node": ">=8" + } + }, + "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, + "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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/bindings/wasm/browser-its/package.json b/bindings/wasm/browser-its/package.json new file mode 100644 index 000000000..1c2070ce5 --- /dev/null +++ b/bindings/wasm/browser-its/package.json @@ -0,0 +1,15 @@ +{ + "name": "limbo-wasm-integration-tests", + "type": "module", + "private": true, + "scripts": { + "test": "PROVIDER=better-sqlite3 ava tests/test.js && PROVIDER=limbo-wasm ava tests/test.js" + }, + "devDependencies": { + "ava": "^5.3.0" + }, + "dependencies": { + "better-sqlite3": "^8.4.0", + "limbo-wasm": "../pkg" + } +} diff --git a/bindings/wasm/browser-its/tests/test.js b/bindings/wasm/browser-its/tests/test.js new file mode 100644 index 000000000..5932e7f0e --- /dev/null +++ b/bindings/wasm/browser-its/tests/test.js @@ -0,0 +1,84 @@ +import test from "ava"; + +test.beforeEach(async (t) => { + const [db, errorType, provider] = await connect(); + db.exec(` + DROP TABLE IF EXISTS users; + CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT) + `); + db.exec( + "INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.org')" + ); + db.exec( + "INSERT INTO users (id, name, email) VALUES (2, 'Bob', 'bob@example.com')" + ); + t.context = { + db, + errorType, + provider + }; + }); + +test.serial("Statement.raw().all()", async (t) => { + const db = t.context.db; + + const stmt = db.prepare("SELECT * FROM users"); + const expected = [ + [1, "Alice", "alice@example.org"], + [2, "Bob", "bob@example.com"], + ]; + t.deepEqual(stmt.raw().all(), expected); +}); + +test.serial("Statement.raw().get()", async (t) => { + const db = t.context.db; + + const stmt = db.prepare("SELECT * FROM users"); + const expected = [ + 1, "Alice", "alice@example.org" + ]; + t.deepEqual(stmt.raw().get(), expected); + + const emptyStmt = db.prepare("SELECT * FROM users WHERE id = -1"); + t.is(emptyStmt.raw().get(), undefined); +}); + +test.serial("Statement.raw().iterate()", async (t) => { + const db = t.context.db; + + const stmt = db.prepare("SELECT * FROM users"); + const expected = [ + { done: false, value: [1, "Alice", "alice@example.org"] }, + { done: false, value: [2, "Bob", "bob@example.com"] }, + { done: true, value: undefined }, + ]; + + let iter = stmt.raw().iterate(); + t.is(typeof iter[Symbol.iterator], 'function'); + t.deepEqual(iter.next(), expected[0]) + t.deepEqual(iter.next(), expected[1]) + t.deepEqual(iter.next(), expected[2]) + + const emptyStmt = db.prepare("SELECT * FROM users WHERE id = -1"); + t.is(typeof emptyStmt[Symbol.iterator], 'undefined'); + t.throws(() => emptyStmt.next(), { instanceOf: TypeError }); +}); + +const connect = async (path_opt) => { + const path = path_opt ?? "hello.db"; + const provider = process.env.PROVIDER; + if (provider === "limbo-wasm") { + const database = process.env.LIBSQL_DATABASE ?? path; + const x = await import("limbo-wasm"); + const options = {}; + const db = new x.Database(database, options); + return [db, x.SqliteError, provider]; + } + if (provider == "better-sqlite3") { + const x = await import("better-sqlite3"); + const options = {}; + const db = x.default(path, options); + return [db, x.SqliteError, provider]; + } + throw new Error("Unknown provider: " + provider); + }; \ No newline at end of file diff --git a/bindings/wasm/index.html b/bindings/wasm/index.html new file mode 100644 index 000000000..f3246b19e --- /dev/null +++ b/bindings/wasm/index.html @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bindings/wasm/lib.rs b/bindings/wasm/lib.rs index 1870cca87..2f226f067 100644 --- a/bindings/wasm/lib.rs +++ b/bindings/wasm/lib.rs @@ -46,7 +46,10 @@ impl Database { } #[wasm_bindgen] - pub fn exec(&self, _sql: &str) {} + pub fn exec(&self, _sql: &str) { + let _res = self.conn.execute(_sql).unwrap(); + // Statement::new(RefCell::new(stmt), false) + } #[wasm_bindgen] pub fn prepare(&self, _sql: &str) -> Statement { @@ -300,7 +303,7 @@ impl limbo_core::DatabaseStorage for DatabaseStorage { } } -#[wasm_bindgen(module = "/vfs.js")] +#[wasm_bindgen(module = "/src/web-vfs.js")] extern "C" { type VFS; diff --git a/bindings/wasm/limbo-opfs-test.html b/bindings/wasm/limbo-opfs-test.html new file mode 100644 index 000000000..4d7b23ff2 --- /dev/null +++ b/bindings/wasm/limbo-opfs-test.html @@ -0,0 +1,62 @@ + + + + + + + diff --git a/bindings/wasm/limbo-test.html b/bindings/wasm/limbo-test.html new file mode 100644 index 000000000..8651f7f77 --- /dev/null +++ b/bindings/wasm/limbo-test.html @@ -0,0 +1,11 @@ + + + + Limbo Test + + + + + diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json new file mode 100644 index 000000000..ee43543ee --- /dev/null +++ b/bindings/wasm/package-lock.json @@ -0,0 +1,2674 @@ +{ + "name": "limbo-wasm", + "version": "0.0.10", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "limbo-wasm", + "version": "0.0.10", + "license": "MIT", + "devDependencies": { + "@playwright/test": "^1.40.0", + "@vitest/ui": "^1.0.0", + "happy-dom": "^12.0.0", + "playwright": "^1.40.0", + "vite": "^5.0.0", + "vite-plugin-wasm": "^3.4.1", + "vitest": "^1.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@playwright/test": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", + "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz", + "integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz", + "integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz", + "integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz", + "integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz", + "integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz", + "integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz", + "integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz", + "integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz", + "integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz", + "integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz", + "integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz", + "integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz", + "integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz", + "integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz", + "integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz", + "integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz", + "integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz", + "integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz", + "integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@vitest/expect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "1.6.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.6.0.tgz", + "integrity": "sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "1.6.0", + "fast-glob": "^3.3.2", + "fflate": "^0.8.1", + "flatted": "^3.2.9", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "sirv": "^2.0.4" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "1.6.0" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "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/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "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.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", + "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "rrweb-cssom": "^0.7.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.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/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", + "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, + "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/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true, + "license": "ISC" + }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 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/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/happy-dom": { + "version": "12.10.3", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-12.10.3.tgz", + "integrity": "sha512-JzUXOh0wdNGY54oKng5hliuBkq/+aT1V3YpTM+lrN/GoLQTANZsMaIvmHiHe612rauHvPJnDZkZ+5GZR++1Abg==", + "dev": true, + "license": "MIT", + "dependencies": { + "css.escape": "^1.5.1", + "entities": "^4.5.0", + "iconv-lite": "^0.6.3", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0" + } + }, + "node_modules/happy-dom/node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/happy-dom/node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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-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-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/local-pkg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "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/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "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-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", + "optional": true, + "peer": true, + "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", + "optional": true, + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mlly": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz", + "integrity": "sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^1.1.2", + "pkg-types": "^1.2.1", + "ufo": "^1.5.4" + } + }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "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/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nwsapi": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "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/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "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-types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz", + "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.2", + "pathe": "^1.1.2" + } + }, + "node_modules/playwright": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "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.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.29.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz", + "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.29.1", + "@rollup/rollup-android-arm64": "4.29.1", + "@rollup/rollup-darwin-arm64": "4.29.1", + "@rollup/rollup-darwin-x64": "4.29.1", + "@rollup/rollup-freebsd-arm64": "4.29.1", + "@rollup/rollup-freebsd-x64": "4.29.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.29.1", + "@rollup/rollup-linux-arm-musleabihf": "4.29.1", + "@rollup/rollup-linux-arm64-gnu": "4.29.1", + "@rollup/rollup-linux-arm64-musl": "4.29.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.29.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.29.1", + "@rollup/rollup-linux-riscv64-gnu": "4.29.1", + "@rollup/rollup-linux-s390x-gnu": "4.29.1", + "@rollup/rollup-linux-x64-gnu": "4.29.1", + "@rollup/rollup-linux-x64-musl": "4.29.1", + "@rollup/rollup-win32-arm64-msvc": "4.29.1", + "@rollup/rollup-win32-ia32-msvc": "4.29.1", + "@rollup/rollup-win32-x64-msvc": "4.29.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "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/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "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/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "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/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "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/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", + "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.70.tgz", + "integrity": "sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tldts-core": "^6.1.70" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.70.tgz", + "integrity": "sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "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/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "peer": true, + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/vite": { + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-plugin-wasm": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/vite-plugin-wasm/-/vite-plugin-wasm-3.4.1.tgz", + "integrity": "sha512-ja3nSo2UCkVeitltJGkS3pfQHAanHv/DqGatdI39ja6McgABlpsZ5hVgl6wuR8Qx5etY3T5qgDQhOWzc5RReZA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "vite": "^2 || ^3 || ^4 || ^5 || ^6" + } + }, + "node_modules/vitest": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.0", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "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 + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index 2a74b138e..b2d1e4221 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -16,5 +16,20 @@ "limbo_wasm.d.ts" ], "main": "limbo_wasm.js", - "types": "limbo_wasm.d.ts" + "types": "limbo_wasm.d.ts", + "type": "module", + "scripts": { + "dev": "vite", + "test": "vitest --sequence.shuffle=false", + "test:ui": "vitest --ui" + }, + "devDependencies": { + "@playwright/test": "^1.40.0", + "@vitest/ui": "^1.0.0", + "happy-dom": "^12.0.0", + "playwright": "^1.40.0", + "vite": "^5.0.0", + "vite-plugin-wasm": "^3.4.1", + "vitest": "^1.0.0" + } } diff --git a/bindings/wasm/playwright.config.js b/bindings/wasm/playwright.config.js new file mode 100644 index 000000000..39a997296 --- /dev/null +++ b/bindings/wasm/playwright.config.js @@ -0,0 +1,13 @@ +// Using Playwright (recommended) +import { expect, test } from "@playwright/test"; + +// playwright.config.js +export default { + use: { + headless: true, + // Required for SharedArrayBuffer + launchOptions: { + args: ["--cross-origin-isolated"], + }, + }, +}; diff --git a/bindings/wasm/scripts/build b/bindings/wasm/scripts/build index 23f2b8b93..1b2cbc2f8 100755 --- a/bindings/wasm/scripts/build +++ b/bindings/wasm/scripts/build @@ -1,4 +1,4 @@ #!/bin/bash -wasm-pack build --no-pack --target nodejs +wasm-pack build --no-pack --target web cp package.json pkg/package.json diff --git a/bindings/wasm/src/limbo-worker.js b/bindings/wasm/src/limbo-worker.js new file mode 100644 index 000000000..f91cf6115 --- /dev/null +++ b/bindings/wasm/src/limbo-worker.js @@ -0,0 +1,55 @@ +import { VFS } from "./opfs.js"; +import init, { Database } from "./../pkg/limbo_wasm.js"; + +let db = null; +let currentStmt = null; + +async function initVFS() { + const vfs = new VFS(); + await vfs.ready; + self.vfs = vfs; + return vfs; +} + +async function initAll() { + await initVFS(); + await init(); +} + +initAll().then(() => { + self.postMessage({ type: "ready" }); + + self.onmessage = (e) => { + try { + switch (e.data.op) { + case "createDb": { + db = new Database(e.data.path); + self.postMessage({ type: "success", op: "createDb" }); + break; + } + case "exec": { + console.log(e.data.sql); + db.exec(e.data.sql); + self.postMessage({ type: "success", op: "exec" }); + break; + } + case "prepare": { + currentStmt = db.prepare(e.data.sql); + const results = currentStmt.raw().all(); + self.postMessage({ type: "result", result: results }); + break; + } + case "get": { + const row = currentStmt?.raw().get(); + self.postMessage({ type: "result", result: row }); + break; + } + } + } catch (err) { + self.postMessage({ type: "error", error: err.toString() }); + } + }; +}).catch((error) => { + self.postMessage({ type: "error", error: error.toString() }); +}); + diff --git a/bindings/wasm/src/opfs-interface.js b/bindings/wasm/src/opfs-interface.js new file mode 100644 index 000000000..f237040a4 --- /dev/null +++ b/bindings/wasm/src/opfs-interface.js @@ -0,0 +1,67 @@ +export class VFSInterface { + constructor(workerUrl) { + this.worker = new Worker(workerUrl, { type: "module" }); + this.nextMessageId = 1; + this.pendingRequests = new Map(); + + this.worker.onmessage = (event) => { + console.log("interface onmessage: ", event.data); + let { id, result, error } = event.data; + const resolver = this.pendingRequests.get(id); + if (event.data?.buffer && event.data?.size) { + result = { size: event.data.size, buffer: event.data.buffer }; + } + + if (resolver) { + this.pendingRequests.delete(id); + if (error) { + resolver.reject(new Error(error)); + } else { + resolver.resolve(result); + } + } + }; + } + + _sendMessage(method, args) { + const id = this.nextMessageId++; + return new Promise((resolve, reject) => { + this.pendingRequests.set(id, { resolve, reject }); + this.worker.postMessage({ id, method, args }); + }); + } + + async open(path, flags) { + return await this._sendMessage("open", { path, flags }); + } + + async close(fd) { + return await this._sendMessage("close", { fd }); + } + + async pwrite(fd, buffer, offset) { + return await this._sendMessage("pwrite", { fd, buffer, offset }, [ + buffer.buffer, + ]); + } + + async pread(fd, buffer, offset) { + console.log("interface in buffer: ", [...buffer]); + const result = await this._sendMessage("pread", { + fd, + buffer: buffer, + offset, + }, []); + console.log("interface out buffer: ", [...buffer]); + buffer.set(new Uint8Array(result.buffer)); + return buffer.length; + } + + async size(fd) { + return await this._sendMessage("size", { fd }); + } + + async sync(fd) { + return await this._sendMessage("sync", { fd }); + } +} diff --git a/bindings/wasm/src/opfs-sync-proxy.js b/bindings/wasm/src/opfs-sync-proxy.js new file mode 100644 index 000000000..2bc1a9105 --- /dev/null +++ b/bindings/wasm/src/opfs-sync-proxy.js @@ -0,0 +1,116 @@ +// opfs-sync-proxy.js +let transferBuffer, statusBuffer, statusArray, statusView; +let transferArray; +let rootDir = null; +const handles = new Map(); +let nextFd = 1; + +self.postMessage("ready"); + +onmessage = async (e) => { + console.log("handle message: ", e.data); + if (e.data.cmd === "init") { + console.log("init"); + transferBuffer = e.data.transferBuffer; + statusBuffer = e.data.statusBuffer; + + transferArray = new Uint8Array(transferBuffer); + statusArray = new Int32Array(statusBuffer); + statusView = new DataView(statusBuffer); + + self.postMessage("done"); + return; + } + + const result = await handleCommand(e.data); + sendResult(result); +}; + +self.onerror = (error) => { + console.error("opfssync error: ", error); + // Don't close, keep running + return true; // Prevents default error handling +}; + +function handleCommand(msg) { + console.log(`handle message: ${msg.cmd}`); + switch (msg.cmd) { + case "open": + return handleOpen(msg.path); + case "close": + return handleClose(msg.fd); + case "read": + return handleRead(msg.fd, msg.offset, msg.size); + case "write": + return handleWrite(msg.fd, msg.buffer, msg.offset); + case "size": + return handleSize(msg.fd); + case "sync": + return handleSync(msg.fd); + } +} + +async function handleOpen(path) { + if (!rootDir) { + rootDir = await navigator.storage.getDirectory(); + } + const fd = nextFd++; + + const handle = await rootDir.getFileHandle(path, { create: true }); + const syncHandle = await handle.createSyncAccessHandle(); + + handles.set(fd, syncHandle); + return { fd }; +} + +function handleClose(fd) { + const handle = handles.get(fd); + handle.close(); + handles.delete(fd); + return { success: true }; +} + +function handleRead(fd, offset, size) { + const handle = handles.get(fd); + const readBuffer = new ArrayBuffer(size); + const readSize = handle.read(readBuffer, { at: offset }); + console.log("opfssync read: size: ", readBuffer.byteLength); + + const tmp = new Uint8Array(readBuffer); + console.log("opfssync read buffer: ", [...tmp]); + + transferArray.set(tmp); + + return { success: true, length: readSize }; +} + +function handleWrite(fd, buffer, offset) { + console.log("opfssync buffer size:", buffer.byteLength); + console.log("opfssync write buffer: ", [...buffer]); + const handle = handles.get(fd); + const size = handle.write(buffer, { at: offset }); + return { success: true, length: size }; +} + +function handleSize(fd) { + const handle = handles.get(fd); + return { success: true, length: handle.getSize() }; +} + +function handleSync(fd) { + const handle = handles.get(fd); + handle.flush(); + return { success: true }; +} + +function sendResult(result) { + if (result?.fd) { + statusView.setInt32(4, result.fd, true); + } else { + console.log("opfs-sync-proxy: result.length: ", result.length); + statusView.setInt32(4, result?.length || 0, true); + } + + Atomics.store(statusArray, 0, 1); + Atomics.notify(statusArray, 0); +} diff --git a/bindings/wasm/src/opfs-worker.js b/bindings/wasm/src/opfs-worker.js new file mode 100644 index 000000000..dd58d3899 --- /dev/null +++ b/bindings/wasm/src/opfs-worker.js @@ -0,0 +1,101 @@ +import { VFS } from "./opfs.js"; + +const vfs = new VFS(); + +onmessage = async function (e) { + if (!vfs.isReady) { + console.log("opfs ready: ", vfs.isReady); + await vfs.ready; + console.log("opfs ready: ", vfs.isReady); + } + + const { id, method, args } = e.data; + + console.log(`interface onmessage method: ${method}`); + try { + let result; + switch (method) { + case "open": + result = vfs.open(args.path, args.flags); + break; + case "close": + result = vfs.close(args.fd); + break; + case "pread": { + const buffer = new Uint8Array(args.buffer); + result = vfs.pread(args.fd, buffer, args.offset); + self.postMessage( + { id, size: result, error: null, buffer }, + ); + console.log("read size: ", result); + console.log("read buffer: ", [...buffer]); + return; + } + case "pwrite": { + result = vfs.pwrite(args.fd, args.buffer, args.offset); + console.log("write size: ", result); + break; + } + case "size": + result = vfs.size(args.fd); + break; + case "sync": + result = vfs.sync(args.fd); + break; + default: + throw new Error(`Unknown method: ${method}`); + } + + self.postMessage( + { id, result, error: null }, + ); + } catch (error) { + self.postMessage({ id, result: null, error: error.message }); + } +}; + +console.log("opfs-worker.js"); +// checkCompatibility(); + +// // In VFS class +// this.worker.onerror = (error) => { +// console.error("Worker stack:", error.error?.stack || error.message); +// }; + +// checkCompatibility(); +// +// async function checkCompatibility() { +// console.log("begin check compatibility"); +// // OPFS API check +// if (!("storage" in navigator && "getDirectory" in navigator.storage)) { +// throw new Error("OPFS API not supported"); +// } +// +// // SharedArrayBuffer support check +// if (typeof SharedArrayBuffer === "undefined") { +// throw new Error("SharedArrayBuffer not supported"); +// } +// +// // Atomics API check +// if (typeof Atomics === "undefined") { +// throw new Error("Atomics API not supported"); +// } +// +// // Permission check for OPFS +// try { +// const root = await navigator.storage.getDirectory(); +// await root.getFileHandle("test.txt", { create: true }); +// } catch (e) { +// console.log(e); +// console.log("throwing OPFS permission Denied"); +// throw new Error("OPFS permission denied"); +// } +// +// // Cross-Origin-Isolation check for SharedArrayBuffer +// if (!crossOriginIsolated) { +// throw new Error("Cross-Origin-Isolation required for SharedArrayBuffer"); +// } +// +// console.log("done check compatibility"); +// return true; +// } diff --git a/bindings/wasm/src/opfs.js b/bindings/wasm/src/opfs.js new file mode 100644 index 000000000..104cf3cfc --- /dev/null +++ b/bindings/wasm/src/opfs.js @@ -0,0 +1,134 @@ +// First file: VFS class +class VFS { + constructor() { + this.transferBuffer = new SharedArrayBuffer(1024 * 1024); // 1mb + this.statusBuffer = new SharedArrayBuffer(8); // Room for status + size + + this.statusArray = new Int32Array(this.statusBuffer); + this.statusView = new DataView(this.statusBuffer); + + this.worker = new Worker( + new URL("./opfs-sync-proxy.js", import.meta.url), + { type: "module" }, + ); + + this.isReady = false; + this.ready = new Promise((resolve, reject) => { + this.worker.addEventListener("message", async (e) => { + if (e.data === "ready") { + await this.initWorker(); + this.isReady = true; + resolve(); + } + }, { once: true }); + this.worker.addEventListener("error", reject, { once: true }); + }); + + this.worker.onerror = (e) => { + console.error("Sync proxy worker error:", e.message); + }; + } + + initWorker() { + return new Promise((resolve) => { + this.worker.addEventListener("message", (e) => { + console.log("eventListener: ", e.data); + resolve(); + }, { once: true }); + + this.worker.postMessage({ + cmd: "init", + transferBuffer: this.transferBuffer, + statusBuffer: this.statusBuffer, + }); + }); + } + + open(path) { + Atomics.store(this.statusArray, 0, 0); + this.worker.postMessage({ cmd: "open", path }); + Atomics.wait(this.statusArray, 0, 0); + + const result = this.statusView.getInt32(4, true); + console.log("opfs.js open result: ", result); + console.log("opfs.js open result type: ", typeof result); + + return result; + } + + close(fd) { + Atomics.store(this.statusArray, 0, 0); + this.worker.postMessage({ cmd: "close", fd }); + Atomics.wait(this.statusArray, 0, 0); + return true; + } + + pread(fd, buffer, offset) { + let bytesRead = 0; + + while (bytesRead < buffer.byteLength) { + const chunkSize = Math.min( + this.transferBuffer.byteLength, + buffer.byteLength - bytesRead, + ); + + Atomics.store(this.statusArray, 0, 0); + this.worker.postMessage({ + cmd: "read", + fd, + offset: offset + bytesRead, + size: chunkSize, + }); + + Atomics.wait(this.statusArray, 0, 0); + const readSize = this.statusView.getInt32(4, true); + buffer.set( + new Uint8Array(this.transferBuffer, 0, readSize), + bytesRead, + ); + console.log("opfs pread buffer: ", [...buffer]); + + bytesRead += readSize; + if (readSize < chunkSize) break; + } + + return bytesRead; + } + + pwrite(fd, buffer, offset) { + console.log("write buffer size: ", buffer.byteLength); + Atomics.store(this.statusArray, 0, 0); + this.worker.postMessage({ + cmd: "write", + fd, + buffer: buffer, + offset: offset, + }); + + Atomics.wait(this.statusArray, 0, 0); + console.log( + "opfs pwrite length statusview: ", + this.statusView.getInt32(4, true), + ); + return this.statusView.getInt32(4, true); + } + + size(fd) { + Atomics.store(this.statusArray, 0, 0); + this.worker.postMessage({ cmd: "size", fd }); + Atomics.wait(this.statusArray, 0, 0); + + const result = this.statusView.getInt32(4, true); + console.log("opfs.js size result: ", result); + console.log("opfs.js size result type: ", typeof result); + return BigInt(result); + } + + sync(fd) { + Atomics.store(this.statusArray, 0, 0); + this.worker.postMessage({ cmd: "sync", fd }); + Atomics.wait(this.statusArray, 0, 0); + } +} + +export { VFS }; diff --git a/bindings/wasm/src/web-vfs.js b/bindings/wasm/src/web-vfs.js new file mode 100644 index 000000000..4ba0cce4a --- /dev/null +++ b/bindings/wasm/src/web-vfs.js @@ -0,0 +1,32 @@ +export class VFS { + constructor() { + return self.vfs; + } + + open(path, flags) { + const result = self.vfs.open(path); + consol.log("webvfs open result: ", result); + consol.log("webvfs open result type: ", typeof result); + return result; + } + + close(fd) { + return self.vfs.close(fd); + } + + pread(fd, buffer, offset) { + return self.vfs.pread(fd, buffer, offset); + } + + pwrite(fd, buffer, offset) { + return self.vfs.pwrite(fd, buffer, offset); + } + + size(fd) { + return self.vfs.size(fd); + } + + sync(fd) { + return self.vfs.sync(fd); + } +} diff --git a/bindings/wasm/test/limbo.test.js b/bindings/wasm/test/limbo.test.js new file mode 100644 index 000000000..37e1f983f --- /dev/null +++ b/bindings/wasm/test/limbo.test.js @@ -0,0 +1,62 @@ +import { expect, test } from "vitest"; + +test("basic database operations", async () => { + const page = globalThis.__page__; + await page.goto("http://localhost:5173/limbo-test.html"); + + page.on("console", (msg) => console.log(msg.text())); + + const result = await page.evaluate(async () => { + const worker = new Worker("./src/limbo-worker.js", { type: "module" }); + + const waitForMessage = (type, op) => + new Promise((resolve, reject) => { + const handler = (e) => { + if (e.data.type === type && (!op || e.data.op === op)) { + worker.removeEventListener("message", handler); + resolve(e.data); + } else if (e.data.type === "error") { + worker.removeEventListener("message", handler); + reject(e.data.error); + } + }; + worker.addEventListener("message", handler); + }); + + try { + await waitForMessage("ready"); + worker.postMessage({ op: "createDb", path: "test.db" }); + await waitForMessage("success", "createDb"); + + worker.postMessage({ + op: "exec", + sql: + "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT);", + }); + await waitForMessage("success", "exec"); + + worker.postMessage({ + op: "exec", + sql: "INSERT INTO users VALUES (1, 'Alice', 'alice@example.org');", + }); + await waitForMessage("success", "exec"); + + worker.postMessage({ + op: "prepare", + sql: "SELECT * FROM users;", + }); + + const results = await waitForMessage("result"); + return results; + } catch (error) { + return { error: error.message }; + } + }); + + if (result.error) throw new Error(`Test failed: ${result.error}`); + console.log("test results: ", result); + console.log("test results: ", result.result[0]); + expect(result.result).toHaveLength(1); + expect(result.result[0]).toEqual([1, "Alice", "alice@example.org"]); + // expect(1).toEqual(1); +}); diff --git a/bindings/wasm/test/opfs.test.js b/bindings/wasm/test/opfs.test.js new file mode 100644 index 000000000..cce0c29bb --- /dev/null +++ b/bindings/wasm/test/opfs.test.js @@ -0,0 +1,144 @@ +import { expect, test } from "vitest"; + +test("basic read/write functionality", async () => { + const page = globalThis.__page__; + await page.goto("http://localhost:5173/index.html"); + + page.on("console", (msg) => console.log(msg.text())); + + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + + try { + fd = await vfs.open("test.txt", {}); + const writeData = new Uint8Array([1, 2, 3, 4]); + const bytesWritten = await vfs.pwrite(fd, writeData, 0); + const readData = new Uint8Array(4); + const bytesRead = await vfs.pread(fd, readData, 0); + await vfs.close(fd); + + return { fd, bytesWritten, bytesRead, readData: Array.from(readData) }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); + + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(result.fd).toBe(1); + expect(result.bytesWritten).toBe(4); + expect(result.bytesRead).toBe(4); + expect(result.readData).toEqual([1, 2, 3, 4]); +}); + +test("larger data read/write", async () => { + const page = globalThis.__page__; + await page.goto("http://localhost:5173/index.html"); + + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + + try { + fd = await vfs.open("large.txt", {}); + const writeData = new Uint8Array(1024).map((_, i) => i % 256); + const bytesWritten = await vfs.pwrite(fd, writeData, 0); + const readData = new Uint8Array(1024); + const bytesRead = await vfs.pread(fd, readData, 0); + await vfs.close(fd); + + return { bytesWritten, bytesRead, readData: Array.from(readData) }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); + + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(result.bytesWritten).toBe(1024); + expect(result.bytesRead).toBe(1024); + expect(result.readData).toEqual( + Array.from({ length: 1024 }, (_, i) => i % 256), + ); +}); + +test("partial reads and writes", async () => { + const page = globalThis.__page__; + await page.goto("http://localhost:5173/index.html"); + + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + + try { + fd = await vfs.open("partial.txt", {}); + + // Write data in chunks + const writeData1 = new Uint8Array([1, 2, 3, 4]); + const writeData2 = new Uint8Array([5, 6, 7, 8]); + await vfs.pwrite(fd, writeData1, 0); + await vfs.pwrite(fd, writeData2, 4); + + // Read partial chunks + const readData1 = new Uint8Array(2); + const readData2 = new Uint8Array(4); + const readData3 = new Uint8Array(2); + + await vfs.pread(fd, readData1, 0); // Should read [1,2] + await vfs.pread(fd, readData2, 2); // Should read [3,4,5,6] + await vfs.pread(fd, readData3, 6); // Should read [7,8] + + await vfs.close(fd); + + return { + readData1: Array.from(readData1), + readData2: Array.from(readData2), + readData3: Array.from(readData3), + }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); + + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(result.readData1).toEqual([1, 2]); + expect(result.readData2).toEqual([3, 4, 5, 6]); + expect(result.readData3).toEqual([7, 8]); +}); + +test("file size operations", async () => { + const page = globalThis.__page__; + await page.goto("http://localhost:5173/index.html"); + + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + + try { + fd = await vfs.open("size.txt", {}); + + // First write + const writeData1 = new Uint8Array([1, 2, 3, 4]); + await vfs.pwrite(fd, writeData1, 0); + const size1 = await vfs.size(fd); + + // Second write with new array + const writeData2 = new Uint8Array([5, 6, 7, 8]); + await vfs.pwrite(fd, writeData2, 4); + const size2 = await vfs.size(fd); + + await vfs.close(fd); + + return { size1, size2 }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); + + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(Number(result.size1)).toBe(4); + expect(Number(result.size2)).toBe(8); +}); diff --git a/bindings/wasm/test/setup.js b/bindings/wasm/test/setup.js new file mode 100644 index 000000000..3ac2a64fc --- /dev/null +++ b/bindings/wasm/test/setup.js @@ -0,0 +1,33 @@ +import { afterEach, beforeEach } from "vitest"; +import { chromium } from "playwright"; +import { createServer } from "vite"; + +let browser; +let context; +let page; +let server; + +beforeEach(async () => { + // Start Vite dev server + server = await createServer({ + configFile: "./vite.config.js", + root: ".", + server: { + port: 5173, + }, + }); + await server.listen(); + + browser = await chromium.launch(); + context = await browser.newContext(); + page = await context.newPage(); + + globalThis.__page__ = page; +}); + +afterEach(async () => { + await context.close(); + await browser.close(); + await server.close(); +}); + diff --git a/bindings/wasm/test/test.html b/bindings/wasm/test/test.html new file mode 100644 index 000000000..ad2534032 --- /dev/null +++ b/bindings/wasm/test/test.html @@ -0,0 +1,37 @@ + + + + + + + diff --git a/bindings/wasm/vite.config.js b/bindings/wasm/vite.config.js new file mode 100644 index 000000000..0e84f547f --- /dev/null +++ b/bindings/wasm/vite.config.js @@ -0,0 +1,34 @@ +import { defineConfig } from "vite"; +import wasm from "vite-plugin-wasm"; + +export default defineConfig({ + plugins: [wasm()], + test: { + globals: true, + environment: "happy-dom", + setupFiles: ["./test/setup.js"], + include: ["test/*.test.js"], + sequence: { + shuffle: false, + concurrent: false, + }, + }, + server: { + headers: { + "Cross-Origin-Embedder-Policy": "require-corp", + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Resource-Policy": "cross-origin", + }, + }, + worker: { + format: "es", + rollupOptions: { + output: { + format: "es", + }, + }, + }, + // worker: { + // format: "es", + // }, +}); diff --git a/core/lib.rs b/core/lib.rs index 5ef48e742..e79a1bf98 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -35,6 +35,7 @@ use storage::sqlite3_ondisk::{DatabaseHeader, DATABASE_HEADER_SIZE}; pub use storage::wal::WalFile; pub use storage::wal::WalFileShared; use util::parse_schema_rows; +// use web_sys::console; // Add to dependencies in Cargo.toml use translate::select::prepare_select_plan; use types::OwnedValue; @@ -80,6 +81,8 @@ impl Database { pub fn open_file(io: Arc, path: &str) -> Result> { use storage::wal::WalFileShared; + // console::log_1(&"Hello from Rust!".into()); + let file = io.open_file(path, io::OpenFlags::Create, true)?; maybe_init_database_file(&file, &io)?; let page_io = Rc::new(FileStorage::new(file)); From 5765ccbfbb99118a3472e407a4e770a3af976dd7 Mon Sep 17 00:00:00 2001 From: Elijah Morgan Date: Wed, 1 Jan 2025 16:14:48 -0500 Subject: [PATCH 02/28] Add readme info Add basic limbo-opfs-test.html Remove unused browser-its structure - we may bring it back --- bindings/wasm/README.md | 27 + bindings/wasm/browser-its/package-lock.json | 2125 ------------------- bindings/wasm/browser-its/package.json | 15 - bindings/wasm/browser-its/tests/test.js | 84 - bindings/wasm/limbo-opfs-test.html | 125 +- 5 files changed, 100 insertions(+), 2276 deletions(-) delete mode 100644 bindings/wasm/browser-its/package-lock.json delete mode 100644 bindings/wasm/browser-its/package.json delete mode 100644 bindings/wasm/browser-its/tests/test.js diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index 7703ab4a8..9c033c017 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -7,3 +7,30 @@ This source tree contains Limbo Wasm bindings. ``` ./scripts/build ``` + +# Browser Support + +Adding experimental support for limbo in the browser. This is done by adding support for OPFS as a VFS. + +To see a basic example of this `npm run dev` and navigate to `http://localhost:5173/limbo-opfs-test.html` and open the console. + +## Design + +This design mirrors sqlite's approach for OPFS support. It has a sync api in `opfs.js` which communicates with `opfs-sync-proxy.js` via `SharedArrayBuffer` and `Atomics.wait`. This allows us to live the VFS api in `lib.rs` unchanged. + +You can see `limbo-opfs-test.html` for basic usage. + +## UTs + +There are OPFS specific unit tests and then some basic limbo unit tests. These are run via `npm test` or `npx vitest`. + +For more info and log output you can run `npx vitest:ui` but you can get some parallel execution of test cases which cause issues. + + +## TODO + +-[] Add a wrapper js that provides a clean interface to the `limbo-worker.js` +-[] Add more tests for opfs.js operations +-[] Add error return handling +-[] Make sure posix flags for open are handled instead of just being ignored (this requires creating a mapping of behaviors from posix to opfs as far as makes sense) + diff --git a/bindings/wasm/browser-its/package-lock.json b/bindings/wasm/browser-its/package-lock.json deleted file mode 100644 index d614997bf..000000000 --- a/bindings/wasm/browser-its/package-lock.json +++ /dev/null @@ -1,2125 +0,0 @@ -{ - "name": "limbo-wasm-integration-tests", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "limbo-wasm-integration-tests", - "dependencies": { - "better-sqlite3": "^8.4.0", - "limbo-wasm": "../pkg" - }, - "devDependencies": { - "ava": "^5.3.0" - } - }, - "../pkg": { - "version": "0.0.6", - "license": "MIT" - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/aggregate-error": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", - "dev": true, - "dependencies": { - "clean-stack": "^4.0.0", - "indent-string": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "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, - "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, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/arrgv": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", - "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/arrify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", - "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ava": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ava/-/ava-5.3.1.tgz", - "integrity": "sha512-Scv9a4gMOXB6+ni4toLuhAm9KYWEjsgBglJl+kMGI5+IVDt120CCDZyB5HNU9DjmLI2t4I0GbnxGLmmRfGTJGg==", - "dev": true, - "dependencies": { - "acorn": "^8.8.2", - "acorn-walk": "^8.2.0", - "ansi-styles": "^6.2.1", - "arrgv": "^1.0.2", - "arrify": "^3.0.0", - "callsites": "^4.0.0", - "cbor": "^8.1.0", - "chalk": "^5.2.0", - "chokidar": "^3.5.3", - "chunkd": "^2.0.1", - "ci-info": "^3.8.0", - "ci-parallel-vars": "^1.0.1", - "clean-yaml-object": "^0.1.0", - "cli-truncate": "^3.1.0", - "code-excerpt": "^4.0.0", - "common-path-prefix": "^3.0.0", - "concordance": "^5.0.4", - "currently-unhandled": "^0.4.1", - "debug": "^4.3.4", - "emittery": "^1.0.1", - "figures": "^5.0.0", - "globby": "^13.1.4", - "ignore-by-default": "^2.1.0", - "indent-string": "^5.0.0", - "is-error": "^2.2.2", - "is-plain-object": "^5.0.0", - "is-promise": "^4.0.0", - "matcher": "^5.0.0", - "mem": "^9.0.2", - "ms": "^2.1.3", - "p-event": "^5.0.1", - "p-map": "^5.5.0", - "picomatch": "^2.3.1", - "pkg-conf": "^4.0.0", - "plur": "^5.1.0", - "pretty-ms": "^8.0.0", - "resolve-cwd": "^3.0.0", - "stack-utils": "^2.0.6", - "strip-ansi": "^7.0.1", - "supertap": "^3.0.1", - "temp-dir": "^3.0.0", - "write-file-atomic": "^5.0.1", - "yargs": "^17.7.2" - }, - "bin": { - "ava": "entrypoints/cli.mjs" - }, - "engines": { - "node": ">=14.19 <15 || >=16.15 <17 || >=18" - }, - "peerDependencies": { - "@ava/typescript": "*" - }, - "peerDependenciesMeta": { - "@ava/typescript": { - "optional": true - } - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/better-sqlite3": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-8.7.0.tgz", - "integrity": "sha512-99jZU4le+f3G6aIl6PmmV0cxUIWqKieHxsiF7G34CVFiE+/UabpYqkU0NJIkY/96mQKikHeBjtR27vFfs5JpEw==", - "hasInstallScript": true, - "dependencies": { - "bindings": "^1.5.0", - "prebuild-install": "^7.1.1" - } - }, - "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, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/blueimp-md5": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", - "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", - "dev": true - }, - "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, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/callsites": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.2.0.tgz", - "integrity": "sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cbor": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", - "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", - "dev": true, - "dependencies": { - "nofilter": "^3.1.0" - }, - "engines": { - "node": ">=12.19" - } - }, - "node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "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, - "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/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" - }, - "node_modules/chunkd": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", - "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", - "dev": true - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/ci-parallel-vars": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", - "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", - "dev": true - }, - "node_modules/clean-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clean-yaml-object": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/clean-yaml-object/-/clean-yaml-object-0.1.0.tgz", - "integrity": "sha512-3yONmlN9CSAkzNwnRCiJQ7Q2xK5mWuEfL3PuTZcAUzhObbXsfsnMptJzXwz93nc5zn9V9TwCVMmV7w4xsm43dw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", - "dev": true, - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "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, - "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 - }, - "node_modules/cliui/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, - "engines": { - "node": ">=8" - } - }, - "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, - "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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/code-excerpt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", - "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", - "dev": true, - "dependencies": { - "convert-to-spaces": "^2.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "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, - "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 - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "dev": true - }, - "node_modules/concordance": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", - "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", - "dev": true, - "dependencies": { - "date-time": "^3.1.0", - "esutils": "^2.0.3", - "fast-diff": "^1.2.0", - "js-string-escape": "^1.0.1", - "lodash": "^4.17.15", - "md5-hex": "^3.0.1", - "semver": "^7.3.2", - "well-known-symbols": "^2.0.0" - }, - "engines": { - "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" - } - }, - "node_modules/convert-to-spaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", - "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", - "dev": true, - "dependencies": { - "array-find-index": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/date-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", - "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", - "dev": true, - "dependencies": { - "time-zone": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "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 - }, - "node_modules/emittery": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.0.3.tgz", - "integrity": "sha512-tJdCJitoy2lrC2ldJcqN4vkqJ00lT+tOWNT1hBJjO/3FDMJa5TTIiYGCKGkn/WfCyOzUMObeohbVTj00fhiLiA==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "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 - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dependencies": { - "once": "^1.4.0" - } - }, - "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, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/figures": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", - "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^5.0.0", - "is-unicode-supported": "^1.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" - }, - "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, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dev": true, - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" - }, - "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, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.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, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" - }, - "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, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dev": true, - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-by-default": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", - "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", - "dev": true, - "engines": { - "node": ">=10 <11 || >=12 <13 || >=14" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "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==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/irregular-plurals": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", - "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "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, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-error": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.2.tgz", - "integrity": "sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==", - "dev": true - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/js-string-escape": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", - "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/limbo-wasm": { - "resolved": "../pkg", - "link": true - }, - "node_modules/load-json-file": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", - "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/map-age-cleaner": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", - "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", - "dev": true, - "dependencies": { - "p-defer": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/matcher": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", - "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/md5-hex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", - "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", - "dev": true, - "dependencies": { - "blueimp-md5": "^2.10.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/mem": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/mem/-/mem-9.0.2.tgz", - "integrity": "sha512-F2t4YIv9XQUBHt6AOJ0y7lSmP1+cY7Fm1DRh9GClTGzKST7UWLMx6ly9WZdLH/G/ppM5RL4MlQfRT71ri9t19A==", - "dev": true, - "dependencies": { - "map-age-cleaner": "^0.1.3", - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sindresorhus/mem?sponsor=1" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "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, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" - }, - "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 - }, - "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" - }, - "node_modules/node-abi": { - "version": "3.71.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", - "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/nofilter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", - "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", - "dev": true, - "engines": { - "node": ">=12.19" - } - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-defer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", - "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-event": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/p-event/-/p-event-5.0.1.tgz", - "integrity": "sha512-dd589iCQ7m1L0bmC5NLlVYfy3TbBEsMUfWx9PyAgPeIcFZ/E2yaTZ4Rz4MiBmmJShviiftHVXOqfnfzJ6kyMrQ==", - "dev": true, - "dependencies": { - "p-timeout": "^5.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", - "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", - "dev": true, - "dependencies": { - "aggregate-error": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-5.1.0.tgz", - "integrity": "sha512-auFDyzzzGZZZdHz3BtET9VEz0SE/uMEAx7uWfGPucfzEwwe/xH0iVeZibQmANYE/hp9T2+UUZT5m+BKyrDp3Ew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-ms": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-3.0.0.tgz", - "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkg-conf": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-4.0.0.tgz", - "integrity": "sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w==", - "dev": true, - "dependencies": { - "find-up": "^6.0.0", - "load-json-file": "^7.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/plur": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", - "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", - "dev": true, - "dependencies": { - "irregular-plurals": "^3.3.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pretty-ms": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz", - "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", - "dev": true, - "dependencies": { - "parse-ms": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "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==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "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, - "engines": { - "node": ">=0.10.0" - } - }, - "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, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "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" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "dependencies": { - "type-fest": "^0.13.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "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 - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=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==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "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, - "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/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/supertap": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", - "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", - "dev": true, - "dependencies": { - "indent-string": "^5.0.0", - "js-yaml": "^3.14.1", - "serialize-error": "^7.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/temp-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", - "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", - "dev": true, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/time-zone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", - "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "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, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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==" - }, - "node_modules/well-known-symbols": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", - "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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, - "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/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/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, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/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 - }, - "node_modules/wrap-ansi/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, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/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, - "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/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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "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==" - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, - "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, - "engines": { - "node": ">=10" - } - }, - "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, - "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", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "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, - "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 - }, - "node_modules/yargs/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, - "engines": { - "node": ">=8" - } - }, - "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, - "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, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yocto-queue": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", - "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/bindings/wasm/browser-its/package.json b/bindings/wasm/browser-its/package.json deleted file mode 100644 index 1c2070ce5..000000000 --- a/bindings/wasm/browser-its/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "limbo-wasm-integration-tests", - "type": "module", - "private": true, - "scripts": { - "test": "PROVIDER=better-sqlite3 ava tests/test.js && PROVIDER=limbo-wasm ava tests/test.js" - }, - "devDependencies": { - "ava": "^5.3.0" - }, - "dependencies": { - "better-sqlite3": "^8.4.0", - "limbo-wasm": "../pkg" - } -} diff --git a/bindings/wasm/browser-its/tests/test.js b/bindings/wasm/browser-its/tests/test.js deleted file mode 100644 index 5932e7f0e..000000000 --- a/bindings/wasm/browser-its/tests/test.js +++ /dev/null @@ -1,84 +0,0 @@ -import test from "ava"; - -test.beforeEach(async (t) => { - const [db, errorType, provider] = await connect(); - db.exec(` - DROP TABLE IF EXISTS users; - CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT) - `); - db.exec( - "INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.org')" - ); - db.exec( - "INSERT INTO users (id, name, email) VALUES (2, 'Bob', 'bob@example.com')" - ); - t.context = { - db, - errorType, - provider - }; - }); - -test.serial("Statement.raw().all()", async (t) => { - const db = t.context.db; - - const stmt = db.prepare("SELECT * FROM users"); - const expected = [ - [1, "Alice", "alice@example.org"], - [2, "Bob", "bob@example.com"], - ]; - t.deepEqual(stmt.raw().all(), expected); -}); - -test.serial("Statement.raw().get()", async (t) => { - const db = t.context.db; - - const stmt = db.prepare("SELECT * FROM users"); - const expected = [ - 1, "Alice", "alice@example.org" - ]; - t.deepEqual(stmt.raw().get(), expected); - - const emptyStmt = db.prepare("SELECT * FROM users WHERE id = -1"); - t.is(emptyStmt.raw().get(), undefined); -}); - -test.serial("Statement.raw().iterate()", async (t) => { - const db = t.context.db; - - const stmt = db.prepare("SELECT * FROM users"); - const expected = [ - { done: false, value: [1, "Alice", "alice@example.org"] }, - { done: false, value: [2, "Bob", "bob@example.com"] }, - { done: true, value: undefined }, - ]; - - let iter = stmt.raw().iterate(); - t.is(typeof iter[Symbol.iterator], 'function'); - t.deepEqual(iter.next(), expected[0]) - t.deepEqual(iter.next(), expected[1]) - t.deepEqual(iter.next(), expected[2]) - - const emptyStmt = db.prepare("SELECT * FROM users WHERE id = -1"); - t.is(typeof emptyStmt[Symbol.iterator], 'undefined'); - t.throws(() => emptyStmt.next(), { instanceOf: TypeError }); -}); - -const connect = async (path_opt) => { - const path = path_opt ?? "hello.db"; - const provider = process.env.PROVIDER; - if (provider === "limbo-wasm") { - const database = process.env.LIBSQL_DATABASE ?? path; - const x = await import("limbo-wasm"); - const options = {}; - const db = new x.Database(database, options); - return [db, x.SqliteError, provider]; - } - if (provider == "better-sqlite3") { - const x = await import("better-sqlite3"); - const options = {}; - const db = x.default(path, options); - return [db, x.SqliteError, provider]; - } - throw new Error("Unknown provider: " + provider); - }; \ No newline at end of file diff --git a/bindings/wasm/limbo-opfs-test.html b/bindings/wasm/limbo-opfs-test.html index 4d7b23ff2..ec82f9dbf 100644 --- a/bindings/wasm/limbo-opfs-test.html +++ b/bindings/wasm/limbo-opfs-test.html @@ -1,62 +1,83 @@ + + Limbo Test + - - + await waitForMessage(worker, 'success', 'exec'); + worker.postMessage({ + op: 'exec', + sql: ` + INSERT INTO users VALUES (3, 'bill', 'bill@example.com'); + ` + }); + + // Wait for exec success then send prepare + await waitForMessage(worker, 'success', 'exec'); + worker.postMessage({ + op: 'prepare', + sql: 'SELECT * FROM users;' + }); + + const results = await waitForMessage(worker, 'result'); + console.log('Query results:', results); + } + + runTests().catch(console.error); + + From 4bda7803c320cfab3154e7772f0871f71b2623ec Mon Sep 17 00:00:00 2001 From: Elijah Morgan Date: Wed, 1 Jan 2025 16:54:31 -0500 Subject: [PATCH 03/28] Add build for nodejs or web Change how VFS gets loaded based on feature flags --- bindings/wasm/Cargo.toml | 5 +++++ bindings/wasm/README.md | 6 ++++++ bindings/wasm/lib.rs | 29 +++++++++++++++++++++++++++++ bindings/wasm/scripts/build | 9 ++++++++- 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/bindings/wasm/Cargo.toml b/bindings/wasm/Cargo.toml index 041bcef82..7fcf66f27 100644 --- a/bindings/wasm/Cargo.toml +++ b/bindings/wasm/Cargo.toml @@ -17,3 +17,8 @@ limbo_core = { path = "../../core", default-features = false } wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" web-sys = "0.3" + +[features] +web = [] +nodejs = [] +default = ["nodejs"] diff --git a/bindings/wasm/README.md b/bindings/wasm/README.md index 9c033c017..34652e801 100644 --- a/bindings/wasm/README.md +++ b/bindings/wasm/README.md @@ -4,9 +4,15 @@ This source tree contains Limbo Wasm bindings. ## Building +For nodejs ``` ./scripts/build ``` +For web + +``` +./scripts/build web +``` # Browser Support diff --git a/bindings/wasm/lib.rs b/bindings/wasm/lib.rs index 2f226f067..949df3516 100644 --- a/bindings/wasm/lib.rs +++ b/bindings/wasm/lib.rs @@ -303,10 +303,39 @@ impl limbo_core::DatabaseStorage for DatabaseStorage { } } +#[cfg(all(feature = "web", feature = "nodejs"))] +compile_error!("Features 'web' and 'nodejs' cannot be enabled at the same time"); + +#[cfg(feature = "web")] #[wasm_bindgen(module = "/src/web-vfs.js")] extern "C" { type VFS; + #[wasm_bindgen(constructor)] + fn new() -> VFS; + #[wasm_bindgen(method)] + fn open(this: &VFS, path: &str, flags: &str) -> i32; + + #[wasm_bindgen(method)] + fn close(this: &VFS, fd: i32) -> bool; + + #[wasm_bindgen(method)] + fn pwrite(this: &VFS, fd: i32, buffer: &[u8], offset: usize) -> i32; + + #[wasm_bindgen(method)] + fn pread(this: &VFS, fd: i32, buffer: &mut [u8], offset: usize) -> i32; + + #[wasm_bindgen(method)] + fn size(this: &VFS, fd: i32) -> u64; + + #[wasm_bindgen(method)] + fn sync(this: &VFS, fd: i32); +} + +#[cfg(feature = "nodejs")] +#[wasm_bindgen(module = "/vfs.js")] +extern "C" { + type VFS; #[wasm_bindgen(constructor)] fn new() -> VFS; diff --git a/bindings/wasm/scripts/build b/bindings/wasm/scripts/build index 1b2cbc2f8..e39ff6ae7 100755 --- a/bindings/wasm/scripts/build +++ b/bindings/wasm/scripts/build @@ -1,4 +1,11 @@ #!/bin/bash -wasm-pack build --no-pack --target web +TARGET=${1:-nodejs} +FEATURE="nodejs" + +if [ "$TARGET" = "web" ]; then + FEATURE="web" +fi + +wasm-pack build --no-pack --target $TARGET --no-default-features --features $FEATURE cp package.json pkg/package.json From d51ca2150c12711ebeb27f29e593039c07be2d71 Mon Sep 17 00:00:00 2001 From: Elijah Morgan Date: Fri, 3 Jan 2025 14:38:34 -0500 Subject: [PATCH 04/28] Cleanup logging, move html files General cleanup of cruft, deleted dead code, moved html files to clean up dir. --- bindings/wasm/html/index.html | 9 ++ bindings/wasm/{ => html}/limbo-opfs-test.html | 0 bindings/wasm/{ => html}/limbo-test.html | 0 bindings/wasm/index.html | 82 ------------------- bindings/wasm/lib.rs | 1 - bindings/wasm/scripts/build | 1 + bindings/wasm/src/limbo-worker.js | 21 ++++- bindings/wasm/src/opfs-sync-proxy.js | 36 ++++++-- bindings/wasm/src/opfs-worker.js | 44 ---------- bindings/wasm/src/opfs.js | 36 ++++++-- bindings/wasm/src/web-vfs.js | 5 +- bindings/wasm/test/setup.js | 14 ++-- bindings/wasm/test/test.html | 37 --------- bindings/wasm/vite.config.js | 5 +- core/lib.rs | 3 - 15 files changed, 97 insertions(+), 197 deletions(-) create mode 100644 bindings/wasm/html/index.html rename bindings/wasm/{ => html}/limbo-opfs-test.html (100%) rename bindings/wasm/{ => html}/limbo-test.html (100%) delete mode 100644 bindings/wasm/index.html delete mode 100644 bindings/wasm/test/test.html diff --git a/bindings/wasm/html/index.html b/bindings/wasm/html/index.html new file mode 100644 index 000000000..0097d61f8 --- /dev/null +++ b/bindings/wasm/html/index.html @@ -0,0 +1,9 @@ + + + + + + diff --git a/bindings/wasm/limbo-opfs-test.html b/bindings/wasm/html/limbo-opfs-test.html similarity index 100% rename from bindings/wasm/limbo-opfs-test.html rename to bindings/wasm/html/limbo-opfs-test.html diff --git a/bindings/wasm/limbo-test.html b/bindings/wasm/html/limbo-test.html similarity index 100% rename from bindings/wasm/limbo-test.html rename to bindings/wasm/html/limbo-test.html diff --git a/bindings/wasm/index.html b/bindings/wasm/index.html deleted file mode 100644 index f3246b19e..000000000 --- a/bindings/wasm/index.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/bindings/wasm/lib.rs b/bindings/wasm/lib.rs index 949df3516..c01b9e986 100644 --- a/bindings/wasm/lib.rs +++ b/bindings/wasm/lib.rs @@ -48,7 +48,6 @@ impl Database { #[wasm_bindgen] pub fn exec(&self, _sql: &str) { let _res = self.conn.execute(_sql).unwrap(); - // Statement::new(RefCell::new(stmt), false) } #[wasm_bindgen] diff --git a/bindings/wasm/scripts/build b/bindings/wasm/scripts/build index e39ff6ae7..09691006c 100755 --- a/bindings/wasm/scripts/build +++ b/bindings/wasm/scripts/build @@ -1,5 +1,6 @@ #!/bin/bash +# get target as argument from cli, defaults to nodejs if no argument is supplied TARGET=${1:-nodejs} FEATURE="nodejs" diff --git a/bindings/wasm/src/limbo-worker.js b/bindings/wasm/src/limbo-worker.js index f91cf6115..d8598303f 100644 --- a/bindings/wasm/src/limbo-worker.js +++ b/bindings/wasm/src/limbo-worker.js @@ -28,7 +28,7 @@ initAll().then(() => { break; } case "exec": { - console.log(e.data.sql); + log(e.data.sql); db.exec(e.data.sql); self.postMessage({ type: "success", op: "exec" }); break; @@ -53,3 +53,22 @@ initAll().then(() => { self.postMessage({ type: "error", error: error.toString() }); }); +// logLevel: +// +// 0 = no logging output +// 1 = only errors +// 2 = warnings and errors +// 3 = debug, warnings, and errors +const logLevel = 1; + +const loggers = { + 0: console.error.bind(console), + 1: console.warn.bind(console), + 2: console.log.bind(console), +}; +const logImpl = (level, ...args) => { + if (logLevel > level) loggers[level]("OPFS asyncer:", ...args); +}; +const log = (...args) => logImpl(2, ...args); +const warn = (...args) => logImpl(1, ...args); +const error = (...args) => logImpl(0, ...args); diff --git a/bindings/wasm/src/opfs-sync-proxy.js b/bindings/wasm/src/opfs-sync-proxy.js index 2bc1a9105..6bac0a759 100644 --- a/bindings/wasm/src/opfs-sync-proxy.js +++ b/bindings/wasm/src/opfs-sync-proxy.js @@ -8,9 +8,9 @@ let nextFd = 1; self.postMessage("ready"); onmessage = async (e) => { - console.log("handle message: ", e.data); + log("handle message: ", e.data); if (e.data.cmd === "init") { - console.log("init"); + log("init"); transferBuffer = e.data.transferBuffer; statusBuffer = e.data.statusBuffer; @@ -33,7 +33,7 @@ self.onerror = (error) => { }; function handleCommand(msg) { - console.log(`handle message: ${msg.cmd}`); + log(`handle message: ${msg.cmd}`); switch (msg.cmd) { case "open": return handleOpen(msg.path); @@ -74,10 +74,10 @@ function handleRead(fd, offset, size) { const handle = handles.get(fd); const readBuffer = new ArrayBuffer(size); const readSize = handle.read(readBuffer, { at: offset }); - console.log("opfssync read: size: ", readBuffer.byteLength); + log("opfssync read: size: ", readBuffer.byteLength); const tmp = new Uint8Array(readBuffer); - console.log("opfssync read buffer: ", [...tmp]); + log("opfssync read buffer: ", [...tmp]); transferArray.set(tmp); @@ -85,8 +85,8 @@ function handleRead(fd, offset, size) { } function handleWrite(fd, buffer, offset) { - console.log("opfssync buffer size:", buffer.byteLength); - console.log("opfssync write buffer: ", [...buffer]); + log("opfssync buffer size:", buffer.byteLength); + log("opfssync write buffer: ", [...buffer]); const handle = handles.get(fd); const size = handle.write(buffer, { at: offset }); return { success: true, length: size }; @@ -107,10 +107,30 @@ function sendResult(result) { if (result?.fd) { statusView.setInt32(4, result.fd, true); } else { - console.log("opfs-sync-proxy: result.length: ", result.length); + log("opfs-sync-proxy: result.length: ", result.length); statusView.setInt32(4, result?.length || 0, true); } Atomics.store(statusArray, 0, 1); Atomics.notify(statusArray, 0); } + +// logLevel: +// +// 0 = no logging output +// 1 = only errors +// 2 = warnings and errors +// 3 = debug, warnings, and errors +const logLevel = 1; + +const loggers = { + 0: console.error.bind(console), + 1: console.warn.bind(console), + 2: console.log.bind(console), +}; +const logImpl = (level, ...args) => { + if (logLevel > level) loggers[level]("OPFS asyncer:", ...args); +}; +const log = (...args) => logImpl(2, ...args); +const warn = (...args) => logImpl(1, ...args); +const error = (...args) => logImpl(0, ...args); diff --git a/bindings/wasm/src/opfs-worker.js b/bindings/wasm/src/opfs-worker.js index dd58d3899..b9a1c0ee3 100644 --- a/bindings/wasm/src/opfs-worker.js +++ b/bindings/wasm/src/opfs-worker.js @@ -55,47 +55,3 @@ onmessage = async function (e) { }; console.log("opfs-worker.js"); -// checkCompatibility(); - -// // In VFS class -// this.worker.onerror = (error) => { -// console.error("Worker stack:", error.error?.stack || error.message); -// }; - -// checkCompatibility(); -// -// async function checkCompatibility() { -// console.log("begin check compatibility"); -// // OPFS API check -// if (!("storage" in navigator && "getDirectory" in navigator.storage)) { -// throw new Error("OPFS API not supported"); -// } -// -// // SharedArrayBuffer support check -// if (typeof SharedArrayBuffer === "undefined") { -// throw new Error("SharedArrayBuffer not supported"); -// } -// -// // Atomics API check -// if (typeof Atomics === "undefined") { -// throw new Error("Atomics API not supported"); -// } -// -// // Permission check for OPFS -// try { -// const root = await navigator.storage.getDirectory(); -// await root.getFileHandle("test.txt", { create: true }); -// } catch (e) { -// console.log(e); -// console.log("throwing OPFS permission Denied"); -// throw new Error("OPFS permission denied"); -// } -// -// // Cross-Origin-Isolation check for SharedArrayBuffer -// if (!crossOriginIsolated) { -// throw new Error("Cross-Origin-Isolation required for SharedArrayBuffer"); -// } -// -// console.log("done check compatibility"); -// return true; -// } diff --git a/bindings/wasm/src/opfs.js b/bindings/wasm/src/opfs.js index 104cf3cfc..01c6d2175 100644 --- a/bindings/wasm/src/opfs.js +++ b/bindings/wasm/src/opfs.js @@ -32,7 +32,7 @@ class VFS { initWorker() { return new Promise((resolve) => { this.worker.addEventListener("message", (e) => { - console.log("eventListener: ", e.data); + log("eventListener: ", e.data); resolve(); }, { once: true }); @@ -50,8 +50,8 @@ class VFS { Atomics.wait(this.statusArray, 0, 0); const result = this.statusView.getInt32(4, true); - console.log("opfs.js open result: ", result); - console.log("opfs.js open result type: ", typeof result); + log("opfs.js open result: ", result); + log("opfs.js open result type: ", typeof result); return result; } @@ -86,7 +86,7 @@ class VFS { new Uint8Array(this.transferBuffer, 0, readSize), bytesRead, ); - console.log("opfs pread buffer: ", [...buffer]); + log("opfs pread buffer: ", [...buffer]); bytesRead += readSize; if (readSize < chunkSize) break; @@ -96,7 +96,7 @@ class VFS { } pwrite(fd, buffer, offset) { - console.log("write buffer size: ", buffer.byteLength); + log("write buffer size: ", buffer.byteLength); Atomics.store(this.statusArray, 0, 0); this.worker.postMessage({ cmd: "write", @@ -106,7 +106,7 @@ class VFS { }); Atomics.wait(this.statusArray, 0, 0); - console.log( + log( "opfs pwrite length statusview: ", this.statusView.getInt32(4, true), ); @@ -119,8 +119,8 @@ class VFS { Atomics.wait(this.statusArray, 0, 0); const result = this.statusView.getInt32(4, true); - console.log("opfs.js size result: ", result); - console.log("opfs.js size result type: ", typeof result); + log("opfs.js size result: ", result); + log("opfs.js size result type: ", typeof result); return BigInt(result); } @@ -131,4 +131,24 @@ class VFS { } } +// logLevel: +// +// 0 = no logging output +// 1 = only errors +// 2 = warnings and errors +// 3 = debug, warnings, and errors +const logLevel = 1; + +const loggers = { + 0: console.error.bind(console), + 1: console.warn.bind(console), + 2: console.log.bind(console), +}; +const logImpl = (level, ...args) => { + if (logLevel > level) loggers[level]("OPFS asyncer:", ...args); +}; +const log = (...args) => logImpl(2, ...args); +const warn = (...args) => logImpl(1, ...args); +const error = (...args) => logImpl(0, ...args); + export { VFS }; diff --git a/bindings/wasm/src/web-vfs.js b/bindings/wasm/src/web-vfs.js index 4ba0cce4a..a24bc75d5 100644 --- a/bindings/wasm/src/web-vfs.js +++ b/bindings/wasm/src/web-vfs.js @@ -4,10 +4,7 @@ export class VFS { } open(path, flags) { - const result = self.vfs.open(path); - consol.log("webvfs open result: ", result); - consol.log("webvfs open result type: ", typeof result); - return result; + return self.vfs.open(path); } close(fd) { diff --git a/bindings/wasm/test/setup.js b/bindings/wasm/test/setup.js index 3ac2a64fc..9e22bb660 100644 --- a/bindings/wasm/test/setup.js +++ b/bindings/wasm/test/setup.js @@ -1,4 +1,4 @@ -import { afterEach, beforeEach } from "vitest"; +import { afterAll, afterEach, beforeAll, beforeEach } from "vitest"; import { chromium } from "playwright"; import { createServer } from "vite"; @@ -7,8 +7,7 @@ let context; let page; let server; -beforeEach(async () => { - // Start Vite dev server +beforeAll(async () => { server = await createServer({ configFile: "./vite.config.js", root: ".", @@ -17,17 +16,20 @@ beforeEach(async () => { }, }); await server.listen(); - browser = await chromium.launch(); +}); + +beforeEach(async () => { context = await browser.newContext(); page = await context.newPage(); - globalThis.__page__ = page; }); afterEach(async () => { await context.close(); +}); + +afterAll(async () => { await browser.close(); await server.close(); }); - diff --git a/bindings/wasm/test/test.html b/bindings/wasm/test/test.html deleted file mode 100644 index ad2534032..000000000 --- a/bindings/wasm/test/test.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - diff --git a/bindings/wasm/vite.config.js b/bindings/wasm/vite.config.js index 0e84f547f..e92b7f354 100644 --- a/bindings/wasm/vite.config.js +++ b/bindings/wasm/vite.config.js @@ -2,6 +2,8 @@ import { defineConfig } from "vite"; import wasm from "vite-plugin-wasm"; export default defineConfig({ + publicDir: "./html", + root: ".", plugins: [wasm()], test: { globals: true, @@ -28,7 +30,4 @@ export default defineConfig({ }, }, }, - // worker: { - // format: "es", - // }, }); diff --git a/core/lib.rs b/core/lib.rs index e79a1bf98..5ef48e742 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -35,7 +35,6 @@ use storage::sqlite3_ondisk::{DatabaseHeader, DATABASE_HEADER_SIZE}; pub use storage::wal::WalFile; pub use storage::wal::WalFileShared; use util::parse_schema_rows; -// use web_sys::console; // Add to dependencies in Cargo.toml use translate::select::prepare_select_plan; use types::OwnedValue; @@ -81,8 +80,6 @@ impl Database { pub fn open_file(io: Arc, path: &str) -> Result> { use storage::wal::WalFileShared; - // console::log_1(&"Hello from Rust!".into()); - let file = io.open_file(path, io::OpenFlags::Create, true)?; maybe_init_database_file(&file, &io)?; let page_io = Rc::new(FileStorage::new(file)); From ad9d372e9ca4a5ccfa5df677be15f976785b2fa8 Mon Sep 17 00:00:00 2001 From: Elijah Morgan Date: Mon, 6 Jan 2025 18:47:08 -0500 Subject: [PATCH 05/28] cleanup remove happy-dom try to fix some issues with tests add wasm-pack as devDependency update versions --- bindings/wasm/package-lock.json | 2733 +++++++++--------------------- bindings/wasm/package.json | 13 +- bindings/wasm/scripts/build | 2 +- bindings/wasm/test/limbo.test.js | 1 + bindings/wasm/test/opfs.test.js | 241 ++- bindings/wasm/test/setup.js | 9 +- 6 files changed, 887 insertions(+), 2112 deletions(-) diff --git a/bindings/wasm/package-lock.json b/bindings/wasm/package-lock.json index ee43543ee..5028485f2 100644 --- a/bindings/wasm/package-lock.json +++ b/bindings/wasm/package-lock.json @@ -1,95 +1,28 @@ { "name": "limbo-wasm", - "version": "0.0.10", + "version": "0.0.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "limbo-wasm", - "version": "0.0.10", + "version": "0.0.11", "license": "MIT", "devDependencies": { - "@playwright/test": "^1.40.0", - "@vitest/ui": "^1.0.0", - "happy-dom": "^12.0.0", - "playwright": "^1.40.0", - "vite": "^5.0.0", + "@playwright/test": "^1.49.1", + "@vitest/ui": "^2.1.8", + "happy-dom": "^16.3.0", + "playwright": "^1.49.1", + "vite": "^6.0.7", "vite-plugin-wasm": "^3.4.1", - "vitest": "^1.0.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "vitest": "^2.1.8", + "wasm-pack": "^0.13.1" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", "cpu": [ "arm64" ], @@ -100,326 +33,7 @@ "darwin" ], "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18" } }, "node_modules/@jridgewell/sourcemap-codec": { @@ -429,44 +43,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@playwright/test": { "version": "1.49.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", @@ -490,38 +66,10 @@ "dev": true, "license": "MIT" }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz", - "integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz", - "integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz", - "integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.0.tgz", + "integrity": "sha512-617pd92LhdA9+wpixnzsyhVft3szYiN16aNUMzVkf2N+yAk8UXY226Bfp36LvxYTUt7MO/ycqGFjQgJ0wlMaWQ==", "cpu": [ "arm64" ], @@ -532,237 +80,6 @@ "darwin" ] }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz", - "integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz", - "integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz", - "integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz", - "integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz", - "integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz", - "integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz", - "integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz", - "integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz", - "integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz", - "integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz", - "integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz", - "integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz", - "integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz", - "integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz", - "integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz", - "integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -770,195 +87,165 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "node_modules/@vitest/expect": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", + "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "undici-types": "~6.20.0" + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/expect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", - "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "node_modules/@vitest/pretty-format": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", + "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", - "chai": "^4.3.10" + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", - "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", + "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "1.6.0", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" + "@vitest/utils": "2.1.8", + "pathe": "^1.1.2" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", - "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz", + "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==", "dev": true, "license": "MIT", "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" + "@vitest/pretty-format": "2.1.8", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", - "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", + "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^2.2.0" + "tinyspy": "^3.0.2" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/ui": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.6.0.tgz", - "integrity": "sha512-k3Lyo+ONLOgylctiGovRKy7V4+dIN2yxstX3eY5cWFXH6WP+ooVX79YSyi0GagdTQzLmT43BF27T0s6dOIPBXA==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-2.1.8.tgz", + "integrity": "sha512-5zPJ1fs0ixSVSs5+5V2XJjXLmNzjugHRyV11RqxYVR+oMcogZ9qTuSfKW+OcTV0JeFNznI83BNylzH6SSNJ1+w==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "1.6.0", - "fast-glob": "^3.3.2", - "fflate": "^0.8.1", - "flatted": "^3.2.9", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "sirv": "^2.0.4" + "@vitest/utils": "2.1.8", + "fflate": "^0.8.2", + "flatted": "^3.3.1", + "pathe": "^1.1.2", + "sirv": "^3.0.0", + "tinyglobby": "^0.2.10", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.6.0" + "vitest": "2.1.8" } }, "node_modules/@vitest/utils": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", - "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", + "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", "dev": true, "license": "MIT", "dependencies": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" + "@vitest/pretty-format": "2.1.8", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, "engines": { - "node": ">=0.4.0" + "node": ">=12" } }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.11.0" + "follow-redirects": "^1.14.8" + } + }, + "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/binary-install": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-1.1.0.tgz", + "integrity": "sha512-rkwNGW+3aQVSZoD0/o3mfPN6Yxh3Id0R/xzTVBVVpGNlVz8EGwusksxRlbk/A5iKTZt9zkMn3qIqmAt3vpfbzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^0.26.1", + "rimraf": "^3.0.2", + "tar": "^6.1.11" }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 14" - } - }, - "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "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==", + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/cac": { @@ -972,112 +259,49 @@ } }, "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", + "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", "dev": true, "license": "MIT", "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=12" } }, "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, "engines": { - "node": "*" + "node": ">= 16" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, + "license": "ISC", "engines": { - "node": ">= 0.8" + "node": ">=10" } }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "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/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.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cssstyle": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", - "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "rrweb-cssom": "^0.7.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -1096,67 +320,27 @@ } } }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, "engines": { "node": ">=6" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.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" - } + "license": "MIT" }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1164,32 +348,34 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" } }, "node_modules/estree-walker": { @@ -1202,55 +388,29 @@ "@types/estree": "^1.0.0" } }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=12.0.0" } }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", "dev": true, "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "peerDependencies": { + "picomatch": "^3 || ^4" }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", - "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, "node_modules/fflate": { @@ -1260,19 +420,6 @@ "dev": true, "license": "MIT" }, - "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/flatted": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", @@ -1280,321 +427,122 @@ "dev": true, "license": "ISC" }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "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, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 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" + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, "license": "MIT", "engines": { - "node": "*" - } - }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/happy-dom": { - "version": "12.10.3", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-12.10.3.tgz", - "integrity": "sha512-JzUXOh0wdNGY54oKng5hliuBkq/+aT1V3YpTM+lrN/GoLQTANZsMaIvmHiHe612rauHvPJnDZkZ+5GZR++1Abg==", - "dev": true, - "license": "MIT", - "dependencies": { - "css.escape": "^1.5.1", - "entities": "^4.5.0", - "iconv-lite": "^0.6.3", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0" - } - }, - "node_modules/happy-dom/node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/happy-dom/node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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-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-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsdom": { - "version": "25.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", - "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "cssstyle": "^4.1.0", - "data-urls": "^5.0.0", - "decimal.js": "^10.4.3", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.12", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.7.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.0.0", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^2.11.2" + "node": ">=4.0" }, "peerDependenciesMeta": { - "canvas": { + "debug": { "optional": true } } }, - "node_modules/local-pkg": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", - "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "mlly": "^1.7.3", - "pkg-types": "^1.2.1" + "minipass": "^3.0.0" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" + "node": ">= 8" } }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "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/happy-dom": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-16.3.0.tgz", + "integrity": "sha512-Q71RaIhyS21vhW17Tpa5W36yqQXIlE1TZ0A0Gguts8PShUSQE/7fBgxYGxgm3+5y0gF6afdlAVHLQqgrIcfRzg==", "dev": true, "license": "MIT", "dependencies": { - "get-func-name": "^2.0.1" + "webidl-conversions": "^7.0.0", + "whatwg-mimetype": "^3.0.0" + }, + "engines": { + "node": ">=18.0.0" } }, + "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/loupe": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "dev": true, + "license": "MIT" + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -1605,88 +553,67 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "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==", + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT" + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, "engines": { "node": ">= 8" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "yallist": "^4.0.0" }, "engines": { - "node": ">=8.6" + "node": ">=8" } }, - "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==", + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "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", - "optional": true, - "peer": true, - "dependencies": { - "mime-db": "1.52.0" + "bin": { + "mkdirp": "bin/cmd.js" }, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mlly": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.3.tgz", - "integrity": "sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.14.0", - "pathe": "^1.1.2", - "pkg-types": "^1.2.1", - "ufo": "^1.5.4" + "node": ">=10" } }, "node_modules/mrmime": { @@ -1725,99 +652,24 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "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": "MIT", + "license": "ISC", "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "wrappy": "1" } }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "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": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nwsapi": { - "version": "2.2.16", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", - "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "entities": "^4.5.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "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": ">=0.10.0" } }, "node_modules/pathe": { @@ -1828,13 +680,13 @@ "license": "MIT" }, "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", "dev": true, "license": "MIT", "engines": { - "node": "*" + "node": ">= 14.16" } }, "node_modules/picocolors": { @@ -1845,30 +697,18 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "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": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pkg-types": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz", - "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.2", - "pathe": "^1.1.2" - } - }, "node_modules/playwright": { "version": "1.49.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", @@ -1901,21 +741,6 @@ "node": ">=18" } }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/postcss": { "version": "8.4.49", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", @@ -1945,76 +770,27 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "glob": "^7.1.3" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "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/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rollup": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz", - "integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==", + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.0.tgz", + "integrity": "sha512-sDnr1pcjTgUT69qBksNF1N1anwfbyYG6TBQ22b03bII8EdiUQ7J0TlozVaTMjT/eEJAO49e1ndV7t+UZfL1+vA==", "dev": true, "license": "MIT", "dependencies": { @@ -2028,106 +804,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.29.1", - "@rollup/rollup-android-arm64": "4.29.1", - "@rollup/rollup-darwin-arm64": "4.29.1", - "@rollup/rollup-darwin-x64": "4.29.1", - "@rollup/rollup-freebsd-arm64": "4.29.1", - "@rollup/rollup-freebsd-x64": "4.29.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.29.1", - "@rollup/rollup-linux-arm-musleabihf": "4.29.1", - "@rollup/rollup-linux-arm64-gnu": "4.29.1", - "@rollup/rollup-linux-arm64-musl": "4.29.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.29.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.29.1", - "@rollup/rollup-linux-riscv64-gnu": "4.29.1", - "@rollup/rollup-linux-s390x-gnu": "4.29.1", - "@rollup/rollup-linux-x64-gnu": "4.29.1", - "@rollup/rollup-linux-x64-musl": "4.29.1", - "@rollup/rollup-win32-arm64-msvc": "4.29.1", - "@rollup/rollup-win32-ia32-msvc": "4.29.1", - "@rollup/rollup-win32-x64-msvc": "4.29.1", + "@rollup/rollup-android-arm-eabi": "4.30.0", + "@rollup/rollup-android-arm64": "4.30.0", + "@rollup/rollup-darwin-arm64": "4.30.0", + "@rollup/rollup-darwin-x64": "4.30.0", + "@rollup/rollup-freebsd-arm64": "4.30.0", + "@rollup/rollup-freebsd-x64": "4.30.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.30.0", + "@rollup/rollup-linux-arm-musleabihf": "4.30.0", + "@rollup/rollup-linux-arm64-gnu": "4.30.0", + "@rollup/rollup-linux-arm64-musl": "4.30.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.30.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.30.0", + "@rollup/rollup-linux-riscv64-gnu": "4.30.0", + "@rollup/rollup-linux-s390x-gnu": "4.30.0", + "@rollup/rollup-linux-x64-gnu": "4.30.0", + "@rollup/rollup-linux-x64-musl": "4.30.0", + "@rollup/rollup-win32-arm64-msvc": "4.30.0", + "@rollup/rollup-win32-ia32-msvc": "4.30.0", + "@rollup/rollup-win32-x64-msvc": "4.30.0", "fsevents": "~2.3.2" } }, - "node_modules/rrweb-cssom": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", - "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "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", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "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/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "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/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -2135,23 +833,10 @@ "dev": true, "license": "ISC" }, - "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/sirv": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", - "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz", + "integrity": "sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==", "dev": true, "license": "MIT", "dependencies": { @@ -2160,7 +845,7 @@ "totalist": "^3.0.0" }, "engines": { - "node": ">= 10" + "node": ">=18" } }, "node_modules/source-map-js": { @@ -2187,41 +872,24 @@ "dev": true, "license": "MIT" }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-literal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", - "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", - "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "js-tokens": "^9.0.1" + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/antfu" + "engines": { + "node": ">=10" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -2229,10 +897,41 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", + "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/tinypool": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", - "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", "dev": true, "license": "MIT", "engines": { @@ -2240,52 +939,15 @@ } }, "node_modules/tinyspy": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", - "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" } }, - "node_modules/tldts": { - "version": "6.1.70", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.70.tgz", - "integrity": "sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tldts-core": "^6.1.70" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "6.1.70", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.70.tgz", - "integrity": "sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "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/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -2296,63 +958,158 @@ "node": ">=6" } }, - "node_modules/tough-cookie": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", - "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", - "dev": true, - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tr46": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/ufo": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", - "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/vite": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz", + "integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.24.2", + "postcss": "^8.4.49", + "rollup": "^4.23.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", + "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-node/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite-node/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vite-node/node_modules/vite": { "version": "5.4.11", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", @@ -2412,29 +1169,6 @@ } } }, - "node_modules/vite-node": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", - "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/vite-plugin-wasm": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/vite-plugin-wasm/-/vite-plugin-wasm-3.4.1.tgz", @@ -2446,32 +1180,32 @@ } }, "node_modules/vitest": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", - "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", + "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "1.6.0", - "@vitest/runner": "1.6.0", - "@vitest/snapshot": "1.6.0", - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", - "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.3", + "@vitest/expect": "2.1.8", + "@vitest/mocker": "2.1.8", + "@vitest/pretty-format": "^2.1.8", + "@vitest/runner": "2.1.8", + "@vitest/snapshot": "2.1.8", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", "vite": "^5.0.0", - "vite-node": "1.6.0", - "why-is-node-running": "^2.2.2" + "vite-node": "2.1.8", + "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" @@ -2485,8 +1219,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.0", - "@vitest/ui": "1.6.0", + "@vitest/browser": "2.1.8", + "@vitest/ui": "2.1.8", "happy-dom": "*", "jsdom": "*" }, @@ -2511,19 +1245,161 @@ } } }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "peer": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", + "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", + "dev": true, + "license": "MIT", "dependencies": { - "xml-name-validator": "^5.0.0" + "@vitest/spy": "2.1.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=18" + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest/node_modules/vite": { + "version": "5.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", + "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/wasm-pack": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.13.1.tgz", + "integrity": "sha512-P9exD4YkjpDbw68xUhF3MDm/CC/3eTmmthyG5bHJ56kalxOTewOunxTke4SyF8MTXV6jUtNjXggPgrGmMtczGg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "binary-install": "^1.0.1" + }, + "bin": { + "wasm-pack": "run.js" } }, "node_modules/webidl-conversions": { @@ -2536,63 +1412,14 @@ "node": ">=12" } }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", - "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tr46": "^5.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "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": ">=12" } }, "node_modules/why-is-node-running": { @@ -2612,63 +1439,19 @@ "node": ">=8" } }, - "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "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": "MIT", - "optional": true, - "peer": true, - "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 - } - } + "license": "ISC" }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/yocto-queue": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", - "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "ISC" } } } diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index b2d1e4221..7fcc1e9e4 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -24,12 +24,13 @@ "test:ui": "vitest --ui" }, "devDependencies": { - "@playwright/test": "^1.40.0", - "@vitest/ui": "^1.0.0", - "happy-dom": "^12.0.0", - "playwright": "^1.40.0", - "vite": "^5.0.0", + "@playwright/test": "^1.49.1", + "@vitest/ui": "^2.1.8", + "happy-dom": "^16.3.0", + "playwright": "^1.49.1", + "vite": "^6.0.7", "vite-plugin-wasm": "^3.4.1", - "vitest": "^1.0.0" + "vitest": "^2.1.8", + "wasm-pack": "^0.13.1" } } diff --git a/bindings/wasm/scripts/build b/bindings/wasm/scripts/build index 09691006c..4cd7a48d2 100755 --- a/bindings/wasm/scripts/build +++ b/bindings/wasm/scripts/build @@ -8,5 +8,5 @@ if [ "$TARGET" = "web" ]; then FEATURE="web" fi -wasm-pack build --no-pack --target $TARGET --no-default-features --features $FEATURE +npx wasm-pack build --no-pack --target $TARGET --no-default-features --features $FEATURE cp package.json pkg/package.json diff --git a/bindings/wasm/test/limbo.test.js b/bindings/wasm/test/limbo.test.js index 37e1f983f..322f11e8c 100644 --- a/bindings/wasm/test/limbo.test.js +++ b/bindings/wasm/test/limbo.test.js @@ -1,6 +1,7 @@ import { expect, test } from "vitest"; test("basic database operations", async () => { + await globalThis.beforeEachPromise; const page = globalThis.__page__; await page.goto("http://localhost:5173/limbo-test.html"); diff --git a/bindings/wasm/test/opfs.test.js b/bindings/wasm/test/opfs.test.js index cce0c29bb..b7726f8e5 100644 --- a/bindings/wasm/test/opfs.test.js +++ b/bindings/wasm/test/opfs.test.js @@ -1,144 +1,131 @@ -import { expect, test } from "vitest"; +import { beforeEach, describe, expect, test } from "vitest"; -test("basic read/write functionality", async () => { - const page = globalThis.__page__; - await page.goto("http://localhost:5173/index.html"); - - page.on("console", (msg) => console.log(msg.text())); - - const result = await page.evaluate(async () => { - const vfs = new window.VFSInterface("/src/opfs-worker.js"); - let fd; - - try { - fd = await vfs.open("test.txt", {}); - const writeData = new Uint8Array([1, 2, 3, 4]); - const bytesWritten = await vfs.pwrite(fd, writeData, 0); - const readData = new Uint8Array(4); - const bytesRead = await vfs.pread(fd, readData, 0); - await vfs.close(fd); - - return { fd, bytesWritten, bytesRead, readData: Array.from(readData) }; - } catch (error) { - if (fd !== undefined) await vfs.close(fd); - return { error: error.message }; - } +describe.sequential("OPFS Tests", () => { + beforeEach(async () => { + await globalThis.beforeEachPromise; + const page = globalThis.__page__; + await page.goto("http://localhost:5173/index.html"); + await page.waitForFunction(() => window.VFSInterface !== undefined); }); - if (result.error) throw new Error(`Test failed: ${result.error}`); - expect(result.fd).toBe(1); - expect(result.bytesWritten).toBe(4); - expect(result.bytesRead).toBe(4); - expect(result.readData).toEqual([1, 2, 3, 4]); -}); + test("basic read/write functionality", async () => { + const page = globalThis.__page__; + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + try { + fd = await vfs.open("test.txt", {}); + const writeData = new Uint8Array([1, 2, 3, 4]); + const bytesWritten = await vfs.pwrite(fd, writeData, 0); + const readData = new Uint8Array(4); + const bytesRead = await vfs.pread(fd, readData, 0); + await vfs.close(fd); + return { fd, bytesWritten, bytesRead, readData: Array.from(readData) }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); -test("larger data read/write", async () => { - const page = globalThis.__page__; - await page.goto("http://localhost:5173/index.html"); - - const result = await page.evaluate(async () => { - const vfs = new window.VFSInterface("/src/opfs-worker.js"); - let fd; - - try { - fd = await vfs.open("large.txt", {}); - const writeData = new Uint8Array(1024).map((_, i) => i % 256); - const bytesWritten = await vfs.pwrite(fd, writeData, 0); - const readData = new Uint8Array(1024); - const bytesRead = await vfs.pread(fd, readData, 0); - await vfs.close(fd); - - return { bytesWritten, bytesRead, readData: Array.from(readData) }; - } catch (error) { - if (fd !== undefined) await vfs.close(fd); - return { error: error.message }; - } + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(result.fd).toBe(1); + expect(result.bytesWritten).toBe(4); + expect(result.bytesRead).toBe(4); + expect(result.readData).toEqual([1, 2, 3, 4]); }); - if (result.error) throw new Error(`Test failed: ${result.error}`); - expect(result.bytesWritten).toBe(1024); - expect(result.bytesRead).toBe(1024); - expect(result.readData).toEqual( - Array.from({ length: 1024 }, (_, i) => i % 256), - ); -}); + test("larger data read/write", async () => { + const page = globalThis.__page__; + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + try { + fd = await vfs.open("large.txt", {}); + const writeData = new Uint8Array(1024).map((_, i) => i % 256); + const bytesWritten = await vfs.pwrite(fd, writeData, 0); + const readData = new Uint8Array(1024); + const bytesRead = await vfs.pread(fd, readData, 0); + await vfs.close(fd); + return { bytesWritten, bytesRead, readData: Array.from(readData) }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); -test("partial reads and writes", async () => { - const page = globalThis.__page__; - await page.goto("http://localhost:5173/index.html"); - - const result = await page.evaluate(async () => { - const vfs = new window.VFSInterface("/src/opfs-worker.js"); - let fd; - - try { - fd = await vfs.open("partial.txt", {}); - - // Write data in chunks - const writeData1 = new Uint8Array([1, 2, 3, 4]); - const writeData2 = new Uint8Array([5, 6, 7, 8]); - await vfs.pwrite(fd, writeData1, 0); - await vfs.pwrite(fd, writeData2, 4); - - // Read partial chunks - const readData1 = new Uint8Array(2); - const readData2 = new Uint8Array(4); - const readData3 = new Uint8Array(2); - - await vfs.pread(fd, readData1, 0); // Should read [1,2] - await vfs.pread(fd, readData2, 2); // Should read [3,4,5,6] - await vfs.pread(fd, readData3, 6); // Should read [7,8] - - await vfs.close(fd); - - return { - readData1: Array.from(readData1), - readData2: Array.from(readData2), - readData3: Array.from(readData3), - }; - } catch (error) { - if (fd !== undefined) await vfs.close(fd); - return { error: error.message }; - } + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(result.bytesWritten).toBe(1024); + expect(result.bytesRead).toBe(1024); + expect(result.readData).toEqual( + Array.from({ length: 1024 }, (_, i) => i % 256), + ); }); - if (result.error) throw new Error(`Test failed: ${result.error}`); - expect(result.readData1).toEqual([1, 2]); - expect(result.readData2).toEqual([3, 4, 5, 6]); - expect(result.readData3).toEqual([7, 8]); -}); + test("partial reads and writes", async () => { + const page = globalThis.__page__; + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + try { + fd = await vfs.open("partial.txt", {}); -test("file size operations", async () => { - const page = globalThis.__page__; - await page.goto("http://localhost:5173/index.html"); + const writeData1 = new Uint8Array([1, 2, 3, 4]); + const writeData2 = new Uint8Array([5, 6, 7, 8]); + await vfs.pwrite(fd, writeData1, 0); + await vfs.pwrite(fd, writeData2, 4); - const result = await page.evaluate(async () => { - const vfs = new window.VFSInterface("/src/opfs-worker.js"); - let fd; + const readData1 = new Uint8Array(2); + const readData2 = new Uint8Array(4); + const readData3 = new Uint8Array(2); - try { - fd = await vfs.open("size.txt", {}); + await vfs.pread(fd, readData1, 0); + await vfs.pread(fd, readData2, 2); + await vfs.pread(fd, readData3, 6); - // First write - const writeData1 = new Uint8Array([1, 2, 3, 4]); - await vfs.pwrite(fd, writeData1, 0); - const size1 = await vfs.size(fd); + await vfs.close(fd); + return { + readData1: Array.from(readData1), + readData2: Array.from(readData2), + readData3: Array.from(readData3), + }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); - // Second write with new array - const writeData2 = new Uint8Array([5, 6, 7, 8]); - await vfs.pwrite(fd, writeData2, 4); - const size2 = await vfs.size(fd); - - await vfs.close(fd); - - return { size1, size2 }; - } catch (error) { - if (fd !== undefined) await vfs.close(fd); - return { error: error.message }; - } + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(result.readData1).toEqual([1, 2]); + expect(result.readData2).toEqual([3, 4, 5, 6]); + expect(result.readData3).toEqual([7, 8]); }); - if (result.error) throw new Error(`Test failed: ${result.error}`); - expect(Number(result.size1)).toBe(4); - expect(Number(result.size2)).toBe(8); + test("file size operations", async () => { + const page = globalThis.__page__; + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + try { + fd = await vfs.open("size.txt", {}); + const writeData1 = new Uint8Array([1, 2, 3, 4]); + await vfs.pwrite(fd, writeData1, 0); + const size1 = await vfs.size(fd); + + const writeData2 = new Uint8Array([5, 6, 7, 8]); + await vfs.pwrite(fd, writeData2, 4); + const size2 = await vfs.size(fd); + + await vfs.close(fd); + return { size1, size2 }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); + + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(Number(result.size1)).toBe(4); + expect(Number(result.size2)).toBe(8); + }); }); + diff --git a/bindings/wasm/test/setup.js b/bindings/wasm/test/setup.js index 9e22bb660..d304902bd 100644 --- a/bindings/wasm/test/setup.js +++ b/bindings/wasm/test/setup.js @@ -20,9 +20,12 @@ beforeAll(async () => { }); beforeEach(async () => { - context = await browser.newContext(); - page = await context.newPage(); - globalThis.__page__ = page; + globalThis.beforeEachPromise = (async () => { + context = await browser.newContext(); + page = await context.newPage(); + globalThis.__page__ = page; + })(); + await globalThis.beforeEachPromise; }); afterEach(async () => { From b9c94ba53c1412f714c5ab07daa911d943e06509 Mon Sep 17 00:00:00 2001 From: Elijah Morgan Date: Mon, 6 Jan 2025 20:42:37 -0500 Subject: [PATCH 06/28] have tests run on different ports cleanup tests cruft from trying to fix that issue --- bindings/wasm/test/limbo.test.js | 51 +++++- bindings/wasm/test/opfs.test.js | 288 +++++++++++++++++-------------- bindings/wasm/test/setup.js | 38 ---- bindings/wasm/vite.config.js | 4 - 4 files changed, 204 insertions(+), 177 deletions(-) diff --git a/bindings/wasm/test/limbo.test.js b/bindings/wasm/test/limbo.test.js index 322f11e8c..b368f81a2 100644 --- a/bindings/wasm/test/limbo.test.js +++ b/bindings/wasm/test/limbo.test.js @@ -1,11 +1,49 @@ -import { expect, test } from "vitest"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + expect, + test, +} from "vitest"; +import { chromium } from "playwright"; +import { createServer } from "vite"; + +let browser; +let context; +let page; +let server; + +beforeAll(async () => { + server = await createServer({ + configFile: "./vite.config.js", + root: ".", + server: { + port: 5174, + }, + }); + await server.listen(); + browser = await chromium.launch(); +}); + +beforeEach(async () => { + context = await browser.newContext(); + page = await context.newPage(); + globalThis.__page__ = page; +}); + +afterEach(async () => { + await context.close(); +}); + +afterAll(async () => { + await browser.close(); + await server.close(); +}); test("basic database operations", async () => { - await globalThis.beforeEachPromise; const page = globalThis.__page__; - await page.goto("http://localhost:5173/limbo-test.html"); - - page.on("console", (msg) => console.log(msg.text())); + await page.goto("http://localhost:5174/limbo-test.html"); const result = await page.evaluate(async () => { const worker = new Worker("./src/limbo-worker.js", { type: "module" }); @@ -55,9 +93,6 @@ test("basic database operations", async () => { }); if (result.error) throw new Error(`Test failed: ${result.error}`); - console.log("test results: ", result); - console.log("test results: ", result.result[0]); expect(result.result).toHaveLength(1); expect(result.result[0]).toEqual([1, "Alice", "alice@example.org"]); - // expect(1).toEqual(1); }); diff --git a/bindings/wasm/test/opfs.test.js b/bindings/wasm/test/opfs.test.js index b7726f8e5..82f01c591 100644 --- a/bindings/wasm/test/opfs.test.js +++ b/bindings/wasm/test/opfs.test.js @@ -1,131 +1,165 @@ -import { beforeEach, describe, expect, test } from "vitest"; +import { + afterAll, + afterEach, + beforeAll, + beforeEach, + describe, + expect, + test, +} from "vitest"; +import { chromium } from "playwright"; +import { createServer } from "vite"; -describe.sequential("OPFS Tests", () => { - beforeEach(async () => { - await globalThis.beforeEachPromise; - const page = globalThis.__page__; - await page.goto("http://localhost:5173/index.html"); - await page.waitForFunction(() => window.VFSInterface !== undefined); - }); - - test("basic read/write functionality", async () => { - const page = globalThis.__page__; - const result = await page.evaluate(async () => { - const vfs = new window.VFSInterface("/src/opfs-worker.js"); - let fd; - try { - fd = await vfs.open("test.txt", {}); - const writeData = new Uint8Array([1, 2, 3, 4]); - const bytesWritten = await vfs.pwrite(fd, writeData, 0); - const readData = new Uint8Array(4); - const bytesRead = await vfs.pread(fd, readData, 0); - await vfs.close(fd); - return { fd, bytesWritten, bytesRead, readData: Array.from(readData) }; - } catch (error) { - if (fd !== undefined) await vfs.close(fd); - return { error: error.message }; - } - }); - - if (result.error) throw new Error(`Test failed: ${result.error}`); - expect(result.fd).toBe(1); - expect(result.bytesWritten).toBe(4); - expect(result.bytesRead).toBe(4); - expect(result.readData).toEqual([1, 2, 3, 4]); - }); - - test("larger data read/write", async () => { - const page = globalThis.__page__; - const result = await page.evaluate(async () => { - const vfs = new window.VFSInterface("/src/opfs-worker.js"); - let fd; - try { - fd = await vfs.open("large.txt", {}); - const writeData = new Uint8Array(1024).map((_, i) => i % 256); - const bytesWritten = await vfs.pwrite(fd, writeData, 0); - const readData = new Uint8Array(1024); - const bytesRead = await vfs.pread(fd, readData, 0); - await vfs.close(fd); - return { bytesWritten, bytesRead, readData: Array.from(readData) }; - } catch (error) { - if (fd !== undefined) await vfs.close(fd); - return { error: error.message }; - } - }); - - if (result.error) throw new Error(`Test failed: ${result.error}`); - expect(result.bytesWritten).toBe(1024); - expect(result.bytesRead).toBe(1024); - expect(result.readData).toEqual( - Array.from({ length: 1024 }, (_, i) => i % 256), - ); - }); - - test("partial reads and writes", async () => { - const page = globalThis.__page__; - const result = await page.evaluate(async () => { - const vfs = new window.VFSInterface("/src/opfs-worker.js"); - let fd; - try { - fd = await vfs.open("partial.txt", {}); - - const writeData1 = new Uint8Array([1, 2, 3, 4]); - const writeData2 = new Uint8Array([5, 6, 7, 8]); - await vfs.pwrite(fd, writeData1, 0); - await vfs.pwrite(fd, writeData2, 4); - - const readData1 = new Uint8Array(2); - const readData2 = new Uint8Array(4); - const readData3 = new Uint8Array(2); - - await vfs.pread(fd, readData1, 0); - await vfs.pread(fd, readData2, 2); - await vfs.pread(fd, readData3, 6); - - await vfs.close(fd); - return { - readData1: Array.from(readData1), - readData2: Array.from(readData2), - readData3: Array.from(readData3), - }; - } catch (error) { - if (fd !== undefined) await vfs.close(fd); - return { error: error.message }; - } - }); - - if (result.error) throw new Error(`Test failed: ${result.error}`); - expect(result.readData1).toEqual([1, 2]); - expect(result.readData2).toEqual([3, 4, 5, 6]); - expect(result.readData3).toEqual([7, 8]); - }); - - test("file size operations", async () => { - const page = globalThis.__page__; - const result = await page.evaluate(async () => { - const vfs = new window.VFSInterface("/src/opfs-worker.js"); - let fd; - try { - fd = await vfs.open("size.txt", {}); - const writeData1 = new Uint8Array([1, 2, 3, 4]); - await vfs.pwrite(fd, writeData1, 0); - const size1 = await vfs.size(fd); - - const writeData2 = new Uint8Array([5, 6, 7, 8]); - await vfs.pwrite(fd, writeData2, 4); - const size2 = await vfs.size(fd); - - await vfs.close(fd); - return { size1, size2 }; - } catch (error) { - if (fd !== undefined) await vfs.close(fd); - return { error: error.message }; - } - }); - - if (result.error) throw new Error(`Test failed: ${result.error}`); - expect(Number(result.size1)).toBe(4); - expect(Number(result.size2)).toBe(8); +let browser; +let context; +let page; +let server; + +beforeAll(async () => { + server = await createServer({ + configFile: "./vite.config.js", + root: ".", + server: { + port: 5173, + }, }); + await server.listen(); + browser = await chromium.launch(); }); +beforeEach(async () => { + context = await browser.newContext(); + page = await context.newPage(); + globalThis.__page__ = page; + await page.goto("http://localhost:5173/index.html"); + await page.waitForFunction(() => window.VFSInterface !== undefined); +}); + +afterEach(async () => { + await context.close(); +}); + +afterAll(async () => { + await browser.close(); + await server.close(); +}); + +test("basic read/write functionality", async () => { + const page = globalThis.__page__; + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + try { + fd = await vfs.open("test.txt", {}); + const writeData = new Uint8Array([1, 2, 3, 4]); + const bytesWritten = await vfs.pwrite(fd, writeData, 0); + const readData = new Uint8Array(4); + const bytesRead = await vfs.pread(fd, readData, 0); + await vfs.close(fd); + return { fd, bytesWritten, bytesRead, readData: Array.from(readData) }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); + + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(result.fd).toBe(1); + expect(result.bytesWritten).toBe(4); + expect(result.bytesRead).toBe(4); + expect(result.readData).toEqual([1, 2, 3, 4]); +}); + +test("larger data read/write", async () => { + const page = globalThis.__page__; + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + try { + fd = await vfs.open("large.txt", {}); + const writeData = new Uint8Array(1024).map((_, i) => i % 256); + const bytesWritten = await vfs.pwrite(fd, writeData, 0); + const readData = new Uint8Array(1024); + const bytesRead = await vfs.pread(fd, readData, 0); + await vfs.close(fd); + return { bytesWritten, bytesRead, readData: Array.from(readData) }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); + + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(result.bytesWritten).toBe(1024); + expect(result.bytesRead).toBe(1024); + expect(result.readData).toEqual( + Array.from({ length: 1024 }, (_, i) => i % 256), + ); +}); + +test("partial reads and writes", async () => { + const page = globalThis.__page__; + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + try { + fd = await vfs.open("partial.txt", {}); + + const writeData1 = new Uint8Array([1, 2, 3, 4]); + const writeData2 = new Uint8Array([5, 6, 7, 8]); + await vfs.pwrite(fd, writeData1, 0); + await vfs.pwrite(fd, writeData2, 4); + + const readData1 = new Uint8Array(2); + const readData2 = new Uint8Array(4); + const readData3 = new Uint8Array(2); + + await vfs.pread(fd, readData1, 0); + await vfs.pread(fd, readData2, 2); + await vfs.pread(fd, readData3, 6); + + await vfs.close(fd); + return { + readData1: Array.from(readData1), + readData2: Array.from(readData2), + readData3: Array.from(readData3), + }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); + + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(result.readData1).toEqual([1, 2]); + expect(result.readData2).toEqual([3, 4, 5, 6]); + expect(result.readData3).toEqual([7, 8]); +}); + +test("file size operations", async () => { + const page = globalThis.__page__; + const result = await page.evaluate(async () => { + const vfs = new window.VFSInterface("/src/opfs-worker.js"); + let fd; + try { + fd = await vfs.open("size.txt", {}); + const writeData1 = new Uint8Array([1, 2, 3, 4]); + await vfs.pwrite(fd, writeData1, 0); + const size1 = await vfs.size(fd); + + const writeData2 = new Uint8Array([5, 6, 7, 8]); + await vfs.pwrite(fd, writeData2, 4); + const size2 = await vfs.size(fd); + + await vfs.close(fd); + return { size1, size2 }; + } catch (error) { + if (fd !== undefined) await vfs.close(fd); + return { error: error.message }; + } + }); + + if (result.error) throw new Error(`Test failed: ${result.error}`); + expect(Number(result.size1)).toBe(4); + expect(Number(result.size2)).toBe(8); +}); diff --git a/bindings/wasm/test/setup.js b/bindings/wasm/test/setup.js index d304902bd..e69de29bb 100644 --- a/bindings/wasm/test/setup.js +++ b/bindings/wasm/test/setup.js @@ -1,38 +0,0 @@ -import { afterAll, afterEach, beforeAll, beforeEach } from "vitest"; -import { chromium } from "playwright"; -import { createServer } from "vite"; - -let browser; -let context; -let page; -let server; - -beforeAll(async () => { - server = await createServer({ - configFile: "./vite.config.js", - root: ".", - server: { - port: 5173, - }, - }); - await server.listen(); - browser = await chromium.launch(); -}); - -beforeEach(async () => { - globalThis.beforeEachPromise = (async () => { - context = await browser.newContext(); - page = await context.newPage(); - globalThis.__page__ = page; - })(); - await globalThis.beforeEachPromise; -}); - -afterEach(async () => { - await context.close(); -}); - -afterAll(async () => { - await browser.close(); - await server.close(); -}); diff --git a/bindings/wasm/vite.config.js b/bindings/wasm/vite.config.js index e92b7f354..d86194560 100644 --- a/bindings/wasm/vite.config.js +++ b/bindings/wasm/vite.config.js @@ -10,10 +10,6 @@ export default defineConfig({ environment: "happy-dom", setupFiles: ["./test/setup.js"], include: ["test/*.test.js"], - sequence: { - shuffle: false, - concurrent: false, - }, }, server: { headers: { From c8232b019a9f0e550481c5bf020654f83c7a057e Mon Sep 17 00:00:00 2001 From: Elijah Morgan Date: Mon, 6 Jan 2025 20:57:02 -0500 Subject: [PATCH 07/28] commonize test code --- bindings/wasm/test/helpers.js | 23 +++++++++++++++ bindings/wasm/test/limbo.test.js | 44 ++++++---------------------- bindings/wasm/test/opfs.test.js | 49 ++++++++------------------------ 3 files changed, 44 insertions(+), 72 deletions(-) create mode 100644 bindings/wasm/test/helpers.js diff --git a/bindings/wasm/test/helpers.js b/bindings/wasm/test/helpers.js new file mode 100644 index 000000000..cc9de4ca2 --- /dev/null +++ b/bindings/wasm/test/helpers.js @@ -0,0 +1,23 @@ +import { createServer } from "vite"; +import { chromium } from "playwright"; + +export async function setupTestEnvironment(port) { + const server = await createServer({ + configFile: "./vite.config.js", + root: ".", + server: { port }, + }); + await server.listen(); + const browser = await chromium.launch(); + const context = await browser.newContext(); + const page = await context.newPage(); + globalThis.__page__ = page; + + return { server, browser, context, page }; +} + +export async function teardownTestEnvironment({ server, browser, context }) { + await context.close(); + await browser.close(); + await server.close(); +} diff --git a/bindings/wasm/test/limbo.test.js b/bindings/wasm/test/limbo.test.js index b368f81a2..f7f34bee8 100644 --- a/bindings/wasm/test/limbo.test.js +++ b/bindings/wasm/test/limbo.test.js @@ -1,50 +1,23 @@ -import { - afterAll, - afterEach, - beforeAll, - beforeEach, - expect, - test, -} from "vitest"; -import { chromium } from "playwright"; -import { createServer } from "vite"; +import { afterAll, beforeAll, beforeEach, expect, test } from "vitest"; +import { setupTestEnvironment, teardownTestEnvironment } from "./helpers.js"; -let browser; -let context; -let page; -let server; +let testEnv; beforeAll(async () => { - server = await createServer({ - configFile: "./vite.config.js", - root: ".", - server: { - port: 5174, - }, - }); - await server.listen(); - browser = await chromium.launch(); + testEnv = await setupTestEnvironment(5174); }); beforeEach(async () => { - context = await browser.newContext(); - page = await context.newPage(); - globalThis.__page__ = page; -}); - -afterEach(async () => { - await context.close(); + const { page } = testEnv; + await page.goto("http://localhost:5174/limbo-test.html"); }); afterAll(async () => { - await browser.close(); - await server.close(); + await teardownTestEnvironment(testEnv); }); test("basic database operations", async () => { - const page = globalThis.__page__; - await page.goto("http://localhost:5174/limbo-test.html"); - + const { page } = testEnv; const result = await page.evaluate(async () => { const worker = new Worker("./src/limbo-worker.js", { type: "module" }); @@ -96,3 +69,4 @@ test("basic database operations", async () => { expect(result.result).toHaveLength(1); expect(result.result[0]).toEqual([1, "Alice", "alice@example.org"]); }); + diff --git a/bindings/wasm/test/opfs.test.js b/bindings/wasm/test/opfs.test.js index 82f01c591..ea96add44 100644 --- a/bindings/wasm/test/opfs.test.js +++ b/bindings/wasm/test/opfs.test.js @@ -1,51 +1,25 @@ -import { - afterAll, - afterEach, - beforeAll, - beforeEach, - describe, - expect, - test, -} from "vitest"; -import { chromium } from "playwright"; -import { createServer } from "vite"; +// test/opfs.test.js +import { afterAll, beforeAll, beforeEach, expect, test } from "vitest"; +import { setupTestEnvironment, teardownTestEnvironment } from "./helpers"; -let browser; -let context; -let page; -let server; +let testEnv; beforeAll(async () => { - server = await createServer({ - configFile: "./vite.config.js", - root: ".", - server: { - port: 5173, - }, - }); - await server.listen(); - browser = await chromium.launch(); + testEnv = await setupTestEnvironment(5173); }); beforeEach(async () => { - context = await browser.newContext(); - page = await context.newPage(); - globalThis.__page__ = page; + const { page } = testEnv; await page.goto("http://localhost:5173/index.html"); await page.waitForFunction(() => window.VFSInterface !== undefined); }); -afterEach(async () => { - await context.close(); -}); - afterAll(async () => { - await browser.close(); - await server.close(); + await teardownTestEnvironment(testEnv); }); test("basic read/write functionality", async () => { - const page = globalThis.__page__; + const { page } = testEnv; const result = await page.evaluate(async () => { const vfs = new window.VFSInterface("/src/opfs-worker.js"); let fd; @@ -71,7 +45,7 @@ test("basic read/write functionality", async () => { }); test("larger data read/write", async () => { - const page = globalThis.__page__; + const { page } = testEnv; const result = await page.evaluate(async () => { const vfs = new window.VFSInterface("/src/opfs-worker.js"); let fd; @@ -98,7 +72,7 @@ test("larger data read/write", async () => { }); test("partial reads and writes", async () => { - const page = globalThis.__page__; + const { page } = testEnv; const result = await page.evaluate(async () => { const vfs = new window.VFSInterface("/src/opfs-worker.js"); let fd; @@ -137,7 +111,7 @@ test("partial reads and writes", async () => { }); test("file size operations", async () => { - const page = globalThis.__page__; + const { page } = testEnv; const result = await page.evaluate(async () => { const vfs = new window.VFSInterface("/src/opfs-worker.js"); let fd; @@ -163,3 +137,4 @@ test("file size operations", async () => { expect(Number(result.size1)).toBe(4); expect(Number(result.size2)).toBe(8); }); + From 731ff1480f31cab91ff2bb8bb3d1664f50b2a34a Mon Sep 17 00:00:00 2001 From: Jussi Saurio Date: Tue, 7 Jan 2025 12:46:01 +0200 Subject: [PATCH 08/28] Simplify working with labels TLDR: no need to call either of: program.emit_insn_with_label_dependency() -> just call program.emit_insn() program.defer_label_resolution() -> just call program.resolve_label() Changes: - make BranchOffset an explicit enum (Label, Offset, Placeholder) - remove program.emit_insn_with_label_dependency() - label dependency is automatically detected - for label to offset mapping, use a hashmap from label(negative i32) to offset (positive u32) - resolve all labels in program.build() - remove program.defer_label_resolution() - all labels are resolved in build() --- core/translate/emitter.rs | 40 ++-- core/translate/expr.rs | 405 +++++++++++++---------------------- core/translate/group_by.rs | 136 +++++------- core/translate/insert.rs | 63 +++--- core/translate/main_loop.rs | 188 +++++++--------- core/translate/mod.rs | 28 +-- core/translate/order_by.rs | 24 +-- core/translate/planner.rs | 2 +- core/translate/result_row.rs | 13 +- core/translate/subquery.rs | 15 +- core/vdbe/builder.rs | 215 +++++++------------ core/vdbe/explain.rs | 113 ++++++---- core/vdbe/mod.rs | 228 +++++++++++++------- 13 files changed, 637 insertions(+), 833 deletions(-) diff --git a/core/translate/emitter.rs b/core/translate/emitter.rs index 7b07f1a7a..8fdff8cd1 100644 --- a/core/translate/emitter.rs +++ b/core/translate/emitter.rs @@ -104,12 +104,9 @@ fn prologue<'a>( let mut program = ProgramBuilder::new(); let init_label = program.allocate_label(); - program.emit_insn_with_label_dependency( - Insn::Init { - target_pc: init_label, - }, - init_label, - ); + program.emit_insn(Insn::Init { + target_pc: init_label, + }); let start_offset = program.offset(); @@ -151,8 +148,6 @@ fn epilogue( target_pc: start_offset, }); - program.resolve_deferred_labels(); - Ok(()) } @@ -218,12 +213,9 @@ pub fn emit_query<'a>( let after_main_loop_label = program.allocate_label(); t_ctx.label_main_loop_end = Some(after_main_loop_label); if plan.contains_constant_false_condition { - program.emit_insn_with_label_dependency( - Insn::Goto { - target_pc: after_main_loop_label, - }, - after_main_loop_label, - ); + program.emit_insn(Insn::Goto { + target_pc: after_main_loop_label, + }); } // Allocate registers for result columns @@ -281,12 +273,9 @@ fn emit_program_for_delete( // No rows will be read from source table loops if there is a constant false condition eg. WHERE 0 let after_main_loop_label = program.allocate_label(); if plan.contains_constant_false_condition { - program.emit_insn_with_label_dependency( - Insn::Goto { - target_pc: after_main_loop_label, - }, - after_main_loop_label, - ); + program.emit_insn(Insn::Goto { + target_pc: after_main_loop_label, + }); } // Initialize cursors and other resources needed for query execution @@ -356,13 +345,10 @@ fn emit_delete_insns<'a>( dest: limit_reg, }); program.mark_last_insn_constant(); - program.emit_insn_with_label_dependency( - Insn::DecrJumpZero { - reg: limit_reg, - target_pc: t_ctx.label_main_loop_end.unwrap(), - }, - t_ctx.label_main_loop_end.unwrap(), - ) + program.emit_insn(Insn::DecrJumpZero { + reg: limit_reg, + target_pc: t_ctx.label_main_loop_end.unwrap(), + }) } Ok(()) diff --git a/core/translate/expr.rs b/core/translate/expr.rs index b4fb5e87e..4c4ee72b2 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -13,7 +13,7 @@ use crate::Result; use super::emitter::Resolver; use super::plan::{TableReference, TableReferenceType}; -#[derive(Default, Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub struct ConditionMetadata { pub jump_if_condition_is_true: bool, pub jump_target_when_true: BranchOffset, @@ -87,128 +87,92 @@ pub fn translate_condition_expr( match op { ast::Operator::Greater => { if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::Gt { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }, - condition_metadata.jump_target_when_true, - ) + program.emit_insn(Insn::Gt { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_true, + }) } else { - program.emit_insn_with_label_dependency( - Insn::Le { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }, - condition_metadata.jump_target_when_false, - ) + program.emit_insn(Insn::Le { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_false, + }) } } ast::Operator::GreaterEquals => { if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::Ge { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }, - condition_metadata.jump_target_when_true, - ) + program.emit_insn(Insn::Ge { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_true, + }) } else { - program.emit_insn_with_label_dependency( - Insn::Lt { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }, - condition_metadata.jump_target_when_false, - ) + program.emit_insn(Insn::Lt { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_false, + }) } } ast::Operator::Less => { if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::Lt { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }, - condition_metadata.jump_target_when_true, - ) + program.emit_insn(Insn::Lt { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_true, + }) } else { - program.emit_insn_with_label_dependency( - Insn::Ge { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }, - condition_metadata.jump_target_when_false, - ) + program.emit_insn(Insn::Ge { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_false, + }) } } ast::Operator::LessEquals => { if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::Le { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }, - condition_metadata.jump_target_when_true, - ) + program.emit_insn(Insn::Le { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_true, + }) } else { - program.emit_insn_with_label_dependency( - Insn::Gt { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }, - condition_metadata.jump_target_when_false, - ) + program.emit_insn(Insn::Gt { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_false, + }) } } ast::Operator::Equals => { if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::Eq { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }, - condition_metadata.jump_target_when_true, - ) + program.emit_insn(Insn::Eq { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_true, + }) } else { - program.emit_insn_with_label_dependency( - Insn::Ne { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }, - condition_metadata.jump_target_when_false, - ) + program.emit_insn(Insn::Ne { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_false, + }) } } ast::Operator::NotEquals => { if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::Ne { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }, - condition_metadata.jump_target_when_true, - ) + program.emit_insn(Insn::Ne { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_true, + }) } else { - program.emit_insn_with_label_dependency( - Insn::Eq { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }, - condition_metadata.jump_target_when_false, - ) + program.emit_insn(Insn::Eq { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_false, + }) } } ast::Operator::Is => todo!(), @@ -228,23 +192,17 @@ pub fn translate_condition_expr( dest: reg, }); if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::If { - reg, - target_pc: condition_metadata.jump_target_when_true, - null_reg: reg, - }, - condition_metadata.jump_target_when_true, - ) + program.emit_insn(Insn::If { + reg, + target_pc: condition_metadata.jump_target_when_true, + null_reg: reg, + }) } else { - program.emit_insn_with_label_dependency( - Insn::IfNot { - reg, - target_pc: condition_metadata.jump_target_when_false, - null_reg: reg, - }, - condition_metadata.jump_target_when_false, - ) + program.emit_insn(Insn::IfNot { + reg, + target_pc: condition_metadata.jump_target_when_false, + null_reg: reg, + }) } } else { crate::bail_parse_error!("unsupported literal type in condition"); @@ -257,23 +215,17 @@ pub fn translate_condition_expr( dest: reg, }); if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::If { - reg, - target_pc: condition_metadata.jump_target_when_true, - null_reg: reg, - }, - condition_metadata.jump_target_when_true, - ) + program.emit_insn(Insn::If { + reg, + target_pc: condition_metadata.jump_target_when_true, + null_reg: reg, + }) } else { - program.emit_insn_with_label_dependency( - Insn::IfNot { - reg, - target_pc: condition_metadata.jump_target_when_false, - null_reg: reg, - }, - condition_metadata.jump_target_when_false, - ) + program.emit_insn(Insn::IfNot { + reg, + target_pc: condition_metadata.jump_target_when_false, + null_reg: reg, + }) } } unimpl => todo!("literal {:?} not implemented", unimpl), @@ -302,20 +254,14 @@ pub fn translate_condition_expr( // Note that we are already breaking up our WHERE clauses into a series of terms at "AND" boundaries, so right now we won't be running into cases where jumping on true would be incorrect, // but once we have e.g. parenthesization and more complex conditions, not having this 'if' here would introduce a bug. if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::Goto { - target_pc: condition_metadata.jump_target_when_true, - }, - condition_metadata.jump_target_when_true, - ); + program.emit_insn(Insn::Goto { + target_pc: condition_metadata.jump_target_when_true, + }); } } else { - program.emit_insn_with_label_dependency( - Insn::Goto { - target_pc: condition_metadata.jump_target_when_false, - }, - condition_metadata.jump_target_when_false, - ); + program.emit_insn(Insn::Goto { + target_pc: condition_metadata.jump_target_when_false, + }); } return Ok(()); } @@ -349,35 +295,26 @@ pub fn translate_condition_expr( translate_expr(program, Some(referenced_tables), expr, rhs_reg, resolver)?; // If this is not the last condition, we need to jump to the 'jump_target_when_true' label if the condition is true. if !last_condition { - program.emit_insn_with_label_dependency( - Insn::Eq { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: jump_target_when_true, - }, - jump_target_when_true, - ); + program.emit_insn(Insn::Eq { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: jump_target_when_true, + }); } else { // If this is the last condition, we need to jump to the 'jump_target_when_false' label if there is no match. - program.emit_insn_with_label_dependency( - Insn::Ne { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }, - condition_metadata.jump_target_when_false, - ); + program.emit_insn(Insn::Ne { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_false, + }); } } // If we got here, then the last condition was a match, so we jump to the 'jump_target_when_true' label if 'jump_if_condition_is_true'. // If not, we can just fall through without emitting an unnecessary instruction. if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::Goto { - target_pc: condition_metadata.jump_target_when_true, - }, - condition_metadata.jump_target_when_true, - ); + program.emit_insn(Insn::Goto { + target_pc: condition_metadata.jump_target_when_true, + }); } } else { // If it's a NOT IN expression, we need to jump to the 'jump_target_when_false' label if any of the conditions are true. @@ -385,24 +322,18 @@ pub fn translate_condition_expr( let rhs_reg = program.alloc_register(); let _ = translate_expr(program, Some(referenced_tables), expr, rhs_reg, resolver)?; - program.emit_insn_with_label_dependency( - Insn::Eq { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }, - condition_metadata.jump_target_when_false, - ); + program.emit_insn(Insn::Eq { + lhs: lhs_reg, + rhs: rhs_reg, + target_pc: condition_metadata.jump_target_when_false, + }); } // If we got here, then none of the conditions were a match, so we jump to the 'jump_target_when_true' label if 'jump_if_condition_is_true'. // If not, we can just fall through without emitting an unnecessary instruction. if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::Goto { - target_pc: condition_metadata.jump_target_when_true, - }, - condition_metadata.jump_target_when_true, - ); + program.emit_insn(Insn::Goto { + target_pc: condition_metadata.jump_target_when_true, + }); } } @@ -464,42 +395,30 @@ pub fn translate_condition_expr( } if !*not { if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::If { - reg: cur_reg, - target_pc: condition_metadata.jump_target_when_true, - null_reg: cur_reg, - }, - condition_metadata.jump_target_when_true, - ); - } else { - program.emit_insn_with_label_dependency( - Insn::IfNot { - reg: cur_reg, - target_pc: condition_metadata.jump_target_when_false, - null_reg: cur_reg, - }, - condition_metadata.jump_target_when_false, - ); - } - } else if condition_metadata.jump_if_condition_is_true { - program.emit_insn_with_label_dependency( - Insn::IfNot { + program.emit_insn(Insn::If { reg: cur_reg, target_pc: condition_metadata.jump_target_when_true, null_reg: cur_reg, - }, - condition_metadata.jump_target_when_true, - ); - } else { - program.emit_insn_with_label_dependency( - Insn::If { + }); + } else { + program.emit_insn(Insn::IfNot { reg: cur_reg, target_pc: condition_metadata.jump_target_when_false, null_reg: cur_reg, - }, - condition_metadata.jump_target_when_false, - ); + }); + } + } else if condition_metadata.jump_if_condition_is_true { + program.emit_insn(Insn::IfNot { + reg: cur_reg, + target_pc: condition_metadata.jump_target_when_true, + null_reg: cur_reg, + }); + } else { + program.emit_insn(Insn::If { + reg: cur_reg, + target_pc: condition_metadata.jump_target_when_false, + null_reg: cur_reg, + }); } } ast::Expr::Parenthesized(exprs) => { @@ -707,23 +626,17 @@ pub fn translate_expr( translate_expr(program, referenced_tables, when_expr, expr_reg, resolver)?; match base_reg { // CASE 1 WHEN 0 THEN 0 ELSE 1 becomes 1==0, Ne branch to next clause - Some(base_reg) => program.emit_insn_with_label_dependency( - Insn::Ne { - lhs: base_reg, - rhs: expr_reg, - target_pc: next_case_label, - }, - next_case_label, - ), + Some(base_reg) => program.emit_insn(Insn::Ne { + lhs: base_reg, + rhs: expr_reg, + target_pc: next_case_label, + }), // CASE WHEN 0 THEN 0 ELSE 1 becomes ifnot 0 branch to next clause - None => program.emit_insn_with_label_dependency( - Insn::IfNot { - reg: expr_reg, - target_pc: next_case_label, - null_reg: 1, - }, - next_case_label, - ), + None => program.emit_insn(Insn::IfNot { + reg: expr_reg, + target_pc: next_case_label, + null_reg: 1, + }), }; // THEN... translate_expr( @@ -733,12 +646,9 @@ pub fn translate_expr( target_register, resolver, )?; - program.emit_insn_with_label_dependency( - Insn::Goto { - target_pc: return_label, - }, - return_label, - ); + program.emit_insn(Insn::Goto { + target_pc: return_label, + }); // This becomes either the next WHEN, or in the last WHEN/THEN, we're // assured to have at least one instruction corresponding to the ELSE immediately follow. program.preassign_label_to_next_insn(next_case_label); @@ -984,13 +894,10 @@ pub fn translate_expr( resolver, )?; if index < args.len() - 1 { - program.emit_insn_with_label_dependency( - Insn::NotNull { - reg, - target_pc: label_coalesce_end, - }, - label_coalesce_end, - ); + program.emit_insn(Insn::NotNull { + reg, + target_pc: label_coalesce_end, + }); } } program.preassign_label_to_next_insn(label_coalesce_end); @@ -1085,7 +992,7 @@ pub fn translate_expr( )?; program.emit_insn(Insn::NotNull { reg: temp_reg, - target_pc: program.offset() + 2, + target_pc: program.offset().add(2u32), }); translate_expr( @@ -1120,14 +1027,11 @@ pub fn translate_expr( resolver, )?; let jump_target_when_false = program.allocate_label(); - program.emit_insn_with_label_dependency( - Insn::IfNot { - reg: temp_reg, - target_pc: jump_target_when_false, - null_reg: 1, - }, - jump_target_when_false, - ); + program.emit_insn(Insn::IfNot { + reg: temp_reg, + target_pc: jump_target_when_false, + null_reg: 1, + }); translate_expr( program, referenced_tables, @@ -1136,12 +1040,9 @@ pub fn translate_expr( resolver, )?; let jump_target_result = program.allocate_label(); - program.emit_insn_with_label_dependency( - Insn::Goto { - target_pc: jump_target_result, - }, - jump_target_result, - ); + program.emit_insn(Insn::Goto { + target_pc: jump_target_result, + }); program.resolve_label(jump_target_when_false, program.offset()); translate_expr( program, @@ -2033,7 +1934,7 @@ fn wrap_eval_jump_expr( value: 1, // emit True by default dest: target_register, }); - program.emit_insn_with_label_dependency(insn, if_true_label); + program.emit_insn(insn); program.emit_insn(Insn::Integer { value: 0, // emit False if we reach this point (no jump) dest: target_register, diff --git a/core/translate/group_by.rs b/core/translate/group_by.rs index a38a9d26c..0990bee0d 100644 --- a/core/translate/group_by.rs +++ b/core/translate/group_by.rs @@ -93,13 +93,10 @@ pub fn init_group_by( program.add_comment(program.offset(), "go to clear accumulator subroutine"); let reg_subrtn_acc_clear_return_offset = program.alloc_register(); - program.emit_insn_with_label_dependency( - Insn::Gosub { - target_pc: label_subrtn_acc_clear, - return_reg: reg_subrtn_acc_clear_return_offset, - }, - label_subrtn_acc_clear, - ); + program.emit_insn(Insn::Gosub { + target_pc: label_subrtn_acc_clear, + return_reg: reg_subrtn_acc_clear_return_offset, + }); t_ctx.reg_agg_start = Some(reg_agg_exprs_start); @@ -187,15 +184,12 @@ pub fn emit_group_by<'a>( }); // Sort the sorter based on the group by columns - program.emit_insn_with_label_dependency( - Insn::SorterSort { - cursor_id: sort_cursor, - pc_if_empty: label_grouping_loop_end, - }, - label_grouping_loop_end, - ); + program.emit_insn(Insn::SorterSort { + cursor_id: sort_cursor, + pc_if_empty: label_grouping_loop_end, + }); - program.defer_label_resolution(label_grouping_loop_start, program.offset() as usize); + program.resolve_label(label_grouping_loop_start, program.offset()); // Read a row from the sorted data in the sorter into the pseudo cursor program.emit_insn(Insn::SorterData { cursor_id: sort_cursor, @@ -229,14 +223,11 @@ pub fn emit_group_by<'a>( "start new group if comparison is not equal", ); // If we are at a new group, continue. If we are at the same group, jump to the aggregation step (i.e. accumulate more values into the aggregations) - program.emit_insn_with_label_dependency( - Insn::Jump { - target_pc_lt: program.offset() + 1, - target_pc_eq: agg_step_label, - target_pc_gt: program.offset() + 1, - }, - agg_step_label, - ); + program.emit_insn(Insn::Jump { + target_pc_lt: program.offset().add(1u32), + target_pc_eq: agg_step_label, + target_pc_gt: program.offset().add(1u32), + }); // New group, move current group by columns into the comparison register program.emit_insn(Insn::Move { @@ -249,32 +240,23 @@ pub fn emit_group_by<'a>( program.offset(), "check if ended group had data, and output if so", ); - program.emit_insn_with_label_dependency( - Insn::Gosub { - target_pc: label_subrtn_acc_output, - return_reg: reg_subrtn_acc_output_return_offset, - }, - label_subrtn_acc_output, - ); + program.emit_insn(Insn::Gosub { + target_pc: label_subrtn_acc_output, + return_reg: reg_subrtn_acc_output_return_offset, + }); program.add_comment(program.offset(), "check abort flag"); - program.emit_insn_with_label_dependency( - Insn::IfPos { - reg: reg_abort_flag, - target_pc: label_group_by_end, - decrement_by: 0, - }, - label_group_by_end, - ); + program.emit_insn(Insn::IfPos { + reg: reg_abort_flag, + target_pc: label_group_by_end, + decrement_by: 0, + }); program.add_comment(program.offset(), "goto clear accumulator subroutine"); - program.emit_insn_with_label_dependency( - Insn::Gosub { - target_pc: label_subrtn_acc_clear, - return_reg: reg_subrtn_acc_clear_return_offset, - }, - label_subrtn_acc_clear, - ); + program.emit_insn(Insn::Gosub { + target_pc: label_subrtn_acc_clear, + return_reg: reg_subrtn_acc_clear_return_offset, + }); // Accumulate the values into the aggregations program.resolve_label(agg_step_label, program.offset()); @@ -299,14 +281,11 @@ pub fn emit_group_by<'a>( program.offset(), "don't emit group columns if continuing existing group", ); - program.emit_insn_with_label_dependency( - Insn::If { - target_pc: label_acc_indicator_set_flag_true, - reg: reg_data_in_acc_flag, - null_reg: 0, // unused in this case - }, - label_acc_indicator_set_flag_true, - ); + program.emit_insn(Insn::If { + target_pc: label_acc_indicator_set_flag_true, + reg: reg_data_in_acc_flag, + null_reg: 0, // unused in this case + }); // Read the group by columns for a finished group for i in 0..group_by.exprs.len() { @@ -326,32 +305,23 @@ pub fn emit_group_by<'a>( dest: reg_data_in_acc_flag, }); - program.emit_insn_with_label_dependency( - Insn::SorterNext { - cursor_id: sort_cursor, - pc_if_next: label_grouping_loop_start, - }, - label_grouping_loop_start, - ); + program.emit_insn(Insn::SorterNext { + cursor_id: sort_cursor, + pc_if_next: label_grouping_loop_start, + }); program.resolve_label(label_grouping_loop_end, program.offset()); program.add_comment(program.offset(), "emit row for final group"); - program.emit_insn_with_label_dependency( - Insn::Gosub { - target_pc: label_subrtn_acc_output, - return_reg: reg_subrtn_acc_output_return_offset, - }, - label_subrtn_acc_output, - ); + program.emit_insn(Insn::Gosub { + target_pc: label_subrtn_acc_output, + return_reg: reg_subrtn_acc_output_return_offset, + }); program.add_comment(program.offset(), "group by finished"); - program.emit_insn_with_label_dependency( - Insn::Goto { - target_pc: label_group_by_end, - }, - label_group_by_end, - ); + program.emit_insn(Insn::Goto { + target_pc: label_group_by_end, + }); program.emit_insn(Insn::Integer { value: 1, dest: reg_abort_flag, @@ -363,19 +333,13 @@ pub fn emit_group_by<'a>( program.resolve_label(label_subrtn_acc_output, program.offset()); program.add_comment(program.offset(), "output group by row subroutine start"); - program.emit_insn_with_label_dependency( - Insn::IfPos { - reg: reg_data_in_acc_flag, - target_pc: label_agg_final, - decrement_by: 0, - }, - label_agg_final, - ); + program.emit_insn(Insn::IfPos { + reg: reg_data_in_acc_flag, + target_pc: label_agg_final, + decrement_by: 0, + }); let group_by_end_without_emitting_row_label = program.allocate_label(); - program.defer_label_resolution( - group_by_end_without_emitting_row_label, - program.offset() as usize, - ); + program.resolve_label(group_by_end_without_emitting_row_label, program.offset()); program.emit_insn(Insn::Return { return_reg: reg_subrtn_acc_output_return_offset, }); @@ -417,7 +381,7 @@ pub fn emit_group_by<'a>( ConditionMetadata { jump_if_condition_is_true: false, jump_target_when_false: group_by_end_without_emitting_row_label, - jump_target_when_true: i64::MAX, // unused + jump_target_when_true: BranchOffset::Placeholder, // not used. FIXME: this is a bug. HAVING can have e.g. HAVING a OR b. }, &t_ctx.resolver, )?; diff --git a/core/translate/insert.rs b/core/translate/insert.rs index 3213e89cd..b9f73ba8c 100644 --- a/core/translate/insert.rs +++ b/core/translate/insert.rs @@ -7,6 +7,7 @@ use sqlite3_parser::ast::{ use crate::error::SQLITE_CONSTRAINT_PRIMARYKEY; use crate::util::normalize_ident; +use crate::vdbe::BranchOffset; use crate::{ schema::{Column, Schema, Table}, storage::sqlite3_ondisk::DatabaseHeader, @@ -40,12 +41,9 @@ pub fn translate_insert( let mut program = ProgramBuilder::new(); let resolver = Resolver::new(syms); let init_label = program.allocate_label(); - program.emit_insn_with_label_dependency( - Insn::Init { - target_pc: init_label, - }, - init_label, - ); + program.emit_insn(Insn::Init { + target_pc: init_label, + }); let start_offset = program.offset(); // open table @@ -104,7 +102,7 @@ pub fn translate_insert( let record_register = program.alloc_register(); let halt_label = program.allocate_label(); - let mut loop_start_offset = 0; + let mut loop_start_offset = BranchOffset::Offset(0); let inserting_multiple_rows = values.len() > 1; @@ -112,14 +110,11 @@ pub fn translate_insert( if inserting_multiple_rows { let yield_reg = program.alloc_register(); let jump_on_definition_label = program.allocate_label(); - program.emit_insn_with_label_dependency( - Insn::InitCoroutine { - yield_reg, - jump_on_definition: jump_on_definition_label, - start_offset: program.offset() + 1, - }, - jump_on_definition_label, - ); + program.emit_insn(Insn::InitCoroutine { + yield_reg, + jump_on_definition: jump_on_definition_label, + start_offset: program.offset().add(1u32), + }); for value in values { populate_column_registers( @@ -133,7 +128,7 @@ pub fn translate_insert( )?; program.emit_insn(Insn::Yield { yield_reg, - end_offset: 0, + end_offset: halt_label, }); } program.emit_insn(Insn::EndCoroutine { yield_reg }); @@ -149,13 +144,10 @@ pub fn translate_insert( // FIXME: rollback is not implemented. E.g. if you insert 2 rows and one fails to unique constraint violation, // the other row will still be inserted. loop_start_offset = program.offset(); - program.emit_insn_with_label_dependency( - Insn::Yield { - yield_reg, - end_offset: halt_label, - }, - halt_label, - ); + program.emit_insn(Insn::Yield { + yield_reg, + end_offset: halt_label, + }); } else { // Single row - populate registers directly program.emit_insn(Insn::OpenWriteAsync { @@ -194,13 +186,10 @@ pub fn translate_insert( program.emit_insn(Insn::SoftNull { reg }); } // the user provided rowid value might itself be NULL. If it is, we create a new rowid on the next instruction. - program.emit_insn_with_label_dependency( - Insn::NotNull { - reg: rowid_reg, - target_pc: check_rowid_is_integer_label.unwrap(), - }, - check_rowid_is_integer_label.unwrap(), - ); + program.emit_insn(Insn::NotNull { + reg: rowid_reg, + target_pc: check_rowid_is_integer_label.unwrap(), + }); } // Create new rowid if a) not provided by user or b) provided by user but is NULL @@ -220,14 +209,11 @@ pub fn translate_insert( // When the DB allocates it there are no need for separate uniqueness checks. if has_user_provided_rowid { let make_record_label = program.allocate_label(); - program.emit_insn_with_label_dependency( - Insn::NotExists { - cursor: cursor_id, - rowid_reg, - target_pc: make_record_label, - }, - make_record_label, - ); + program.emit_insn(Insn::NotExists { + cursor: cursor_id, + rowid_reg, + target_pc: make_record_label, + }); let rowid_column_name = if let Some(index) = rowid_alias_index { table.column_index_to_name(index).unwrap() } else { @@ -276,7 +262,6 @@ pub fn translate_insert( program.emit_insn(Insn::Goto { target_pc: start_offset, }); - program.resolve_deferred_labels(); Ok(program.build(database_header, connection)) } diff --git a/core/translate/main_loop.rs b/core/translate/main_loop.rs index c11cc17c4..05daf01e5 100644 --- a/core/translate/main_loop.rs +++ b/core/translate/main_loop.rs @@ -194,7 +194,7 @@ pub fn open_loop( // In case the subquery is an inner loop, it needs to be reinitialized on each iteration of the outer loop. program.emit_insn(Insn::InitCoroutine { yield_reg, - jump_on_definition: 0, + jump_on_definition: BranchOffset::Offset(0), start_offset: coroutine_implementation_start, }); let LoopLabels { @@ -205,18 +205,15 @@ pub fn open_loop( .labels_main_loop .get(id) .expect("subquery has no loop labels"); - program.defer_label_resolution(loop_start, program.offset() as usize); + program.resolve_label(loop_start, program.offset()); // A subquery within the main loop of a parent query has no cursor, so instead of advancing the cursor, // it emits a Yield which jumps back to the main loop of the subquery itself to retrieve the next row. // When the subquery coroutine completes, this instruction jumps to the label at the top of the termination_label_stack, // which in this case is the end of the Yield-Goto loop in the parent query. - program.emit_insn_with_label_dependency( - Insn::Yield { - yield_reg, - end_offset: loop_end, - }, - loop_end, - ); + program.emit_insn(Insn::Yield { + yield_reg, + end_offset: loop_end, + }); // These are predicates evaluated outside of the subquery, // so they are translated here. @@ -291,10 +288,7 @@ pub fn open_loop( if *outer { let lj_meta = t_ctx.meta_left_joins.get(id).unwrap(); - program.defer_label_resolution( - lj_meta.label_match_flag_set_true, - program.offset() as usize, - ); + program.resolve_label(lj_meta.label_match_flag_set_true, program.offset()); program.emit_insn(Insn::Integer { value: 1, dest: lj_meta.reg_match_flag, @@ -326,7 +320,7 @@ pub fn open_loop( .labels_main_loop .get(id) .expect("scan has no loop labels"); - program.emit_insn_with_label_dependency( + program.emit_insn( if iter_dir .as_ref() .is_some_and(|dir| *dir == IterationDirection::Backwards) @@ -341,9 +335,8 @@ pub fn open_loop( pc_if_empty: loop_end, } }, - loop_end, ); - program.defer_label_resolution(loop_start, program.offset() as usize); + program.resolve_label(loop_start, program.offset()); if let Some(preds) = predicates { for expr in preds { @@ -420,28 +413,25 @@ pub fn open_loop( _ => unreachable!(), } // If we try to seek to a key that is not present in the table/index, we exit the loop entirely. - program.emit_insn_with_label_dependency( - match cmp_op { - ast::Operator::Equals | ast::Operator::GreaterEquals => Insn::SeekGE { - is_index: index_cursor_id.is_some(), - cursor_id: index_cursor_id.unwrap_or(table_cursor_id), - start_reg: cmp_reg, - num_regs: 1, - target_pc: loop_end, - }, - ast::Operator::Greater - | ast::Operator::Less - | ast::Operator::LessEquals => Insn::SeekGT { - is_index: index_cursor_id.is_some(), - cursor_id: index_cursor_id.unwrap_or(table_cursor_id), - start_reg: cmp_reg, - num_regs: 1, - target_pc: loop_end, - }, - _ => unreachable!(), + program.emit_insn(match cmp_op { + ast::Operator::Equals | ast::Operator::GreaterEquals => Insn::SeekGE { + is_index: index_cursor_id.is_some(), + cursor_id: index_cursor_id.unwrap_or(table_cursor_id), + start_reg: cmp_reg, + num_regs: 1, + target_pc: loop_end, }, - loop_end, - ); + ast::Operator::Greater | ast::Operator::Less | ast::Operator::LessEquals => { + Insn::SeekGT { + is_index: index_cursor_id.is_some(), + cursor_id: index_cursor_id.unwrap_or(table_cursor_id), + start_reg: cmp_reg, + num_regs: 1, + target_pc: loop_end, + } + } + _ => unreachable!(), + }); if *cmp_op == ast::Operator::Less || *cmp_op == ast::Operator::LessEquals { translate_expr( program, @@ -452,7 +442,7 @@ pub fn open_loop( )?; } - program.defer_label_resolution(loop_start, program.offset() as usize); + program.resolve_label(loop_start, program.offset()); // TODO: We are currently only handling ascending indexes. // For conditions like index_key > 10, we have already seeked to the first key greater than 10, and can just scan forward. // For conditions like index_key < 10, we are at the beginning of the index, and will scan forward and emit IdxGE(10) with a conditional jump to the end. @@ -466,56 +456,44 @@ pub fn open_loop( match cmp_op { ast::Operator::Equals | ast::Operator::LessEquals => { if let Some(index_cursor_id) = index_cursor_id { - program.emit_insn_with_label_dependency( - Insn::IdxGT { - cursor_id: index_cursor_id, - start_reg: cmp_reg, - num_regs: 1, - target_pc: loop_end, - }, - loop_end, - ); + program.emit_insn(Insn::IdxGT { + cursor_id: index_cursor_id, + start_reg: cmp_reg, + num_regs: 1, + target_pc: loop_end, + }); } else { let rowid_reg = program.alloc_register(); program.emit_insn(Insn::RowId { cursor_id: table_cursor_id, dest: rowid_reg, }); - program.emit_insn_with_label_dependency( - Insn::Gt { - lhs: rowid_reg, - rhs: cmp_reg, - target_pc: loop_end, - }, - loop_end, - ); + program.emit_insn(Insn::Gt { + lhs: rowid_reg, + rhs: cmp_reg, + target_pc: loop_end, + }); } } ast::Operator::Less => { if let Some(index_cursor_id) = index_cursor_id { - program.emit_insn_with_label_dependency( - Insn::IdxGE { - cursor_id: index_cursor_id, - start_reg: cmp_reg, - num_regs: 1, - target_pc: loop_end, - }, - loop_end, - ); + program.emit_insn(Insn::IdxGE { + cursor_id: index_cursor_id, + start_reg: cmp_reg, + num_regs: 1, + target_pc: loop_end, + }); } else { let rowid_reg = program.alloc_register(); program.emit_insn(Insn::RowId { cursor_id: table_cursor_id, dest: rowid_reg, }); - program.emit_insn_with_label_dependency( - Insn::Ge { - lhs: rowid_reg, - rhs: cmp_reg, - target_pc: loop_end, - }, - loop_end, - ); + program.emit_insn(Insn::Ge { + lhs: rowid_reg, + rhs: cmp_reg, + target_pc: loop_end, + }); } } _ => {} @@ -538,14 +516,11 @@ pub fn open_loop( src_reg, &t_ctx.resolver, )?; - program.emit_insn_with_label_dependency( - Insn::SeekRowid { - cursor_id: table_cursor_id, - src_reg, - target_pc: next, - }, - next, - ); + program.emit_insn(Insn::SeekRowid { + cursor_id: table_cursor_id, + src_reg, + target_pc: next, + }); } if let Some(predicates) = predicates { for predicate in predicates.iter() { @@ -748,12 +723,9 @@ pub fn close_loop( // A subquery has no cursor to call NextAsync on, so it just emits a Goto // to the Yield instruction, which in turn jumps back to the main loop of the subquery, // so that the next row from the subquery can be read. - program.emit_insn_with_label_dependency( - Insn::Goto { - target_pc: loop_labels.loop_start, - }, - loop_labels.loop_start, - ); + program.emit_insn(Insn::Goto { + target_pc: loop_labels.loop_start, + }); } SourceOperator::Join { id, @@ -771,7 +743,7 @@ pub fn close_loop( // If the left join match flag has been set to 1, we jump to the next row on the outer table, // i.e. continue to the next row of t1 in our example. program.resolve_label(lj_meta.label_match_flag_check_value, program.offset()); - let jump_offset = program.offset() + 3; + let jump_offset = program.offset().add(3u32); program.emit_insn(Insn::IfPos { reg: lj_meta.reg_match_flag, target_pc: jump_offset, @@ -799,12 +771,9 @@ pub fn close_loop( // and we will end up back in the IfPos instruction above, which will then // check the match flag again, and since it is now 1, we will jump to the // next row in the left table. - program.emit_insn_with_label_dependency( - Insn::Goto { - target_pc: lj_meta.label_match_flag_set_true, - }, - lj_meta.label_match_flag_set_true, - ); + program.emit_insn(Insn::Goto { + target_pc: lj_meta.label_match_flag_set_true, + }); assert!(program.offset() == jump_offset); } @@ -830,21 +799,15 @@ pub fn close_loop( .as_ref() .is_some_and(|dir| *dir == IterationDirection::Backwards) { - program.emit_insn_with_label_dependency( - Insn::PrevAwait { - cursor_id, - pc_if_next: loop_labels.loop_start, - }, - loop_labels.loop_start, - ); + program.emit_insn(Insn::PrevAwait { + cursor_id, + pc_if_next: loop_labels.loop_start, + }); } else { - program.emit_insn_with_label_dependency( - Insn::NextAwait { - cursor_id, - pc_if_next: loop_labels.loop_start, - }, - loop_labels.loop_start, - ); + program.emit_insn(Insn::NextAwait { + cursor_id, + pc_if_next: loop_labels.loop_start, + }); } } SourceOperator::Search { @@ -866,13 +829,10 @@ pub fn close_loop( }; program.emit_insn(Insn::NextAsync { cursor_id }); - program.emit_insn_with_label_dependency( - Insn::NextAwait { - cursor_id, - pc_if_next: loop_labels.loop_start, - }, - loop_labels.loop_start, - ); + program.emit_insn(Insn::NextAwait { + cursor_id, + pc_if_next: loop_labels.loop_start, + }); } SourceOperator::Nothing { .. } => {} }; diff --git a/core/translate/mod.rs b/core/translate/mod.rs index f8adb0e09..92b661ce1 100644 --- a/core/translate/mod.rs +++ b/core/translate/mod.rs @@ -388,12 +388,9 @@ fn translate_create_table( if schema.get_table(tbl_name.name.0.as_str()).is_some() { if if_not_exists { let init_label = program.allocate_label(); - program.emit_insn_with_label_dependency( - Insn::Init { - target_pc: init_label, - }, - init_label, - ); + program.emit_insn(Insn::Init { + target_pc: init_label, + }); let start_offset = program.offset(); program.emit_insn(Insn::Halt { err_code: 0, @@ -414,12 +411,9 @@ fn translate_create_table( let parse_schema_label = program.allocate_label(); let init_label = program.allocate_label(); - program.emit_insn_with_label_dependency( - Insn::Init { - target_pc: init_label, - }, - init_label, - ); + program.emit_insn(Insn::Init { + target_pc: init_label, + }); let start_offset = program.offset(); // TODO: ReadCookie // TODO: If @@ -544,12 +538,9 @@ fn translate_pragma( ) -> Result { let mut program = ProgramBuilder::new(); let init_label = program.allocate_label(); - program.emit_insn_with_label_dependency( - Insn::Init { - target_pc: init_label, - }, - init_label, - ); + program.emit_insn(Insn::Init { + target_pc: init_label, + }); let start_offset = program.offset(); let mut write = false; match body { @@ -581,7 +572,6 @@ fn translate_pragma( program.emit_insn(Insn::Goto { target_pc: start_offset, }); - program.resolve_deferred_labels(); Ok(program.build(database_header, connection)) } diff --git a/core/translate/order_by.rs b/core/translate/order_by.rs index d05fccd60..b58e7ec21 100644 --- a/core/translate/order_by.rs +++ b/core/translate/order_by.rs @@ -110,15 +110,12 @@ pub fn emit_order_by( num_fields: num_columns_in_sorter, }); - program.emit_insn_with_label_dependency( - Insn::SorterSort { - cursor_id: sort_cursor, - pc_if_empty: sort_loop_end_label, - }, - sort_loop_end_label, - ); + program.emit_insn(Insn::SorterSort { + cursor_id: sort_cursor, + pc_if_empty: sort_loop_end_label, + }); - program.defer_label_resolution(sort_loop_start_label, program.offset() as usize); + program.resolve_label(sort_loop_start_label, program.offset()); program.emit_insn(Insn::SorterData { cursor_id: sort_cursor, dest_reg: reg_sorter_data, @@ -140,13 +137,10 @@ pub fn emit_order_by( emit_result_row_and_limit(program, t_ctx, plan, start_reg, Some(sort_loop_end_label))?; - program.emit_insn_with_label_dependency( - Insn::SorterNext { - cursor_id: sort_cursor, - pc_if_next: sort_loop_start_label, - }, - sort_loop_start_label, - ); + program.emit_insn(Insn::SorterNext { + cursor_id: sort_cursor, + pc_if_next: sort_loop_start_label, + }); program.resolve_label(sort_loop_end_label, program.offset()); diff --git a/core/translate/planner.rs b/core/translate/planner.rs index 1f0bf687b..b12391579 100644 --- a/core/translate/planner.rs +++ b/core/translate/planner.rs @@ -294,7 +294,7 @@ fn parse_from_clause_table( }; subplan.query_type = SelectQueryType::Subquery { yield_reg: usize::MAX, // will be set later in bytecode emission - coroutine_implementation_start: BranchOffset::MAX, // will be set later in bytecode emission + coroutine_implementation_start: BranchOffset::Placeholder, // will be set later in bytecode emission }; let identifier = maybe_alias .map(|a| match a { diff --git a/core/translate/result_row.rs b/core/translate/result_row.rs index 4901cc9ec..5c76f3008 100644 --- a/core/translate/result_row.rs +++ b/core/translate/result_row.rs @@ -54,7 +54,7 @@ pub fn emit_result_row_and_limit( SelectQueryType::Subquery { yield_reg, .. } => { program.emit_insn(Insn::Yield { yield_reg: *yield_reg, - end_offset: 0, + end_offset: BranchOffset::Offset(0), }); } } @@ -71,13 +71,10 @@ pub fn emit_result_row_and_limit( dest: t_ctx.reg_limit.unwrap(), }); program.mark_last_insn_constant(); - program.emit_insn_with_label_dependency( - Insn::DecrJumpZero { - reg: t_ctx.reg_limit.unwrap(), - target_pc: label_on_limit_reached.unwrap(), - }, - label_on_limit_reached.unwrap(), - ); + program.emit_insn(Insn::DecrJumpZero { + reg: t_ctx.reg_limit.unwrap(), + target_pc: label_on_limit_reached.unwrap(), + }); } Ok(()) } diff --git a/core/translate/subquery.rs b/core/translate/subquery.rs index cb0527a6d..206da45ba 100644 --- a/core/translate/subquery.rs +++ b/core/translate/subquery.rs @@ -72,7 +72,7 @@ pub fn emit_subquery<'a>( t_ctx: &mut TranslateCtx<'a>, ) -> Result { let yield_reg = program.alloc_register(); - let coroutine_implementation_start_offset = program.offset() + 1; + let coroutine_implementation_start_offset = program.offset().add(1u32); match &mut plan.query_type { SelectQueryType::Subquery { yield_reg: y, @@ -100,14 +100,11 @@ pub fn emit_subquery<'a>( resolver: Resolver::new(t_ctx.resolver.symbol_table), }; let subquery_body_end_label = program.allocate_label(); - program.emit_insn_with_label_dependency( - Insn::InitCoroutine { - yield_reg, - jump_on_definition: subquery_body_end_label, - start_offset: coroutine_implementation_start_offset, - }, - subquery_body_end_label, - ); + program.emit_insn(Insn::InitCoroutine { + yield_reg, + jump_on_definition: subquery_body_end_label, + start_offset: coroutine_implementation_start_offset, + }); // Normally we mark each LIMIT value as a constant insn that is emitted only once, but in the case of a subquery, // we need to initialize it every time the subquery is run; otherwise subsequent runs of the subquery will already // have the LIMIT counter at 0, and will never return rows. diff --git a/core/vdbe/builder.rs b/core/vdbe/builder.rs index 561765738..40f936cd1 100644 --- a/core/vdbe/builder.rs +++ b/core/vdbe/builder.rs @@ -11,23 +11,20 @@ use super::{BranchOffset, CursorID, Insn, InsnReference, Program, Table}; #[allow(dead_code)] pub struct ProgramBuilder { next_free_register: usize, - next_free_label: BranchOffset, + next_free_label: i32, next_free_cursor_id: usize, insns: Vec, // for temporarily storing instructions that will be put after Transaction opcode constant_insns: Vec, - // Each label has a list of InsnReferences that must - // be resolved. Lists are indexed by: label.abs() - 1 - unresolved_labels: Vec>, next_insn_label: Option, // Cursors that are referenced by the program. Indexed by CursorID. pub cursor_ref: Vec<(Option, Option)>, - // List of deferred label resolutions. Each entry is a pair of (label, insn_reference). - deferred_label_resolutions: Vec<(BranchOffset, InsnReference)>, + // Hashmap of label to insn reference. Resolved in build(). + label_to_resolved_offset: HashMap, // Bitmask of cursors that have emitted a SeekRowid instruction. seekrowid_emitted_bitmask: u64, // map of instruction index to manual comment (used in EXPLAIN) - comments: HashMap, + comments: HashMap, } impl ProgramBuilder { @@ -37,11 +34,10 @@ impl ProgramBuilder { next_free_label: 0, next_free_cursor_id: 0, insns: Vec::new(), - unresolved_labels: Vec::new(), next_insn_label: None, cursor_ref: Vec::new(), constant_insns: Vec::new(), - deferred_label_resolutions: Vec::new(), + label_to_resolved_offset: HashMap::new(), seekrowid_emitted_bitmask: 0, comments: HashMap::new(), } @@ -71,20 +67,17 @@ impl ProgramBuilder { cursor } - fn _emit_insn(&mut self, insn: Insn) { + pub fn emit_insn(&mut self, insn: Insn) { + if let Some(label) = self.next_insn_label { + self.label_to_resolved_offset + .insert(label.to_label_value(), self.insns.len() as InsnReference); + self.next_insn_label = None; + } self.insns.push(insn); } - pub fn emit_insn(&mut self, insn: Insn) { - self._emit_insn(insn); - if let Some(label) = self.next_insn_label { - self.next_insn_label = None; - self.resolve_label(label, (self.insns.len() - 1) as BranchOffset); - } - } - pub fn add_comment(&mut self, insn_index: BranchOffset, comment: &'static str) { - self.comments.insert(insn_index, comment); + self.comments.insert(insn_index.to_offset_int(), comment); } // Emit an instruction that will be put at the end of the program (after Transaction statement). @@ -99,19 +92,13 @@ impl ProgramBuilder { self.insns.append(&mut self.constant_insns); } - pub fn emit_insn_with_label_dependency(&mut self, insn: Insn, label: BranchOffset) { - self._emit_insn(insn); - self.add_label_dependency(label, (self.insns.len() - 1) as BranchOffset); - } - pub fn offset(&self) -> BranchOffset { - self.insns.len() as BranchOffset + BranchOffset::Offset(self.insns.len() as InsnReference) } pub fn allocate_label(&mut self) -> BranchOffset { self.next_free_label -= 1; - self.unresolved_labels.push(Vec::new()); - self.next_free_label + BranchOffset::Label(self.next_free_label) } // Effectively a GOTO without the need to emit an explicit GOTO instruction. @@ -121,232 +108,187 @@ impl ProgramBuilder { self.next_insn_label = Some(label); } - fn label_to_index(&self, label: BranchOffset) -> usize { - (label.abs() - 1) as usize - } - - pub fn add_label_dependency(&mut self, label: BranchOffset, insn_reference: BranchOffset) { - assert!(insn_reference >= 0); - assert!(label < 0); - let label_index = self.label_to_index(label); - assert!(label_index < self.unresolved_labels.len()); - let insn_reference = insn_reference as InsnReference; - let label_references = &mut self.unresolved_labels[label_index]; - label_references.push(insn_reference); - } - - pub fn defer_label_resolution(&mut self, label: BranchOffset, insn_reference: InsnReference) { - self.deferred_label_resolutions - .push((label, insn_reference)); + pub fn resolve_label(&mut self, label: BranchOffset, to_offset: BranchOffset) { + assert!(matches!(label, BranchOffset::Label(_))); + assert!(matches!(to_offset, BranchOffset::Offset(_))); + self.label_to_resolved_offset + .insert(label.to_label_value(), to_offset.to_offset_int()); } /// Resolve unresolved labels to a specific offset in the instruction list. /// - /// This function updates all instructions that reference the given label - /// to point to the specified offset. It ensures that the label and offset - /// are valid and updates the target program counter (PC) of each instruction - /// that references the label. - /// - /// # Arguments - /// - /// * `label` - The label to resolve. - /// * `to_offset` - The offset to which the labeled instructions should be resolved to. - pub fn resolve_label(&mut self, label: BranchOffset, to_offset: BranchOffset) { - assert!(label < 0); - assert!(to_offset >= 0); - let label_index = self.label_to_index(label); - assert!( - label_index < self.unresolved_labels.len(), - "Forbidden resolve of an unexistent label!" - ); - - let label_references = &mut self.unresolved_labels[label_index]; - for insn_reference in label_references.iter() { - let insn = &mut self.insns[*insn_reference]; + /// This function scans all instructions and resolves any labels to their corresponding offsets. + /// It ensures that all labels are resolved correctly and updates the target program counter (PC) + /// of each instruction that references a label. + pub fn resolve_labels(&mut self) { + let resolve = |pc: &mut BranchOffset, insn_name: &str| { + if let BranchOffset::Label(label) = pc { + let to_offset = *self.label_to_resolved_offset.get(label).unwrap_or_else(|| { + panic!("Reference to undefined label in {}: {}", insn_name, label) + }); + *pc = BranchOffset::Offset(to_offset); + } + }; + for insn in self.insns.iter_mut() { match insn { Insn::Init { target_pc } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "Init"); } Insn::Eq { lhs: _lhs, rhs: _rhs, target_pc, } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "Eq"); } Insn::Ne { lhs: _lhs, rhs: _rhs, target_pc, } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "Ne"); } Insn::Lt { lhs: _lhs, rhs: _rhs, target_pc, } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "Lt"); } Insn::Le { lhs: _lhs, rhs: _rhs, target_pc, } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "Le"); } Insn::Gt { lhs: _lhs, rhs: _rhs, target_pc, } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "Gt"); } Insn::Ge { lhs: _lhs, rhs: _rhs, target_pc, } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "Ge"); } Insn::If { reg: _reg, target_pc, null_reg: _, } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "If"); } Insn::IfNot { reg: _reg, target_pc, null_reg: _, } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "IfNot"); } Insn::RewindAwait { cursor_id: _cursor_id, pc_if_empty, } => { - assert!(*pc_if_empty < 0); - *pc_if_empty = to_offset; + resolve(pc_if_empty, "RewindAwait"); } Insn::LastAwait { cursor_id: _cursor_id, pc_if_empty, } => { - assert!(*pc_if_empty < 0); - *pc_if_empty = to_offset; + resolve(pc_if_empty, "LastAwait"); } Insn::Goto { target_pc } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "Goto"); } Insn::DecrJumpZero { reg: _reg, target_pc, } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "DecrJumpZero"); } Insn::SorterNext { cursor_id: _cursor_id, pc_if_next, } => { - assert!(*pc_if_next < 0); - *pc_if_next = to_offset; + resolve(pc_if_next, "SorterNext"); } Insn::SorterSort { pc_if_empty, .. } => { - assert!(*pc_if_empty < 0); - *pc_if_empty = to_offset; + resolve(pc_if_empty, "SorterSort"); } Insn::NotNull { reg: _reg, target_pc, } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "NotNull"); } Insn::IfPos { target_pc, .. } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "IfPos"); } Insn::NextAwait { pc_if_next, .. } => { - assert!(*pc_if_next < 0); - *pc_if_next = to_offset; + resolve(pc_if_next, "NextAwait"); } Insn::PrevAwait { pc_if_next, .. } => { - assert!(*pc_if_next < 0); - *pc_if_next = to_offset; + resolve(pc_if_next, "PrevAwait"); } Insn::InitCoroutine { yield_reg: _, jump_on_definition, start_offset: _, } => { - *jump_on_definition = to_offset; + resolve(jump_on_definition, "InitCoroutine"); } Insn::NotExists { cursor: _, rowid_reg: _, target_pc, } => { - *target_pc = to_offset; + resolve(target_pc, "NotExists"); } Insn::Yield { yield_reg: _, end_offset, } => { - *end_offset = to_offset; + resolve(end_offset, "Yield"); } Insn::SeekRowid { target_pc, .. } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "SeekRowid"); } Insn::Gosub { target_pc, .. } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "Gosub"); } - Insn::Jump { target_pc_eq, .. } => { - // FIXME: this current implementation doesnt scale for insns that - // have potentially multiple label dependencies. - assert!(*target_pc_eq < 0); - *target_pc_eq = to_offset; + Insn::Jump { + target_pc_eq, + target_pc_lt, + target_pc_gt, + } => { + resolve(target_pc_eq, "Jump"); + resolve(target_pc_lt, "Jump"); + resolve(target_pc_gt, "Jump"); } Insn::SeekGE { target_pc, .. } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "SeekGE"); } Insn::SeekGT { target_pc, .. } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "SeekGT"); } Insn::IdxGE { target_pc, .. } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "IdxGE"); } Insn::IdxGT { target_pc, .. } => { - assert!(*target_pc < 0); - *target_pc = to_offset; + resolve(target_pc, "IdxGT"); } Insn::IsNull { src: _, target_pc } => { - assert!(*target_pc < 0); - *target_pc = to_offset; - } - _ => { - todo!("missing resolve_label for {:?}", insn); + resolve(target_pc, "IsNull"); } + _ => continue, } } - label_references.clear(); + self.label_to_resolved_offset.clear(); } // translate table to cursor id @@ -361,23 +303,12 @@ impl ProgramBuilder { .unwrap() } - pub fn resolve_deferred_labels(&mut self) { - for i in 0..self.deferred_label_resolutions.len() { - let (label, insn_reference) = self.deferred_label_resolutions[i]; - self.resolve_label(label, insn_reference as BranchOffset); - } - self.deferred_label_resolutions.clear(); - } - pub fn build( - self, + mut self, database_header: Rc>, connection: Weak, ) -> Program { - assert!( - self.deferred_label_resolutions.is_empty(), - "deferred_label_resolutions is not empty when build() is called, did you forget to call resolve_deferred_labels()?" - ); + self.resolve_labels(); assert!( self.constant_insns.is_empty(), "constant_insns is not empty when build() is called, did you forget to call emit_constant_insns()?" diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index a17ababcf..133172b78 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -13,11 +13,11 @@ pub fn insn_to_str( Insn::Init { target_pc } => ( "Init", 0, - *target_pc as i32, + target_pc.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, - format!("Start at {}", target_pc), + format!("Start at {}", target_pc.to_debug_int()), ), Insn::Add { lhs, rhs, dest } => ( "Add", @@ -114,11 +114,11 @@ pub fn insn_to_str( Insn::NotNull { reg, target_pc } => ( "NotNull", *reg as i32, - *target_pc as i32, + target_pc.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, - format!("r[{}]!=NULL -> goto {}", reg, target_pc), + format!("r[{}]!=NULL -> goto {}", reg, target_pc.to_debug_int()), ), Insn::Compare { start_reg_a, @@ -145,9 +145,9 @@ pub fn insn_to_str( target_pc_gt, } => ( "Jump", - *target_pc_lt as i32, - *target_pc_eq as i32, - *target_pc_gt as i32, + target_pc_lt.to_debug_int(), + target_pc_eq.to_debug_int(), + target_pc_gt.to_debug_int(), OwnedValue::build_text(Rc::new("".to_string())), 0, "".to_string(), @@ -178,13 +178,16 @@ pub fn insn_to_str( } => ( "IfPos", *reg as i32, - *target_pc as i32, + target_pc.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, format!( "r[{}]>0 -> r[{}]-={}, goto {}", - reg, reg, decrement_by, target_pc + reg, + reg, + decrement_by, + target_pc.to_debug_int() ), ), Insn::Eq { @@ -195,10 +198,15 @@ pub fn insn_to_str( "Eq", *lhs as i32, *rhs as i32, - *target_pc as i32, + target_pc.to_debug_int(), OwnedValue::build_text(Rc::new("".to_string())), 0, - format!("if r[{}]==r[{}] goto {}", lhs, rhs, target_pc), + format!( + "if r[{}]==r[{}] goto {}", + lhs, + rhs, + target_pc.to_debug_int() + ), ), Insn::Ne { lhs, @@ -208,10 +216,15 @@ pub fn insn_to_str( "Ne", *lhs as i32, *rhs as i32, - *target_pc as i32, + target_pc.to_debug_int(), OwnedValue::build_text(Rc::new("".to_string())), 0, - format!("if r[{}]!=r[{}] goto {}", lhs, rhs, target_pc), + format!( + "if r[{}]!=r[{}] goto {}", + lhs, + rhs, + target_pc.to_debug_int() + ), ), Insn::Lt { lhs, @@ -221,10 +234,10 @@ pub fn insn_to_str( "Lt", *lhs as i32, *rhs as i32, - *target_pc as i32, + target_pc.to_debug_int(), OwnedValue::build_text(Rc::new("".to_string())), 0, - format!("if r[{}]r[{}] goto {}", lhs, rhs, target_pc), + format!("if r[{}]>r[{}] goto {}", lhs, rhs, target_pc.to_debug_int()), ), Insn::Ge { lhs, @@ -260,10 +278,15 @@ pub fn insn_to_str( "Ge", *lhs as i32, *rhs as i32, - *target_pc as i32, + target_pc.to_debug_int(), OwnedValue::build_text(Rc::new("".to_string())), 0, - format!("if r[{}]>=r[{}] goto {}", lhs, rhs, target_pc), + format!( + "if r[{}]>=r[{}] goto {}", + lhs, + rhs, + target_pc.to_debug_int() + ), ), Insn::If { reg, @@ -272,11 +295,11 @@ pub fn insn_to_str( } => ( "If", *reg as i32, - *target_pc as i32, + target_pc.to_debug_int(), *null_reg as i32, OwnedValue::build_text(Rc::new("".to_string())), 0, - format!("if r[{}] goto {}", reg, target_pc), + format!("if r[{}] goto {}", reg, target_pc.to_debug_int()), ), Insn::IfNot { reg, @@ -285,11 +308,11 @@ pub fn insn_to_str( } => ( "IfNot", *reg as i32, - *target_pc as i32, + target_pc.to_debug_int(), *null_reg as i32, OwnedValue::build_text(Rc::new("".to_string())), 0, - format!("if !r[{}] goto {}", reg, target_pc), + format!("if !r[{}] goto {}", reg, target_pc.to_debug_int()), ), Insn::OpenReadAsync { cursor_id, @@ -347,7 +370,7 @@ pub fn insn_to_str( } => ( "RewindAwait", *cursor_id as i32, - *pc_if_empty as i32, + pc_if_empty.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -431,7 +454,7 @@ pub fn insn_to_str( } => ( "NextAwait", *cursor_id as i32, - *pc_if_next as i32, + pc_if_next.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -461,7 +484,7 @@ pub fn insn_to_str( Insn::Goto { target_pc } => ( "Goto", 0, - *target_pc as i32, + target_pc.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -473,7 +496,7 @@ pub fn insn_to_str( } => ( "Gosub", *return_reg as i32, - *target_pc as i32, + target_pc.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -562,7 +585,7 @@ pub fn insn_to_str( "SeekRowid", *cursor_id as i32, *src_reg as i32, - *target_pc as i32, + target_pc.to_debug_int(), OwnedValue::build_text(Rc::new("".to_string())), 0, format!( @@ -572,7 +595,7 @@ pub fn insn_to_str( .0 .as_ref() .unwrap_or(&format!("cursor {}", cursor_id)), - target_pc + target_pc.to_debug_int() ), ), Insn::DeferredSeek { @@ -596,7 +619,7 @@ pub fn insn_to_str( } => ( "SeekGT", *cursor_id as i32, - *target_pc as i32, + target_pc.to_debug_int(), *start_reg as i32, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -611,7 +634,7 @@ pub fn insn_to_str( } => ( "SeekGE", *cursor_id as i32, - *target_pc as i32, + target_pc.to_debug_int(), *start_reg as i32, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -625,7 +648,7 @@ pub fn insn_to_str( } => ( "IdxGT", *cursor_id as i32, - *target_pc as i32, + target_pc.to_debug_int(), *start_reg as i32, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -639,7 +662,7 @@ pub fn insn_to_str( } => ( "IdxGE", *cursor_id as i32, - *target_pc as i32, + target_pc.to_debug_int(), *start_reg as i32, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -648,11 +671,11 @@ pub fn insn_to_str( Insn::DecrJumpZero { reg, target_pc } => ( "DecrJumpZero", *reg as i32, - *target_pc as i32, + target_pc.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, - format!("if (--r[{}]==0) goto {}", reg, target_pc), + format!("if (--r[{}]==0) goto {}", reg, target_pc.to_debug_int()), ), Insn::AggStep { func, @@ -742,7 +765,7 @@ pub fn insn_to_str( } => ( "SorterSort", *cursor_id as i32, - *pc_if_empty as i32, + pc_if_empty.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -754,7 +777,7 @@ pub fn insn_to_str( } => ( "SorterNext", *cursor_id as i32, - *pc_if_next as i32, + pc_if_next.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -792,8 +815,8 @@ pub fn insn_to_str( } => ( "InitCoroutine", *yield_reg as i32, - *jump_on_definition as i32, - *start_offset as i32, + jump_on_definition.to_debug_int(), + start_offset.to_debug_int(), OwnedValue::build_text(Rc::new("".to_string())), 0, "".to_string(), @@ -813,7 +836,7 @@ pub fn insn_to_str( } => ( "Yield", *yield_reg as i32, - *end_offset as i32, + end_offset.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -898,7 +921,7 @@ pub fn insn_to_str( } => ( "NotExists", *cursor as i32, - *target_pc as i32, + target_pc.to_debug_int(), *rowid_reg as i32, OwnedValue::build_text(Rc::new("".to_string())), 0, @@ -968,11 +991,11 @@ pub fn insn_to_str( Insn::IsNull { src, target_pc } => ( "IsNull", *src as i32, - *target_pc as i32, + target_pc.to_debug_int(), 0, OwnedValue::build_text(Rc::new("".to_string())), 0, - format!("if (r[{}]==NULL) goto {}", src, target_pc), + format!("if (r[{}]==NULL) goto {}", src, target_pc.to_debug_int()), ), Insn::ParseSchema { db, where_clause } => ( "ParseSchema", diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index a0b42abb6..c9151e934 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -58,13 +58,75 @@ use std::cell::RefCell; use std::collections::{BTreeMap, HashMap}; use std::rc::{Rc, Weak}; -pub type BranchOffset = i64; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +/// Represents a target for a jump instruction. +/// Stores 32-bit ints to keep the enum word-sized. +pub enum BranchOffset { + /// A label is a named location in the program. + /// If there are references to it, it must always be resolved to an Offset + /// via program.resolve_label(). + Label(i32), + /// An offset is a direct index into the instruction list. + Offset(InsnReference), + /// A placeholder is a temporary value to satisfy the compiler. + /// It must be set later. + Placeholder, +} + +impl BranchOffset { + /// Returns true if the branch offset is a label. + pub fn is_label(&self) -> bool { + matches!(self, BranchOffset::Label(_)) + } + + /// Returns true if the branch offset is an offset. + pub fn is_offset(&self) -> bool { + matches!(self, BranchOffset::Offset(_)) + } + + /// Returns the offset value. Panics if the branch offset is a label or placeholder. + pub fn to_offset_int(&self) -> InsnReference { + match self { + BranchOffset::Label(v) => unreachable!("Unresolved label: {}", v), + BranchOffset::Offset(v) => *v, + BranchOffset::Placeholder => unreachable!("Unresolved placeholder"), + } + } + + /// Returns the label value. Panics if the branch offset is an offset or placeholder. + pub fn to_label_value(&self) -> i32 { + match self { + BranchOffset::Label(v) => *v, + BranchOffset::Offset(_) => unreachable!("Offset cannot be converted to label value"), + BranchOffset::Placeholder => unreachable!("Unresolved placeholder"), + } + } + + /// Returns the branch offset as a signed integer. + /// Used in explain output, where we don't want to panic in case we have an unresolved + /// label or placeholder. + pub fn to_debug_int(&self) -> i32 { + match self { + BranchOffset::Label(v) => *v, + BranchOffset::Offset(v) => *v as i32, + BranchOffset::Placeholder => i32::MAX, + } + } + + /// Adds an integer value to the branch offset. + /// Returns a new branch offset. + /// Panics if the branch offset is a label or placeholder. + pub fn add>(self, n: N) -> BranchOffset { + BranchOffset::Offset(self.to_offset_int() + n.into()) + } +} + pub type CursorID = usize; pub type PageIdx = usize; // Index of insn in list of insns -type InsnReference = usize; +type InsnReference = u32; pub enum StepResult<'a> { Done, @@ -101,7 +163,7 @@ impl RegexCache { /// The program state describes the environment in which the program executes. pub struct ProgramState { - pub pc: BranchOffset, + pub pc: InsnReference, cursors: RefCell>>, registers: Vec, last_compare: Option, @@ -151,7 +213,7 @@ pub struct Program { pub insns: Vec, pub cursor_ref: Vec<(Option, Option
)>, pub database_header: Rc>, - pub comments: HashMap, + pub comments: HashMap, pub connection: Weak, pub auto_commit: bool, } @@ -189,8 +251,8 @@ impl Program { let mut cursors = state.cursors.borrow_mut(); match insn { Insn::Init { target_pc } => { - assert!(*target_pc >= 0); - state.pc = *target_pc; + assert!(target_pc.is_offset()); + state.pc = target_pc.to_offset_int(); } Insn::Add { lhs, rhs, dest } => { state.registers[*dest] = @@ -278,6 +340,9 @@ impl Program { target_pc_eq, target_pc_gt, } => { + assert!(target_pc_lt.is_offset()); + assert!(target_pc_eq.is_offset()); + assert!(target_pc_gt.is_offset()); let cmp = state.last_compare.take(); if cmp.is_none() { return Err(LimboError::InternalError( @@ -289,8 +354,7 @@ impl Program { std::cmp::Ordering::Equal => *target_pc_eq, std::cmp::Ordering::Greater => *target_pc_gt, }; - assert!(target_pc >= 0); - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } Insn::Move { source_reg, @@ -313,12 +377,12 @@ impl Program { target_pc, decrement_by, } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); let reg = *reg; let target_pc = *target_pc; match &state.registers[reg] { OwnedValue::Integer(n) if *n > 0 => { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); state.registers[reg] = OwnedValue::Integer(*n - *decrement_by as i64); } OwnedValue::Integer(_) => { @@ -332,7 +396,7 @@ impl Program { } } Insn::NotNull { reg, target_pc } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); let reg = *reg; let target_pc = *target_pc; match &state.registers[reg] { @@ -340,7 +404,7 @@ impl Program { state.pc += 1; } _ => { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } } } @@ -350,17 +414,17 @@ impl Program { rhs, target_pc, } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } _ => { if state.registers[lhs] == state.registers[rhs] { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -372,17 +436,17 @@ impl Program { rhs, target_pc, } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } _ => { if state.registers[lhs] != state.registers[rhs] { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -394,17 +458,17 @@ impl Program { rhs, target_pc, } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } _ => { if state.registers[lhs] < state.registers[rhs] { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -416,17 +480,17 @@ impl Program { rhs, target_pc, } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } _ => { if state.registers[lhs] <= state.registers[rhs] { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -438,17 +502,17 @@ impl Program { rhs, target_pc, } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } _ => { if state.registers[lhs] > state.registers[rhs] { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -460,17 +524,17 @@ impl Program { rhs, target_pc, } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); let lhs = *lhs; let rhs = *rhs; let target_pc = *target_pc; match (&state.registers[lhs], &state.registers[rhs]) { (_, OwnedValue::Null) | (OwnedValue::Null, _) => { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } _ => { if state.registers[lhs] >= state.registers[rhs] { - state.pc = target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -482,9 +546,9 @@ impl Program { target_pc, null_reg, } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); if exec_if(&state.registers[*reg], &state.registers[*null_reg], false) { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -494,9 +558,9 @@ impl Program { target_pc, null_reg, } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); if exec_if(&state.registers[*reg], &state.registers[*null_reg], true) { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -539,10 +603,11 @@ impl Program { cursor_id, pc_if_empty, } => { + assert!(pc_if_empty.is_offset()); let cursor = cursors.get_mut(cursor_id).unwrap(); cursor.wait_for_completion()?; if cursor.is_empty() { - state.pc = *pc_if_empty; + state.pc = pc_if_empty.to_offset_int(); } else { state.pc += 1; } @@ -551,10 +616,11 @@ impl Program { cursor_id, pc_if_empty, } => { + assert!(pc_if_empty.is_offset()); let cursor = cursors.get_mut(cursor_id).unwrap(); cursor.wait_for_completion()?; if cursor.is_empty() { - state.pc = *pc_if_empty; + state.pc = pc_if_empty.to_offset_int(); } else { state.pc += 1; } @@ -620,11 +686,11 @@ impl Program { cursor_id, pc_if_next, } => { - assert!(*pc_if_next >= 0); + assert!(pc_if_next.is_offset()); let cursor = cursors.get_mut(cursor_id).unwrap(); cursor.wait_for_completion()?; if !cursor.is_empty() { - state.pc = *pc_if_next; + state.pc = pc_if_next.to_offset_int(); } else { state.pc += 1; } @@ -633,11 +699,11 @@ impl Program { cursor_id, pc_if_next, } => { - assert!(*pc_if_next >= 0); + assert!(pc_if_next.is_offset()); let cursor = cursors.get_mut(cursor_id).unwrap(); cursor.wait_for_completion()?; if !cursor.is_empty() { - state.pc = *pc_if_next; + state.pc = pc_if_next.to_offset_int(); } else { state.pc += 1; } @@ -705,24 +771,22 @@ impl Program { state.pc += 1; } Insn::Goto { target_pc } => { - assert!(*target_pc >= 0); - state.pc = *target_pc; + assert!(target_pc.is_offset()); + state.pc = target_pc.to_offset_int(); } Insn::Gosub { target_pc, return_reg, } => { - assert!(*target_pc >= 0); - state.registers[*return_reg] = OwnedValue::Integer(state.pc + 1); - state.pc = *target_pc; + assert!(target_pc.is_offset()); + state.registers[*return_reg] = OwnedValue::Integer((state.pc + 1) as i64); + state.pc = target_pc.to_offset_int(); } Insn::Return { return_reg } => { if let OwnedValue::Integer(pc) = state.registers[*return_reg] { - if pc < 0 { - return Err(LimboError::InternalError( - "Return register is negative".to_string(), - )); - } + let pc: u32 = pc + .try_into() + .unwrap_or_else(|_| panic!("Return register is negative: {}", pc)); state.pc = pc; } else { return Err(LimboError::InternalError( @@ -779,11 +843,12 @@ impl Program { src_reg, target_pc, } => { + assert!(target_pc.is_offset()); let cursor = cursors.get_mut(cursor_id).unwrap(); let rowid = match &state.registers[*src_reg] { OwnedValue::Integer(rowid) => *rowid as u64, OwnedValue::Null => { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); continue; } other => { @@ -794,7 +859,7 @@ impl Program { }; let found = return_if_io!(cursor.seek(SeekKey::TableRowId(rowid), SeekOp::EQ)); if !found { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -813,6 +878,7 @@ impl Program { target_pc, is_index, } => { + assert!(target_pc.is_offset()); if *is_index { let cursor = cursors.get_mut(cursor_id).unwrap(); let record_from_regs: OwnedRecord = @@ -821,7 +887,7 @@ impl Program { cursor.seek(SeekKey::IndexKey(&record_from_regs), SeekOp::GE) ); if !found { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -844,7 +910,7 @@ impl Program { let found = return_if_io!(cursor.seek(SeekKey::TableRowId(rowid), SeekOp::GE)); if !found { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -857,6 +923,7 @@ impl Program { target_pc, is_index, } => { + assert!(target_pc.is_offset()); if *is_index { let cursor = cursors.get_mut(cursor_id).unwrap(); let record_from_regs: OwnedRecord = @@ -865,7 +932,7 @@ impl Program { cursor.seek(SeekKey::IndexKey(&record_from_regs), SeekOp::GT) ); if !found { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -888,7 +955,7 @@ impl Program { let found = return_if_io!(cursor.seek(SeekKey::TableRowId(rowid), SeekOp::GT)); if !found { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -900,7 +967,7 @@ impl Program { num_regs, target_pc, } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); let cursor = cursors.get_mut(cursor_id).unwrap(); let record_from_regs: OwnedRecord = make_owned_record(&state.registers, start_reg, num_regs); @@ -909,12 +976,12 @@ impl Program { if idx_record.values[..idx_record.values.len() - 1] >= *record_from_regs.values { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } else { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } } Insn::IdxGT { @@ -923,6 +990,7 @@ impl Program { num_regs, target_pc, } => { + assert!(target_pc.is_offset()); let cursor = cursors.get_mut(cursor_id).unwrap(); let record_from_regs: OwnedRecord = make_owned_record(&state.registers, start_reg, num_regs); @@ -931,21 +999,21 @@ impl Program { if idx_record.values[..idx_record.values.len() - 1] > *record_from_regs.values { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } } else { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } } Insn::DecrJumpZero { reg, target_pc } => { - assert!(*target_pc >= 0); + assert!(target_pc.is_offset()); match state.registers[*reg] { OwnedValue::Integer(n) => { let n = n - 1; if n == 0 { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } else { state.registers[*reg] = OwnedValue::Integer(n); state.pc += 1; @@ -1250,18 +1318,18 @@ impl Program { cursor.rewind()?; state.pc += 1; } else { - state.pc = *pc_if_empty; + state.pc = pc_if_empty.to_offset_int(); } } Insn::SorterNext { cursor_id, pc_if_next, } => { - assert!(*pc_if_next >= 0); + assert!(pc_if_next.is_offset()); let cursor = cursors.get_mut(cursor_id).unwrap(); return_if_io!(cursor.next()); if !cursor.is_empty() { - state.pc = *pc_if_next; + state.pc = pc_if_next.to_offset_int(); } else { state.pc += 1; } @@ -1726,18 +1794,23 @@ impl Program { jump_on_definition, start_offset, } => { - assert!(*jump_on_definition >= 0); - state.registers[*yield_reg] = OwnedValue::Integer(*start_offset); + assert!(jump_on_definition.is_offset()); + let start_offset = start_offset.to_offset_int(); + state.registers[*yield_reg] = OwnedValue::Integer(start_offset as i64); state.ended_coroutine.insert(*yield_reg, false); - state.pc = if *jump_on_definition == 0 { + let jump_on_definition = jump_on_definition.to_offset_int(); + state.pc = if jump_on_definition == 0 { state.pc + 1 } else { - *jump_on_definition + jump_on_definition }; } Insn::EndCoroutine { yield_reg } => { if let OwnedValue::Integer(pc) = state.registers[*yield_reg] { state.ended_coroutine.insert(*yield_reg, true); + let pc: u32 = pc + .try_into() + .unwrap_or_else(|_| panic!("EndCoroutine: pc overflow: {}", pc)); state.pc = pc - 1; // yield jump is always next to yield. Here we substract 1 to go back to yield instruction } else { unreachable!(); @@ -1753,12 +1826,15 @@ impl Program { .get(yield_reg) .expect("coroutine not initialized") { - state.pc = *end_offset; + state.pc = end_offset.to_offset_int(); } else { + let pc: u32 = pc + .try_into() + .unwrap_or_else(|_| panic!("Yield: pc overflow: {}", pc)); // swap the program counter with the value in the yield register // this is the mechanism that allows jumping back and forth between the coroutine and the caller (state.pc, state.registers[*yield_reg]) = - (pc, OwnedValue::Integer(state.pc + 1)); + (pc, OwnedValue::Integer((state.pc + 1) as i64)); } } else { unreachable!( @@ -1842,7 +1918,7 @@ impl Program { if exists { state.pc += 1; } else { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } } // this cursor may be reused for next insert @@ -1894,7 +1970,7 @@ impl Program { } Insn::IsNull { src, target_pc } => { if matches!(state.registers[*src], OwnedValue::Null) { - state.pc = *target_pc; + state.pc = target_pc.to_offset_int(); } else { state.pc += 1; } @@ -1976,7 +2052,7 @@ fn trace_insn(program: &Program, addr: InsnReference, insn: &Insn) { addr, insn, String::new(), - program.comments.get(&(addr as BranchOffset)).copied() + program.comments.get(&(addr as u32)).copied() ) ); } @@ -1987,7 +2063,7 @@ fn print_insn(program: &Program, addr: InsnReference, insn: &Insn, indent: Strin addr, insn, indent, - program.comments.get(&(addr as BranchOffset)).copied(), + program.comments.get(&(addr as u32)).copied(), ); println!("{}", s); } From 737533e35f81b97da78c38e644becc851b1babff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20L=C3=B3pez?= Date: Tue, 7 Jan 2025 14:39:04 +0100 Subject: [PATCH 09/28] Prepare Cargo.toml for upcoming rewrite from macos->unix and linux->io_uring. Make io_uring an optional dependency that is only enabled with a new default feature io-uring. --- core/Cargo.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 0aab086b4..c0c579152 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,7 +14,7 @@ name = "limbo_core" path = "lib.rs" [features] -default = ["fs", "json", "uuid"] +default = ["fs", "json", "uuid", "io_uring"] fs = [] json = [ "dep:jsonb", @@ -22,11 +22,12 @@ json = [ "dep:pest_derive", ] uuid = ["dep:uuid"] +io_uring = ["dep:io-uring"] [target.'cfg(target_os = "linux")'.dependencies] -io-uring = "0.6.1" +io-uring = { version = "0.6.1", optional = true } -[target.'cfg(target_os = "macos")'.dependencies] +[target.'cfg(target_family = "unix")'.dependencies] polling = "3.7.2" rustix = "0.38.34" From e5a12bdf01dcd88ff186effbeabe306e9fff2ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20L=C3=B3pez?= Date: Tue, 7 Jan 2025 14:53:35 +0100 Subject: [PATCH 10/28] Rename linux backend to io_uring and darwin to unix. Add new feature flag to IO backend selection --- core/error.rs | 4 ++-- core/io/{linux.rs => io_uring.rs} | 38 +++++++++++++++---------------- core/io/mod.rs | 12 +++++----- core/io/{darwin.rs => unix.rs} | 20 ++++++++-------- 4 files changed, 37 insertions(+), 37 deletions(-) rename core/io/{linux.rs => io_uring.rs} (93%) rename core/io/{darwin.rs => unix.rs} (97%) diff --git a/core/error.rs b/core/error.rs index 85473ff68..8469a17a2 100644 --- a/core/error.rs +++ b/core/error.rs @@ -19,12 +19,12 @@ pub enum LimboError { EnvVarError(#[from] std::env::VarError), #[error("I/O error: {0}")] IOError(#[from] std::io::Error), - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "io_uring"))] #[error("I/O error: {0}")] LinuxIOError(String), #[error("Locking error: {0}")] LockingError(String), - #[cfg(target_os = "macos")] + #[cfg(target_family = "unix")] #[error("I/O error: {0}")] RustixIOError(#[from] rustix::io::Errno), #[error("Parse error: {0}")] diff --git a/core/io/linux.rs b/core/io/io_uring.rs similarity index 93% rename from core/io/linux.rs rename to core/io/io_uring.rs index 70de8ef79..8f3314eda 100644 --- a/core/io/linux.rs +++ b/core/io/io_uring.rs @@ -14,15 +14,15 @@ const MAX_IOVECS: usize = 128; const SQPOLL_IDLE: u32 = 1000; #[derive(Debug, Error)] -enum LinuxIOError { +enum UringIOError { IOUringCQError(i32), } // Implement the Display trait to customize error messages -impl fmt::Display for LinuxIOError { +impl fmt::Display for UringIOError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - LinuxIOError::IOUringCQError(code) => write!( + UringIOError::IOUringCQError(code) => write!( f, "IOUring completion queue error occurred with code {}", code @@ -31,8 +31,8 @@ impl fmt::Display for LinuxIOError { } } -pub struct LinuxIO { - inner: Rc>, +pub struct UringIO { + inner: Rc>, } struct WrappedIOUring { @@ -42,13 +42,13 @@ struct WrappedIOUring { key: u64, } -struct InnerLinuxIO { +struct InnerUringIO { ring: WrappedIOUring, iovecs: [iovec; MAX_IOVECS], next_iovec: usize, } -impl LinuxIO { +impl UringIO { pub fn new() -> Result { let ring = match io_uring::IoUring::builder() .setup_sqpoll(SQPOLL_IDLE) @@ -57,7 +57,7 @@ impl LinuxIO { Ok(ring) => ring, Err(_) => io_uring::IoUring::new(MAX_IOVECS as u32)?, }; - let inner = InnerLinuxIO { + let inner = InnerUringIO { ring: WrappedIOUring { ring, pending_ops: 0, @@ -76,7 +76,7 @@ impl LinuxIO { } } -impl InnerLinuxIO { +impl InnerUringIO { pub fn get_iovec(&mut self, buf: *const u8, len: usize) -> &iovec { let iovec = &mut self.iovecs[self.next_iovec]; iovec.iov_base = buf as *mut std::ffi::c_void; @@ -125,7 +125,7 @@ impl WrappedIOUring { } } -impl IO for LinuxIO { +impl IO for UringIO { fn open_file(&self, path: &str, flags: OpenFlags, direct: bool) -> Result> { trace!("open_file(path = {})", path); let file = std::fs::File::options() @@ -142,14 +142,14 @@ impl IO for LinuxIO { Err(error) => debug!("Error {error:?} returned when setting O_DIRECT flag to read file. The performance of the system may be affected"), }; } - let linux_file = Rc::new(LinuxFile { + let uring_file = Rc::new(UringFile { io: self.inner.clone(), file, }); if std::env::var(common::ENV_DISABLE_FILE_LOCK).is_err() { - linux_file.lock_file(true)?; + uring_file.lock_file(true)?; } - Ok(linux_file) + Ok(uring_file) } fn run_once(&self) -> Result<()> { @@ -167,7 +167,7 @@ impl IO for LinuxIO { if result < 0 { return Err(LimboError::LinuxIOError(format!( "{} cqe: {:?}", - LinuxIOError::IOUringCQError(result), + UringIOError::IOUringCQError(result), cqe ))); } @@ -191,12 +191,12 @@ impl IO for LinuxIO { } } -pub struct LinuxFile { - io: Rc>, +pub struct UringFile { + io: Rc>, file: std::fs::File, } -impl File for LinuxFile { +impl File for UringFile { fn lock_file(&self, exclusive: bool) -> Result<()> { let fd = self.file.as_raw_fd(); let flock = flock { @@ -306,7 +306,7 @@ impl File for LinuxFile { } } -impl Drop for LinuxFile { +impl Drop for UringFile { fn drop(&mut self) { self.unlock_file().expect("Failed to unlock file"); } @@ -319,6 +319,6 @@ mod tests { #[test] fn test_multiple_processes_cannot_open_file() { - common::tests::test_multiple_processes_cannot_open_file(LinuxIO::new); + common::tests::test_multiple_processes_cannot_open_file(UringIO::new); } } diff --git a/core/io/mod.rs b/core/io/mod.rs index 3bed97b16..7e910f4af 100644 --- a/core/io/mod.rs +++ b/core/io/mod.rs @@ -164,14 +164,14 @@ impl Buffer { } cfg_block! { - #[cfg(target_os = "linux")] { - mod linux; - pub use linux::LinuxIO as PlatformIO; + #[cfg(all(target_os = "linux", feature = "io_uring"))] { + mod io_uring; + pub use io_uring::UringIO as PlatformIO; } - #[cfg(target_os = "macos")] { - mod darwin; - pub use darwin::DarwinIO as PlatformIO; + #[cfg(any(all(target_os = "linux",not(feature = "io_uring")), target_os = "macos"))] { + mod unix; + pub use unix::UnixIO as PlatformIO; } #[cfg(target_os = "windows")] { diff --git a/core/io/darwin.rs b/core/io/unix.rs similarity index 97% rename from core/io/darwin.rs rename to core/io/unix.rs index c052b572f..c86e05ee4 100644 --- a/core/io/darwin.rs +++ b/core/io/unix.rs @@ -14,13 +14,13 @@ use std::collections::HashMap; use std::io::{Read, Seek, Write}; use std::rc::Rc; -pub struct DarwinIO { +pub struct UnixIO { poller: Rc>, events: Rc>, callbacks: Rc>>, } -impl DarwinIO { +impl UnixIO { pub fn new() -> Result { Ok(Self { poller: Rc::new(RefCell::new(Poller::new()?)), @@ -30,7 +30,7 @@ impl DarwinIO { } } -impl IO for DarwinIO { +impl IO for UnixIO { fn open_file(&self, path: &str, flags: OpenFlags, _direct: bool) -> Result> { trace!("open_file(path = {})", path); let file = std::fs::File::options() @@ -40,15 +40,15 @@ impl IO for DarwinIO { .create(matches!(flags, OpenFlags::Create)) .open(path)?; - let darwin_file = Rc::new(DarwinFile { + let unix_file = Rc::new(UnixFile { file: Rc::new(RefCell::new(file)), poller: self.poller.clone(), callbacks: self.callbacks.clone(), }); if std::env::var(common::ENV_DISABLE_FILE_LOCK).is_err() { - darwin_file.lock_file(true)?; + unix_file.lock_file(true)?; } - Ok(darwin_file) + Ok(unix_file) } fn run_once(&self) -> Result<()> { @@ -127,13 +127,13 @@ enum CompletionCallback { ), } -pub struct DarwinFile { +pub struct UnixFile { file: Rc>, poller: Rc>, callbacks: Rc>>, } -impl File for DarwinFile { +impl File for UnixFile { fn lock_file(&self, exclusive: bool) -> Result<()> { let fd = self.file.borrow().as_raw_fd(); let flock = flock { @@ -279,7 +279,7 @@ impl File for DarwinFile { } } -impl Drop for DarwinFile { +impl Drop for UnixFile { fn drop(&mut self) { self.unlock_file().expect("Failed to unlock file"); } @@ -291,6 +291,6 @@ mod tests { #[test] fn test_multiple_processes_cannot_open_file() { - common::tests::test_multiple_processes_cannot_open_file(DarwinIO::new); + common::tests::test_multiple_processes_cannot_open_file(UnixIO::new); } } From 511c0b495db0485e70af4f050d26b352e9ac1e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20L=C3=B3pez?= Date: Tue, 7 Jan 2025 15:01:44 +0100 Subject: [PATCH 11/28] Rename LinuxIOError to UringIOError to match the IO backend renames --- core/error.rs | 2 +- core/io/io_uring.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/error.rs b/core/error.rs index 8469a17a2..646e85825 100644 --- a/core/error.rs +++ b/core/error.rs @@ -21,7 +21,7 @@ pub enum LimboError { IOError(#[from] std::io::Error), #[cfg(all(target_os = "linux", feature = "io_uring"))] #[error("I/O error: {0}")] - LinuxIOError(String), + UringIOError(String), #[error("Locking error: {0}")] LockingError(String), #[cfg(target_family = "unix")] diff --git a/core/io/io_uring.rs b/core/io/io_uring.rs index 8f3314eda..54a02ee61 100644 --- a/core/io/io_uring.rs +++ b/core/io/io_uring.rs @@ -165,7 +165,7 @@ impl IO for UringIO { while let Some(cqe) = ring.get_completion() { let result = cqe.result(); if result < 0 { - return Err(LimboError::LinuxIOError(format!( + return Err(LimboError::UringIOError(format!( "{} cqe: {:?}", UringIOError::IOUringCQError(result), cqe From 345107ce2122df783a11ed5cbc9aa4ffdeda200e Mon Sep 17 00:00:00 2001 From: PThorpe92 Date: Tue, 7 Jan 2025 09:43:34 -0500 Subject: [PATCH 12/28] Fix precision issue in datetime tests --- core/vdbe/datetime.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/core/vdbe/datetime.rs b/core/vdbe/datetime.rs index 37e66ae69..ec995290c 100644 --- a/core/vdbe/datetime.rs +++ b/core/vdbe/datetime.rs @@ -1432,10 +1432,17 @@ mod tests { #[test] fn test_subsec_modifier() { - let now = Utc::now().naive_utc(); - let expected = now.format("%H:%M:%S%.3f").to_string(); + let now = Utc::now().naive_utc().time(); let result = exec_datetime(&[text("now"), text("subsec")], DateTimeOutput::Time); - assert_eq!(result, text(&expected)); + let tolerance = TimeDelta::milliseconds(1); + let result = + chrono::NaiveTime::parse_from_str(&result.to_string(), "%H:%M:%S%.3f").unwrap(); + assert!( + (now - result).num_milliseconds().abs() <= tolerance.num_milliseconds(), + "Expected: {}, Actual: {}", + now, + result + ); } #[test] @@ -1505,11 +1512,10 @@ mod tests { #[test] fn test_combined_modifiers() { let now = Utc::now().naive_utc(); - let dt = now - TimeDelta::days(1) + let expected = now - TimeDelta::days(1) + TimeDelta::hours(5) + TimeDelta::minutes(30) + TimeDelta::seconds(15); - let expected = dt.format("%Y-%m-%d %H:%M:%S%.3f").to_string(); let result = exec_datetime( &[ text("now"), @@ -1521,7 +1527,16 @@ mod tests { ], DateTimeOutput::DateTime, ); - assert_eq!(result, text(&expected)); + let tolerance = TimeDelta::milliseconds(1); + let result = + chrono::NaiveDateTime::parse_from_str(&result.to_string(), "%Y-%m-%d %H:%M:%S%.3f") + .unwrap(); + assert!( + (result - expected).num_milliseconds().abs() <= tolerance.num_milliseconds(), + "Expected: {}, Actual: {}", + expected, + result + ); } #[test] From 4a588988633c276a50ef367de7b8638531775622 Mon Sep 17 00:00:00 2001 From: Jussi Saurio Date: Wed, 8 Jan 2025 08:05:03 +0200 Subject: [PATCH 13/28] Rename eliminate_between to rewrite_exprs and add true/false->1/0 case there --- core/translate/optimizer.rs | 117 +++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 49 deletions(-) diff --git a/core/translate/optimizer.rs b/core/translate/optimizer.rs index 5b64f7b2d..a65468c89 100644 --- a/core/translate/optimizer.rs +++ b/core/translate/optimizer.rs @@ -24,7 +24,7 @@ pub fn optimize_plan(plan: &mut Plan) -> Result<()> { */ fn optimize_select_plan(plan: &mut SelectPlan) -> Result<()> { optimize_subqueries(&mut plan.source)?; - eliminate_between(&mut plan.source, &mut plan.where_clause)?; + rewrite_exprs(&mut plan.source, &mut plan.where_clause)?; if let ConstantConditionEliminationResult::ImpossibleCondition = eliminate_constants(&mut plan.source, &mut plan.where_clause)? { @@ -55,7 +55,7 @@ fn optimize_select_plan(plan: &mut SelectPlan) -> Result<()> { } fn optimize_delete_plan(plan: &mut DeletePlan) -> Result<()> { - eliminate_between(&mut plan.source, &mut plan.where_clause)?; + rewrite_exprs(&mut plan.source, &mut plan.where_clause)?; if let ConstantConditionEliminationResult::ImpossibleCondition = eliminate_constants(&mut plan.source, &mut plan.where_clause)? { @@ -603,12 +603,14 @@ fn push_scan_direction(operator: &mut SourceOperator, direction: &Direction) { } } -fn eliminate_between( +fn rewrite_exprs( operator: &mut SourceOperator, where_clauses: &mut Option>, ) -> Result<()> { if let Some(predicates) = where_clauses { - *predicates = predicates.drain(..).map(convert_between_expr).collect(); + for expr in predicates.iter_mut() { + rewrite_expr(expr)?; + } } match operator { @@ -618,24 +620,30 @@ fn eliminate_between( predicates, .. } => { - eliminate_between(left, where_clauses)?; - eliminate_between(right, where_clauses)?; + rewrite_exprs(left, where_clauses)?; + rewrite_exprs(right, where_clauses)?; if let Some(predicates) = predicates { - *predicates = predicates.drain(..).map(convert_between_expr).collect(); + for expr in predicates.iter_mut() { + rewrite_expr(expr)?; + } } } SourceOperator::Scan { predicates: Some(preds), .. } => { - *preds = preds.drain(..).map(convert_between_expr).collect(); + for expr in preds.iter_mut() { + rewrite_expr(expr)?; + } } SourceOperator::Search { predicates: Some(preds), .. } => { - *preds = preds.drain(..).map(convert_between_expr).collect(); + for expr in preds.iter_mut() { + rewrite_expr(expr)?; + } } _ => (), } @@ -747,16 +755,6 @@ impl Optimizable for ast::Expr { } fn check_constant(&self) -> Result> { match self { - Self::Id(id) => { - // true and false are special constants that are effectively aliases for 1 and 0 - if id.0.eq_ignore_ascii_case("true") { - return Ok(Some(ConstantPredicate::AlwaysTrue)); - } - if id.0.eq_ignore_ascii_case("false") { - return Ok(Some(ConstantPredicate::AlwaysFalse)); - } - Ok(None) - } Self::Literal(lit) => match lit { ast::Literal::Null => Ok(Some(ConstantPredicate::AlwaysFalse)), ast::Literal::Numeric(b) => { @@ -967,8 +965,20 @@ pub fn try_extract_index_search_expression( } } -fn convert_between_expr(expr: ast::Expr) -> ast::Expr { +fn rewrite_expr(expr: &mut ast::Expr) -> Result<()> { match expr { + ast::Expr::Id(id) => { + // Convert "true" and "false" to 1 and 0 + if id.0.eq_ignore_ascii_case("true") { + *expr = ast::Expr::Literal(ast::Literal::Numeric(1.to_string())); + return Ok(()); + } + if id.0.eq_ignore_ascii_case("false") { + *expr = ast::Expr::Literal(ast::Literal::Numeric(0.to_string())); + return Ok(()); + } + Ok(()) + } ast::Expr::Between { lhs, not, @@ -976,53 +986,62 @@ fn convert_between_expr(expr: ast::Expr) -> ast::Expr { end, } => { // Convert `y NOT BETWEEN x AND z` to `x > y OR y > z` - let (lower_op, upper_op) = if not { + let (lower_op, upper_op) = if *not { (ast::Operator::Greater, ast::Operator::Greater) } else { // Convert `y BETWEEN x AND z` to `x <= y AND y <= z` (ast::Operator::LessEquals, ast::Operator::LessEquals) }; - let lower_bound = ast::Expr::Binary(start, lower_op, lhs.clone()); - let upper_bound = ast::Expr::Binary(lhs, upper_op, end); + rewrite_expr(start)?; + rewrite_expr(lhs)?; + rewrite_expr(end)?; - if not { - ast::Expr::Binary( + let start = start.take_ownership(); + let lhs = lhs.take_ownership(); + let end = end.take_ownership(); + + let lower_bound = ast::Expr::Binary(Box::new(start), lower_op, Box::new(lhs.clone())); + let upper_bound = ast::Expr::Binary(Box::new(lhs), upper_op, Box::new(end)); + + if *not { + *expr = ast::Expr::Binary( Box::new(lower_bound), ast::Operator::Or, Box::new(upper_bound), - ) + ); } else { - ast::Expr::Binary( + *expr = ast::Expr::Binary( Box::new(lower_bound), ast::Operator::And, Box::new(upper_bound), - ) + ); } + Ok(()) } - ast::Expr::Parenthesized(mut exprs) => { - ast::Expr::Parenthesized(exprs.drain(..).map(convert_between_expr).collect()) + ast::Expr::Parenthesized(ref mut exprs) => { + for subexpr in exprs.iter_mut() { + rewrite_expr(subexpr)?; + } + let exprs = std::mem::take(exprs); + *expr = ast::Expr::Parenthesized(exprs); + Ok(()) } // Process other expressions recursively - ast::Expr::Binary(lhs, op, rhs) => ast::Expr::Binary( - Box::new(convert_between_expr(*lhs)), - op, - Box::new(convert_between_expr(*rhs)), - ), - ast::Expr::FunctionCall { - name, - distinctness, - args, - order_by, - filter_over, - } => ast::Expr::FunctionCall { - name, - distinctness, - args: args.map(|args| args.into_iter().map(convert_between_expr).collect()), - order_by, - filter_over, - }, - _ => expr, + ast::Expr::Binary(lhs, _, rhs) => { + rewrite_expr(lhs)?; + rewrite_expr(rhs)?; + Ok(()) + } + ast::Expr::FunctionCall { args, .. } => { + if let Some(args) = args { + for arg in args.iter_mut() { + rewrite_expr(arg)?; + } + } + Ok(()) + } + _ => Ok(()), } } From 925bd62cbc18c004842017b7c7445a4d17068fa0 Mon Sep 17 00:00:00 2001 From: Jussi Saurio Date: Wed, 8 Jan 2025 08:19:00 +0200 Subject: [PATCH 14/28] fix logic bug in check_index_scan() that swapped lhs/rhs but not the comparison op --- core/translate/optimizer.rs | 10 +++++++++- testing/where.test | 5 +++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/core/translate/optimizer.rs b/core/translate/optimizer.rs index a65468c89..ec657c58c 100644 --- a/core/translate/optimizer.rs +++ b/core/translate/optimizer.rs @@ -743,9 +743,17 @@ impl Optimizable for ast::Expr { rhs.check_index_scan(table_index, referenced_tables, available_indexes)?; if rhs_index.is_some() { // swap lhs and rhs + let swapped_operator = match *op { + ast::Operator::Equals => ast::Operator::Equals, + ast::Operator::Greater => ast::Operator::Less, + ast::Operator::GreaterEquals => ast::Operator::LessEquals, + ast::Operator::Less => ast::Operator::Greater, + ast::Operator::LessEquals => ast::Operator::GreaterEquals, + _ => unreachable!(), + }; let lhs_new = rhs.take_ownership(); let rhs_new = lhs.take_ownership(); - *self = Self::Binary(Box::new(lhs_new), *op, Box::new(rhs_new)); + *self = Self::Binary(Box::new(lhs_new), swapped_operator, Box::new(rhs_new)); return Ok(rhs_index); } Ok(None) diff --git a/testing/where.test b/testing/where.test index 8a568d0fc..feb4e812a 100755 --- a/testing/where.test +++ b/testing/where.test @@ -338,3 +338,8 @@ do_execsql_test between-price-range-with-names { AND (name = 'sweatshirt' OR name = 'sneakers'); } {5|sweatshirt|74.0 8|sneakers|82.0} + +do_execsql_test where-between-true-and-2 { + select id from users where id between true and 2; +} {1 +2} From b062a5f528fb5b6458bcd4a912c78f607ece76da Mon Sep 17 00:00:00 2001 From: Jussi Saurio Date: Wed, 8 Jan 2025 08:02:24 +0200 Subject: [PATCH 15/28] Fix bug with column being considered rowid alias based on 'primary_key' --- core/translate/planner.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/translate/planner.rs b/core/translate/planner.rs index b12391579..abd224d64 100644 --- a/core/translate/planner.rs +++ b/core/translate/planner.rs @@ -113,18 +113,18 @@ pub fn bind_column_references( crate::bail_parse_error!("Column {} is ambiguous", id.0); } let col = table.columns().get(col_idx.unwrap()).unwrap(); - match_result = Some((tbl_idx, col_idx.unwrap(), col.primary_key)); + match_result = Some((tbl_idx, col_idx.unwrap(), col.is_rowid_alias)); } } if match_result.is_none() { crate::bail_parse_error!("Column {} not found", id.0); } - let (tbl_idx, col_idx, is_primary_key) = match_result.unwrap(); + let (tbl_idx, col_idx, is_rowid_alias) = match_result.unwrap(); *expr = ast::Expr::Column { database: None, // TODO: support different databases table: tbl_idx, column: col_idx, - is_rowid_alias: is_primary_key, + is_rowid_alias, }; Ok(()) } From 281ba8d55236f267d5671a531392f0381c081d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?= Date: Wed, 8 Jan 2025 19:41:31 +0900 Subject: [PATCH 16/28] Remove unused java files --- .../tursodatabase/limbo/Connection.java | 67 --------------- .../github/tursodatabase/limbo/Cursor.java | 86 ------------------- .../org/github/tursodatabase/limbo/Limbo.java | 34 -------- 3 files changed, 187 deletions(-) delete mode 100644 bindings/java/src/main/java/org/github/tursodatabase/limbo/Connection.java delete mode 100644 bindings/java/src/main/java/org/github/tursodatabase/limbo/Cursor.java delete mode 100644 bindings/java/src/main/java/org/github/tursodatabase/limbo/Limbo.java diff --git a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Connection.java b/bindings/java/src/main/java/org/github/tursodatabase/limbo/Connection.java deleted file mode 100644 index aaf0f2ca2..000000000 --- a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Connection.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.github.tursodatabase.limbo; - -import java.lang.Exception; - -/** - * Represents a connection to the database. - * TODO: Deprecate classes under limbo package. We leave this source code for reference. - */ -public class Connection { - - // Pointer to the connection object - private final long connectionPtr; - - public Connection(long connectionPtr) { - this.connectionPtr = connectionPtr; - } - - /** - * Creates a new cursor object using this connection. - * - * @return A new Cursor object. - * @throws Exception If the cursor cannot be created. - */ - public Cursor cursor() throws Exception { - long cursorId = cursor(connectionPtr); - return new Cursor(cursorId); - } - - private native long cursor(long connectionPtr); - - /** - * Closes the connection to the database. - * - * @throws Exception If there is an error closing the connection. - */ - public void close() throws Exception { - close(connectionPtr); - } - - private native void close(long connectionPtr); - - /** - * Commits the current transaction. - * - * @throws Exception If there is an error during commit. - */ - public void commit() throws Exception { - try { - commit(connectionPtr); - } catch (Exception e) { - System.out.println("caught exception: " + e); - } - } - - private native void commit(long connectionPtr) throws Exception; - - /** - * Rolls back the current transaction. - * - * @throws Exception If there is an error during rollback. - */ - public void rollback() throws Exception { - rollback(connectionPtr); - } - - private native void rollback(long connectionPtr) throws Exception; -} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Cursor.java b/bindings/java/src/main/java/org/github/tursodatabase/limbo/Cursor.java deleted file mode 100644 index eaec47f04..000000000 --- a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Cursor.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.github.tursodatabase.limbo; - -/** - * Represents a database cursor. - * TODO: Deprecate classes under limbo package. We leave this source code for reference. - */ -public class Cursor { - private long cursorPtr; - - public Cursor(long cursorPtr) { - this.cursorPtr = cursorPtr; - } - - // TODO: support parameters - public Cursor execute(String sql) { - var result = execute(cursorPtr, sql); - System.out.println("resut: " + result); - return this; - } - - private static native int execute(long cursorPtr, String sql); - - public Object fetchOne() throws Exception { - Object result = fetchOne(cursorPtr); - return processSingleResult(result); - } - - private static native Object fetchOne(long cursorPtr); - - public Object fetchAll() throws Exception { - Object result = fetchAll(cursorPtr); - return processArrayResult(result); - } - - private static native Object fetchAll(long cursorPtr); - - private Object processSingleResult(Object result) throws Exception { - if (result instanceof Object[]) { - System.out.println("The result is of type: Object[]"); - for (Object element : (Object[]) result) { - printElementType(element); - } - return result; - } else { - printElementType(result); - return result; - } - } - - private Object processArrayResult(Object result) throws Exception { - if (result instanceof Object[][]) { - System.out.println("The result is of type: Object[][]"); - Object[][] array = (Object[][]) result; - for (Object[] row : array) { - for (Object element : row) { - printElementType(element); - } - } - return array; - } else { - throw new Exception("result should be of type Object[][]. Maybe internal logic has error."); - } - } - - private void printElementType(Object element) { - if (element instanceof String) { - System.out.println("String: " + element); - } else if (element instanceof Integer) { - System.out.println("Integer: " + element); - } else if (element instanceof Double) { - System.out.println("Double: " + element); - } else if (element instanceof Boolean) { - System.out.println("Boolean: " + element); - } else if (element instanceof Long) { - System.out.println("Long: " + element); - } else if (element instanceof byte[]) { - System.out.print("byte[]: "); - for (byte b : (byte[]) element) { - System.out.print(b + " "); - } - System.out.println(); - } else { - System.out.println("Unknown type: " + element); - } - } -} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Limbo.java b/bindings/java/src/main/java/org/github/tursodatabase/limbo/Limbo.java deleted file mode 100644 index 941ec4656..000000000 --- a/bindings/java/src/main/java/org/github/tursodatabase/limbo/Limbo.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.github.tursodatabase.limbo; - -import org.github.tursodatabase.exceptions.ErrorCode; - -import java.lang.Exception; - -/** - * TODO: Deprecate classes under limbo package. We leave this source code for reference. - */ -public class Limbo { - - private static volatile boolean initialized; - - private Limbo() { - if (!initialized) { - System.loadLibrary("_limbo_java"); - initialized = true; - } - } - - public static Limbo create() { - return new Limbo(); - } - - public Connection getConnection(String path) throws Exception { - long connectionId = connect(path); - if (connectionId == ErrorCode.CONNECTION_FAILURE) { - throw new Exception("Failed to initialize connection"); - } - return new Connection(connectionId); - } - - private static native long connect(String path); -} From 29e434754b01f1bd6670b76e248c4989f76853eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?= Date: Wed, 8 Jan 2025 22:50:48 +0900 Subject: [PATCH 17/28] Enhance LimboDB.java open logic --- bindings/java/Makefile | 10 +-- bindings/java/build.gradle.kts | 3 + .../github/tursodatabase/LimboErrorCode.java | 34 ++++++++ .../java/org/github/tursodatabase/Main.java | 19 ----- .../tursodatabase/NativeInvocation.java | 15 ++++ .../core/{DB.java => AbstractDB.java} | 62 ++++++++++---- .../github/tursodatabase/core/LimboDB.java | 81 +++++++++++++------ .../exceptions/LimboException.java | 18 +++++ .../org/github/tursodatabase/TestUtils.java | 13 +++ .../tursodatabase/core/LimboDBTest.java | 29 +++++++ 10 files changed, 220 insertions(+), 64 deletions(-) create mode 100644 bindings/java/src/main/java/org/github/tursodatabase/LimboErrorCode.java delete mode 100644 bindings/java/src/main/java/org/github/tursodatabase/Main.java create mode 100644 bindings/java/src/main/java/org/github/tursodatabase/NativeInvocation.java rename bindings/java/src/main/java/org/github/tursodatabase/core/{DB.java => AbstractDB.java} (73%) create mode 100644 bindings/java/src/main/java/org/github/tursodatabase/exceptions/LimboException.java create mode 100644 bindings/java/src/test/java/org/github/tursodatabase/TestUtils.java create mode 100644 bindings/java/src/test/java/org/github/tursodatabase/core/LimboDBTest.java diff --git a/bindings/java/Makefile b/bindings/java/Makefile index e91fcc923..a8e979dbc 100644 --- a/bindings/java/Makefile +++ b/bindings/java/Makefile @@ -1,7 +1,7 @@ -java_run: lib - export LIMBO_SYSTEM_PATH=../../target/debug && ./gradlew run - .PHONY: lib -lib: - cargo build +run_test: build_test + ./gradlew test + +build_test: + CARGO_TARGET_DIR=src/test/resources/limbo cargo build diff --git a/bindings/java/build.gradle.kts b/bindings/java/build.gradle.kts index 331b4831f..f1349859d 100644 --- a/bindings/java/build.gradle.kts +++ b/bindings/java/build.gradle.kts @@ -13,6 +13,7 @@ repositories { dependencies { testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") + testImplementation("org.assertj:assertj-core:3.27.0") } application { @@ -28,4 +29,6 @@ application { tasks.test { useJUnitPlatform() + // In order to find rust built file under resources, we need to set it as system path + systemProperty("java.library.path", "${System.getProperty("java.library.path")}:$projectDir/src/test/resources/limbo/debug") } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/LimboErrorCode.java b/bindings/java/src/main/java/org/github/tursodatabase/LimboErrorCode.java new file mode 100644 index 000000000..0c65ba04f --- /dev/null +++ b/bindings/java/src/main/java/org/github/tursodatabase/LimboErrorCode.java @@ -0,0 +1,34 @@ +package org.github.tursodatabase; + +public enum LimboErrorCode { + UNKNOWN_ERROR(-1, "Unknown error"), + ETC(9999, "Unclassified error"); + + public final int code; + public final String message; + + /** + * @param code Error code + * @param message Message for the error. + */ + LimboErrorCode(int code, String message) { + this.code = code; + this.message = message; + } + + public static LimboErrorCode getErrorCode(int errorCode) { + for (LimboErrorCode limboErrorCode: LimboErrorCode.values()) { + if (errorCode == limboErrorCode.code) return limboErrorCode; + } + + return UNKNOWN_ERROR; + } + + @Override + public String toString() { + return "LimboErrorCode{" + + "code=" + code + + ", message='" + message + '\'' + + '}'; + } +} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/Main.java b/bindings/java/src/main/java/org/github/tursodatabase/Main.java deleted file mode 100644 index de9b94e36..000000000 --- a/bindings/java/src/main/java/org/github/tursodatabase/Main.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.github.tursodatabase; - -import org.github.tursodatabase.limbo.Connection; -import org.github.tursodatabase.limbo.Cursor; -import org.github.tursodatabase.limbo.Limbo; - -/** - * TODO: Remove Main class. We can use test code to verify behaviors. - */ -public class Main { - public static void main(String[] args) throws Exception { - Limbo limbo = Limbo.create(); - Connection connection = limbo.getConnection("database.db"); - - Cursor cursor = connection.cursor(); - cursor.execute("SELECT * FROM example_table;"); - System.out.println("result: " + cursor.fetchOne()); - } -} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/NativeInvocation.java b/bindings/java/src/main/java/org/github/tursodatabase/NativeInvocation.java new file mode 100644 index 000000000..ee91caf53 --- /dev/null +++ b/bindings/java/src/main/java/org/github/tursodatabase/NativeInvocation.java @@ -0,0 +1,15 @@ +package org.github.tursodatabase; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to mark methods that are called by native functions. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface NativeInvocation { +} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/DB.java b/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java similarity index 73% rename from bindings/java/src/main/java/org/github/tursodatabase/core/DB.java rename to bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java index 4d82a7a92..adea4cb20 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/DB.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java @@ -1,5 +1,9 @@ package org.github.tursodatabase.core; +import org.github.tursodatabase.LimboErrorCode; +import org.github.tursodatabase.NativeInvocation; +import org.github.tursodatabase.exceptions.LimboException; + import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.concurrent.atomic.AtomicBoolean; @@ -10,18 +14,14 @@ import java.util.concurrent.atomic.AtomicBoolean; * are not only to provide functionality, but to handle contractual * differences between the JDBC specification and the Limbo API. */ -public abstract class DB { +public abstract class AbstractDB { private final String url; private final String fileName; private final AtomicBoolean closed = new AtomicBoolean(true); - public DB(String url, String fileName) throws SQLException { + public AbstractDB(String url, String filaName) throws SQLException { this.url = url; - this.fileName = fileName; - } - - public String getUrl() { - return url; + this.fileName = filaName; } public boolean isClosed() { @@ -36,7 +36,7 @@ public abstract class DB { /** * Executes an SQL statement. * - * @param sql SQL statement to be executed. + * @param sql SQL statement to be executed. * @param autoCommit Whether to auto-commit the transaction. * @throws SQLException if a database access error occurs. */ @@ -47,17 +47,16 @@ public abstract class DB { /** * Creates an SQLite interface to a database for the given connection. - * @see SQLite Open Flags * - * @param fileName The database. * @param openFlags Flags for opening the database. * @throws SQLException if a database access error occurs. */ - public final synchronized void open(String fileName, int openFlags) throws SQLException { - // TODO: add implementation - throw new SQLFeatureNotSupportedException(); + public final synchronized void open(int openFlags) throws SQLException { + _open(fileName, openFlags); } + protected abstract void _open(String fileName, int openFlags) throws SQLException; + /** * Closes a database connection and finalizes any remaining statements before the closing * operation. @@ -95,13 +94,13 @@ public abstract class DB { /** * Creates an SQLite interface to a database with the provided open flags. - * @see SQLite Open Flags * - * @param filename The database to open. + * @param fileName The database to open. * @param openFlags Flags for opening the database. + * @return pointer to database instance * @throws SQLException if a database access error occurs. */ - protected abstract void _open(String filename, int openFlags) throws SQLException; + protected abstract long _open_utf8(byte[] fileName, int openFlags) throws SQLException; /** * Closes the SQLite interface to a database. @@ -173,4 +172,35 @@ public abstract class DB { // TODO: add implementation throw new SQLFeatureNotSupportedException(); } + + /** + * Throws SQL Exception with error code. + * + * @param errorCode Error code to be passed. + * @throws SQLException Formatted SQLException with error code + */ + @NativeInvocation + private LimboException newSQLException(int errorCode, long errorMessagePointer) throws SQLException { + throw newSQLException(errorCode, getErrorMessage(errorMessagePointer)); + } + + /** + * Throws formatted SQLException with error code and message. + * + * @param errorCode Error code to be passed. + * @param errorMessage throw newSQLException(errorCode);Error message to be passed. + * @return Formatted SQLException with error code and message. + */ + public static LimboException newSQLException(int errorCode, String errorMessage) { + LimboErrorCode code = LimboErrorCode.getErrorCode(errorCode); + String msg; + if (code == LimboErrorCode.UNKNOWN_ERROR) { + msg = String.format("%s:%s (%s)", code, errorCode, errorMessage); + } else { + msg = String.format("%s (%s)", code, errorMessage); + } + return new LimboException(msg, code); + } + + protected abstract String getErrorMessage(long errorMessagePointer); } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java index 095da6910..2ed082ce6 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java @@ -1,65 +1,67 @@ package org.github.tursodatabase.core; +import org.github.tursodatabase.LimboErrorCode; + +import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; /** * This class provides a thin JNI layer over the SQLite3 C API. */ -public final class LimboDB extends DB { - /** - * SQLite connection handle. - */ - private long pointer = 0; +public final class LimboDB extends AbstractDB { + + // Pointer to database instance + private long dbPtr; + private boolean isOpen; private static boolean isLoaded; - private static boolean loadSucceeded; static { if ("The Android Project".equals(System.getProperty("java.vm.vendor"))) { - System.loadLibrary("sqlitejdbc"); - isLoaded = true; - loadSucceeded = true; + // TODO } else { // continue with non Android execution path isLoaded = false; - loadSucceeded = false; } } + // url example: "jdbc:sqlite:{fileName} + + /** + * + * @param url e.g. "jdbc:sqlite:fileName + * @param fileName e.g. path to file + */ + public static LimboDB create(String url, String fileName) throws SQLException { + return new LimboDB(url, fileName); + } + // TODO: receive config as argument - public LimboDB(String url, String fileName) throws SQLException { + private LimboDB(String url, String fileName) throws SQLException { super(url, fileName); } /** * Loads the SQLite interface backend. - * - * @return True if the SQLite JDBC driver is successfully loaded; false otherwise. */ - public static boolean load() throws Exception { - if (isLoaded) return loadSucceeded; + public void load() { + if (isLoaded) return; try { System.loadLibrary("_limbo_java"); - loadSucceeded = true; + } finally { isLoaded = true; } - return loadSucceeded; } // WRAPPER FUNCTIONS //////////////////////////////////////////// - @Override - protected synchronized void _open(String file, int openFlags) throws SQLException { - // TODO: add implementation - throw new SQLFeatureNotSupportedException(); - } - // TODO: add support for JNI - synchronized native void _open_utf8(byte[] fileUtf8, int openFlags) throws SQLException; + @Override + protected synchronized native long _open_utf8(byte[] file, int openFlags) throws SQLException; // TODO: add support for JNI @Override @@ -78,6 +80,15 @@ public final class LimboDB extends DB { @Override public native void interrupt(); + @Override + protected void _open(String fileName, int openFlags) throws SQLException { + if (isOpen) { + throw newSQLException(LimboErrorCode.UNKNOWN_ERROR.code, "Already opened"); + } + dbPtr = _open_utf8(stringToUtf8ByteArray(fileName), openFlags); + isOpen = true; + } + @Override protected synchronized SafeStmtPtr prepare(String sql) throws SQLException { // TODO: add implementation @@ -91,4 +102,26 @@ public final class LimboDB extends DB { // TODO: add support for JNI @Override public synchronized native int step(long stmt); + + @Override + protected String getErrorMessage(long errorMessagePointer) { + return utf8ByteBufferToString(getErrorMessageUtf8(errorMessagePointer)); + } + + private native byte[] getErrorMessageUtf8(long errorMessagePointer); + + private static String utf8ByteBufferToString(byte[] buffer) { + if (buffer == null) { + return null; + } + + return new String(buffer, StandardCharsets.UTF_8); + } + + private static byte[] stringToUtf8ByteArray(String str) { + if (str == null) { + return null; + } + return str.getBytes(StandardCharsets.UTF_8); + } } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/exceptions/LimboException.java b/bindings/java/src/main/java/org/github/tursodatabase/exceptions/LimboException.java new file mode 100644 index 000000000..d4526a818 --- /dev/null +++ b/bindings/java/src/main/java/org/github/tursodatabase/exceptions/LimboException.java @@ -0,0 +1,18 @@ +package org.github.tursodatabase.exceptions; + +import org.github.tursodatabase.LimboErrorCode; + +import java.sql.SQLException; + +public class LimboException extends SQLException { + private final LimboErrorCode resultCode; + + public LimboException(String message, LimboErrorCode resultCode) { + super(message, null, resultCode.code & 0xff); + this.resultCode = resultCode; + } + + public LimboErrorCode getResultCode() { + return resultCode; + } +} diff --git a/bindings/java/src/test/java/org/github/tursodatabase/TestUtils.java b/bindings/java/src/test/java/org/github/tursodatabase/TestUtils.java new file mode 100644 index 000000000..0d7e64488 --- /dev/null +++ b/bindings/java/src/test/java/org/github/tursodatabase/TestUtils.java @@ -0,0 +1,13 @@ +package org.github.tursodatabase; + +import java.io.IOException; +import java.nio.file.Files; + +public class TestUtils { + /** + * Create temporary file and returns the path. + */ + public static String createTempFile() throws IOException { + return Files.createTempFile("limbo_test_db", null).toAbsolutePath().toString(); + } +} diff --git a/bindings/java/src/test/java/org/github/tursodatabase/core/LimboDBTest.java b/bindings/java/src/test/java/org/github/tursodatabase/core/LimboDBTest.java new file mode 100644 index 000000000..8a62ea083 --- /dev/null +++ b/bindings/java/src/test/java/org/github/tursodatabase/core/LimboDBTest.java @@ -0,0 +1,29 @@ +package org.github.tursodatabase.core; + +import org.github.tursodatabase.TestUtils; +import org.junit.jupiter.api.Test; + +import java.sql.SQLException; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class LimboDBTest { + + @Test + void db_should_open_normally() throws Exception { + String dbPath = TestUtils.createTempFile(); + LimboDB db = LimboDB.create("jdbc:sqlite" + dbPath, dbPath); + db.load(); + db.open(0); + } + + @Test + void should_throw_exception_when_opened_twice() throws Exception { + String dbPath = TestUtils.createTempFile(); + LimboDB db = LimboDB.create("jdbc:sqlite:" + dbPath, dbPath); + db.load(); + db.open(0); + + assertThatThrownBy(() -> db.open(0)).isInstanceOf(SQLException.class); + } +} From 9e0e3dc81abcb729e6caab223d00cca29824b627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?= Date: Wed, 8 Jan 2025 22:51:33 +0900 Subject: [PATCH 18/28] Update rust side logic to open database --- bindings/java/rs_src/errors.rs | 8 +-- bindings/java/rs_src/lib.rs | 62 +---------------------- bindings/java/rs_src/limbo_db.rs | 85 ++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 64 deletions(-) create mode 100644 bindings/java/rs_src/limbo_db.rs diff --git a/bindings/java/rs_src/errors.rs b/bindings/java/rs_src/errors.rs index 490fdfbd9..ffa26a6b8 100644 --- a/bindings/java/rs_src/errors.rs +++ b/bindings/java/rs_src/errors.rs @@ -6,7 +6,7 @@ pub struct CustomError { } /// This struct defines error codes that correspond to the constants defined in the -/// Java package `org.github.tursodatabase.exceptions.ErrorCode`. +/// Java package `org.github.tursodatabase.LimboErrorCode`. /// /// These error codes are used to handle and represent specific error conditions /// that may occur within the Rust code and need to be communicated to the Java side. @@ -14,9 +14,11 @@ pub struct CustomError { pub struct ErrorCode; impl ErrorCode { - pub const CONNECTION_FAILURE: i32 = -1; - + // TODO: change CONNECTION_FAILURE_STATEMENT_IS_DML to appropriate error code number pub const STATEMENT_IS_DML: i32 = -1; + + pub const UNKNOWN_ERROR: i32 = -1; + pub const ERROR_CODE_ETC: i32 = 9999; } impl From for CustomError { diff --git a/bindings/java/rs_src/lib.rs b/bindings/java/rs_src/lib.rs index 4ba9ba2c4..4dfadd743 100644 --- a/bindings/java/rs_src/lib.rs +++ b/bindings/java/rs_src/lib.rs @@ -1,66 +1,6 @@ mod connection; mod cursor; mod errors; +mod limbo_db; mod macros; mod utils; - -use crate::connection::Connection; -use crate::errors::ErrorCode; -use jni::errors::JniError; -use jni::objects::{JClass, JString}; -use jni::sys::jlong; -use jni::JNIEnv; -use std::sync::{Arc, Mutex}; - -/// Establishes a connection to the database specified by the given path. -/// -/// This function is called from the Java side to create a connection to the database. -/// It returns a pointer to the `Connection` object, which can be used in subsequent -/// native function calls. -/// -/// # Arguments -/// -/// * `env` - The JNI environment pointer. -/// * `_class` - The Java class calling this function. -/// * `path` - A `JString` representing the path to the database file. -/// -/// # Returns -/// -/// A `jlong` representing the pointer to the newly created `Connection` object, -/// or [ErrorCode::CONNECTION_FAILURE] if the connection could not be established. -#[no_mangle] -pub extern "system" fn Java_org_github_tursodatabase_limbo_Limbo_connect<'local>( - mut env: JNIEnv<'local>, - _class: JClass<'local>, - path: JString<'local>, -) -> jlong { - connect_internal(&mut env, path).unwrap_or_else(|_| ErrorCode::CONNECTION_FAILURE as jlong) -} - -#[allow(improper_ctypes_definitions, clippy::arc_with_non_send_sync)] // TODO: remove -fn connect_internal<'local>( - env: &mut JNIEnv<'local>, - path: JString<'local>, -) -> Result { - let io = Arc::new(limbo_core::PlatformIO::new().map_err(|e| { - println!("IO initialization failed: {:?}", e); - JniError::Unknown - })?); - - let path: String = env - .get_string(&path) - .expect("Failed to convert JString to Rust String") - .into(); - let db = limbo_core::Database::open_file(io.clone(), &path).map_err(|e| { - println!("Failed to open database: {:?}", e); - JniError::Unknown - })?; - - let conn = db.connect().clone(); - let connection = Connection { - conn: Arc::new(Mutex::new(conn)), - io, - }; - - Ok(Box::into_raw(Box::new(connection)) as jlong) -} diff --git a/bindings/java/rs_src/limbo_db.rs b/bindings/java/rs_src/limbo_db.rs new file mode 100644 index 000000000..46772bc51 --- /dev/null +++ b/bindings/java/rs_src/limbo_db.rs @@ -0,0 +1,85 @@ +use jni::objects::{JByteArray, JObject}; +use jni::sys::{jint, jlong}; +use jni::JNIEnv; +use limbo_core::Database; +use std::sync::Arc; + +const ERROR_CODE_ETC: i32 = 9999; + +#[no_mangle] +pub extern "system" fn Java_org_github_tursodatabase_core_LimboDB__1open_1utf8<'local>( + mut env: JNIEnv<'local>, + obj: JObject<'local>, + file_name_byte_arr: JByteArray<'local>, + _open_flags: jint, +) -> jlong { + let io = match limbo_core::PlatformIO::new() { + Ok(io) => Arc::new(io), + Err(e) => { + set_err_msg_and_throw_exception(&mut env, obj, ERROR_CODE_ETC, e.to_string()); + return -1; + } + }; + + let path = match env + .convert_byte_array(file_name_byte_arr) + .map_err(|e| e.to_string()) + { + Ok(bytes) => match String::from_utf8(bytes) { + Ok(s) => s, + Err(e) => { + set_err_msg_and_throw_exception(&mut env, obj, ERROR_CODE_ETC, e.to_string()); + return -1; + } + }, + Err(e) => { + set_err_msg_and_throw_exception(&mut env, obj, ERROR_CODE_ETC, e.to_string()); + return -1; + } + }; + + let db = match Database::open_file(io.clone(), &path) { + Ok(db) => db, + Err(e) => { + set_err_msg_and_throw_exception(&mut env, obj, ERROR_CODE_ETC, e.to_string()); + return -1; + } + }; + + Box::into_raw(Box::new(db)) as jlong +} + +fn set_err_msg_and_throw_exception<'local>( + env: &mut JNIEnv<'local>, + obj: JObject<'local>, + err_code: i32, + err_msg: String, +) { + let error_message_pointer = Box::into_raw(Box::new(err_msg)) as i64; + match env.call_method( + obj, + "newSQLException", + "(IJ)Lorg/github/tursodatabase/exceptions/LimboException;", + &[err_code.into(), error_message_pointer.into()], + ) { + Ok(_) => { + // do nothing because above method will always return Err + } + Err(_e) => { + // do nothing because our java app will handle Err + } + } +} + +#[no_mangle] +pub unsafe extern "system" fn Java_org_github_tursodatabase_core_LimboDB_getErrorMessageUtf8< + 'local, +>( + env: JNIEnv<'local>, + _obj: JObject<'local>, + error_message_ptr: jlong, +) -> JByteArray<'local> { + let error_message = Box::from_raw(error_message_ptr as *mut String); + let error_message_bytes = error_message.as_bytes(); + env.byte_array_from_slice(error_message_bytes).unwrap() +} From b3762b3e925249c5e5f3b7cd7a1b40f916b0bea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?= Date: Wed, 8 Jan 2025 23:01:24 +0900 Subject: [PATCH 19/28] Fix clippy --- bindings/java/rs_src/errors.rs | 3 --- bindings/java/rs_src/limbo_db.rs | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/bindings/java/rs_src/errors.rs b/bindings/java/rs_src/errors.rs index ffa26a6b8..7924ffb68 100644 --- a/bindings/java/rs_src/errors.rs +++ b/bindings/java/rs_src/errors.rs @@ -16,9 +16,6 @@ pub struct ErrorCode; impl ErrorCode { // TODO: change CONNECTION_FAILURE_STATEMENT_IS_DML to appropriate error code number pub const STATEMENT_IS_DML: i32 = -1; - - pub const UNKNOWN_ERROR: i32 = -1; - pub const ERROR_CODE_ETC: i32 = 9999; } impl From for CustomError { diff --git a/bindings/java/rs_src/limbo_db.rs b/bindings/java/rs_src/limbo_db.rs index 46772bc51..4357b7a8a 100644 --- a/bindings/java/rs_src/limbo_db.rs +++ b/bindings/java/rs_src/limbo_db.rs @@ -7,6 +7,7 @@ use std::sync::Arc; const ERROR_CODE_ETC: i32 = 9999; #[no_mangle] +#[allow(clippy::arc_with_non_send_sync)] pub extern "system" fn Java_org_github_tursodatabase_core_LimboDB__1open_1utf8<'local>( mut env: JNIEnv<'local>, obj: JObject<'local>, From 6802bb7e6a214d774c8a45c93869e2d9813993b1 Mon Sep 17 00:00:00 2001 From: Pere Diaz Bou Date: Wed, 8 Jan 2025 19:05:49 +0100 Subject: [PATCH 20/28] distinguish balance and balance_non_root `balance_non_root` should be as close as possible to `balance_non_root` in SQLite. This commits extract `balance_non_root` from `balance` and renames `balance_leaf` to `balance` as it enables future work on a complete `balance_non_root` procedure. --- core/storage/btree.rs | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/core/storage/btree.rs b/core/storage/btree.rs index 481866ee7..c84a0c2c0 100644 --- a/core/storage/btree.rs +++ b/core/storage/btree.rs @@ -78,6 +78,7 @@ macro_rules! return_if_locked { enum WriteState { Start, BalanceStart, + BalanceNonRoot, BalanceGetParentPage, BalanceMoveUp, Finish, @@ -730,9 +731,10 @@ impl BTreeCursor { } } WriteState::BalanceStart + | WriteState::BalanceNonRoot | WriteState::BalanceMoveUp | WriteState::BalanceGetParentPage => { - return_if_io!(self.balance_leaf()); + return_if_io!(self.balance()); } WriteState::Finish => { self.write_info.state = WriteState::Start; @@ -882,7 +884,7 @@ impl BTreeCursor { /// This is a naive algorithm that doesn't try to distribute cells evenly by content. /// It will try to split the page in half by keys not by content. /// Sqlite tries to have a page at least 40% full. - fn balance_leaf(&mut self) -> Result> { + fn balance(&mut self) -> Result> { let state = &self.write_info.state; match state { WriteState::BalanceStart => { @@ -906,7 +908,31 @@ impl BTreeCursor { self.balance_root(); return Ok(CursorResult::Ok(())); } - debug!("Balancing leaf. leaf={}", current_page.get().id); + + self.write_info.state = WriteState::BalanceNonRoot; + self.balance_non_root() + } + WriteState::BalanceNonRoot + | WriteState::BalanceGetParentPage + | WriteState::BalanceMoveUp => self.balance_non_root(), + + _ => unreachable!("invalid balance leaf state {:?}", state), + } + } + + fn balance_non_root(&mut self) -> Result> { + let state = &self.write_info.state; + match state { + WriteState::Start => todo!(), + WriteState::BalanceStart => todo!(), + WriteState::BalanceNonRoot => { + // drop divider cells and find right pointer + // NOTE: since we are doing a simple split we only finding the pointer we want to update (right pointer). + // Right pointer means cell that points to the last page, as we don't really want to drop this one. This one + // can be a "rightmost pointer" or a "cell". + // we always asumme there is a parent + let current_page = self.stack.top(); + debug!("balance_non_root(page={})", current_page.get().id); // Copy of page used to reference cell bytes. // This needs to be saved somewhere safe so taht references still point to here, @@ -1186,8 +1212,7 @@ impl BTreeCursor { let _ = self.write_info.page_copy.take(); Ok(CursorResult::Ok(())) } - - _ => unreachable!("invalid balance leaf state {:?}", state), + WriteState::Finish => todo!(), } } From fa0e7d572925f1be6705f4cae7d94682958e9d99 Mon Sep 17 00:00:00 2001 From: PThorpe92 Date: Wed, 8 Jan 2025 09:47:16 -0500 Subject: [PATCH 21/28] Support nested parenthesized conditional exprs in translator --- core/translate/expr.rs | 83 +++++++++++++++++++++++++++---------- core/translate/group_by.rs | 1 + core/translate/main_loop.rs | 4 ++ 3 files changed, 66 insertions(+), 22 deletions(-) diff --git a/core/translate/expr.rs b/core/translate/expr.rs index 4c4ee72b2..6efbe8d89 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -18,6 +18,7 @@ pub struct ConditionMetadata { pub jump_if_condition_is_true: bool, pub jump_target_when_true: BranchOffset, pub jump_target_when_false: BranchOffset, + pub parent_op: Option, } pub fn translate_condition_expr( @@ -30,48 +31,87 @@ pub fn translate_condition_expr( match expr { ast::Expr::Between { .. } => todo!(), ast::Expr::Binary(lhs, ast::Operator::And, rhs) => { - // In a binary AND, never jump to the 'jump_target_when_true' label on the first condition, because - // the second condition must also be true. + // We’re in an AND, so short-circuit on false: let _ = translate_condition_expr( program, referenced_tables, lhs, ConditionMetadata { jump_if_condition_is_true: false, + // Mark that the parent op for sub-expressions is AND + parent_op: Some(ast::Operator::And), ..condition_metadata }, resolver, ); + // Then evaluate RHS with the parent's metadata (still AND) let _ = translate_condition_expr( program, referenced_tables, rhs, - condition_metadata, + ConditionMetadata { + parent_op: Some(ast::Operator::And), + ..condition_metadata + }, resolver, ); } ast::Expr::Binary(lhs, ast::Operator::Or, rhs) => { - let jump_target_when_false = program.allocate_label(); - let _ = translate_condition_expr( - program, - referenced_tables, - lhs, - ConditionMetadata { - // If the first condition is true, we don't need to evaluate the second condition. + if matches!(condition_metadata.parent_op, Some(ast::Operator::And)) { + // we are inside a bigger AND expression, so we do NOT jump to parent's 'true' if LHS or RHS is true. + // we only short-circuit the parent's false label if LHS and RHS are both false. + let local_true_label = program.allocate_label(); + let local_false_label = program.allocate_label(); + + // evaluate LHS in normal OR fashion, short-circuit if true + let lhs_metadata = ConditionMetadata { + jump_if_condition_is_true: true, + jump_target_when_true: local_true_label, + jump_target_when_false: local_false_label, + parent_op: Some(ast::Operator::Or), + }; + translate_condition_expr(program, referenced_tables, lhs, lhs_metadata, resolver)?; + + // if lhs was false, we land here: + program.resolve_label(local_false_label, program.offset()); + + // evaluate rhs with normal OR: short-circuit if true, go to local_true + let rhs_metadata = ConditionMetadata { + jump_if_condition_is_true: true, + jump_target_when_true: local_true_label, + jump_target_when_false: condition_metadata.jump_target_when_false, + // if rhs is also false => parent's false + parent_op: Some(ast::Operator::Or), + }; + translate_condition_expr(program, referenced_tables, rhs, rhs_metadata, resolver)?; + + // if we get here, both lhs+rhs are false: explicit jump to parent's false + program.emit_insn(Insn::Goto { + target_pc: condition_metadata.jump_target_when_false, + }); + // local_true means (C OR D) is true => we do *not* jump to parent's "true" label, + // because the parent is an AND, so we want to keep evaluating the rest: + program.resolve_label(local_true_label, program.offset()); + } else { + let jump_target_when_false = program.allocate_label(); + + let lhs_metadata = ConditionMetadata { jump_if_condition_is_true: true, jump_target_when_false, + parent_op: Some(ast::Operator::Or), ..condition_metadata - }, - resolver, - ); - program.resolve_label(jump_target_when_false, program.offset()); - let _ = translate_condition_expr( - program, - referenced_tables, - rhs, - condition_metadata, - resolver, - ); + }; + + translate_condition_expr(program, referenced_tables, lhs, lhs_metadata, resolver)?; + + // if LHS was false, we land here: + program.resolve_label(jump_target_when_false, program.offset()); + let rhs_metadata = ConditionMetadata { + parent_op: Some(ast::Operator::Or), + ..condition_metadata + }; + translate_condition_expr(program, referenced_tables, rhs, rhs_metadata, resolver)?; + } } ast::Expr::Binary(lhs, op, rhs) => { let lhs_reg = program.alloc_register(); @@ -1917,7 +1957,6 @@ fn translate_variable_sized_function_parameter_list( for arg in args.iter() { translate_expr(program, referenced_tables, arg, current_reg, resolver)?; - current_reg += 1; } diff --git a/core/translate/group_by.rs b/core/translate/group_by.rs index 0990bee0d..c20466f27 100644 --- a/core/translate/group_by.rs +++ b/core/translate/group_by.rs @@ -382,6 +382,7 @@ pub fn emit_group_by<'a>( jump_if_condition_is_true: false, jump_target_when_false: group_by_end_without_emitting_row_label, jump_target_when_true: BranchOffset::Placeholder, // not used. FIXME: this is a bug. HAVING can have e.g. HAVING a OR b. + parent_op: None, }, &t_ctx.resolver, )?; diff --git a/core/translate/main_loop.rs b/core/translate/main_loop.rs index 05daf01e5..e95b3dc22 100644 --- a/core/translate/main_loop.rs +++ b/core/translate/main_loop.rs @@ -225,6 +225,7 @@ pub fn open_loop( jump_if_condition_is_true: false, jump_target_when_true, jump_target_when_false: next, + parent_op: None, }; translate_condition_expr( program, @@ -273,6 +274,7 @@ pub fn open_loop( jump_if_condition_is_true: false, jump_target_when_true, jump_target_when_false, + parent_op: None, }; for predicate in predicates.iter() { translate_condition_expr( @@ -345,6 +347,7 @@ pub fn open_loop( jump_if_condition_is_true: false, jump_target_when_true, jump_target_when_false: next, + parent_op: None, }; translate_condition_expr( program, @@ -529,6 +532,7 @@ pub fn open_loop( jump_if_condition_is_true: false, jump_target_when_true, jump_target_when_false: next, + parent_op: None, }; translate_condition_expr( program, From 183797898b9f91cffaf062e3e650212142e3281e Mon Sep 17 00:00:00 2001 From: PThorpe92 Date: Wed, 8 Jan 2025 12:04:17 -0500 Subject: [PATCH 22/28] Add tests for nested conditional expressions --- core/translate/expr.rs | 4 ++-- testing/where.test | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/core/translate/expr.rs b/core/translate/expr.rs index 6efbe8d89..ec2205e7d 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -31,7 +31,8 @@ pub fn translate_condition_expr( match expr { ast::Expr::Between { .. } => todo!(), ast::Expr::Binary(lhs, ast::Operator::And, rhs) => { - // We’re in an AND, so short-circuit on false: + // In a binary AND, never jump to the 'jump_target_when_true' label on the first condition, because + // the second condition must also be true. let _ = translate_condition_expr( program, referenced_tables, @@ -44,7 +45,6 @@ pub fn translate_condition_expr( }, resolver, ); - // Then evaluate RHS with the parent's metadata (still AND) let _ = translate_condition_expr( program, referenced_tables, diff --git a/testing/where.test b/testing/where.test index feb4e812a..c613f784b 100755 --- a/testing/where.test +++ b/testing/where.test @@ -343,3 +343,25 @@ do_execsql_test where-between-true-and-2 { select id from users where id between true and 2; } {1 2} + +do_execsql_test nested-parens-conditionals-or-and-or { + SELECT count(*) FROM users WHERE ((age > 25 OR age < 18) AND (city = 'Boston' OR state = 'MA')); +} {146} + +do_execsql_test nested-parens-conditionals-and-or-and { + SELECT * FROM users WHERE (((age > 18 AND city = 'New Mario') OR age = 92) AND city = 'Lake Paul'); +} {{9989|Timothy|Harrison|woodsmichael@example.net|+1-447-830-5123|782 Wright Harbors|Lake Paul|ID|52330|92}} + + +do_execsql_test nested-parens-conditionals-and-double-or { + SELECT * FROM users WHERE ((age > 30 OR age < 20) AND (state = 'NY' OR state = 'CA')) AND first_name glob 'An*' order by id; +} {{1738|Angelica|Pena|jacksonjonathan@example.net|(867)536-1578x039|663 Jacqueline Estate Apt. 652|Clairehaven|NY|64172|74 +1811|Andrew|Mckee|jchen@example.net|359.939.9548|19809 Blair Junction Apt. 438|New Lawrencefort|NY|26240|42 +3773|Andrew|Peterson|cscott@example.com|(405)410-4972x90408|90513 Munoz Radial Apt. 786|Travisfurt|CA|52951|43 +3875|Anthony|Cordova|ocross@example.org|+1-356-999-4070x557|77081 Aguilar Turnpike|Michaelfurt|CA|73353|37 +4909|Andrew|Carson|michelle31@example.net|823.423.1516|78514 Luke Springs|Lake Crystal|CA|49481|74 +5498|Anna|Hall|elizabethheath@example.org|9778473725|5803 Taylor Tunnel|New Nicholaston|NY|21825|14 +6340|Angela|Freeman|juankelly@example.net|501.372.4720|3912 Ricardo Mission|West Nancyville|NY|60823|34 +8171|Andrea|Lee|dgarrison@example.com|001-594-430-0646|452 Anthony Stravenue|Sandraville|CA|28572|12 +9110|Anthony|Barrett|steven05@example.net|(562)928-9177x8454|86166 Foster Inlet Apt. 284|North Jeffreyburgh|CA|80147|97 +9279|Annette|Lynn|joanne37@example.com|(272)700-7181|2676 Laura Points Apt. 683|Tristanville|NY|48646|91}} From 97d0fc68b2908f2064643b9b5326d6a1b2548597 Mon Sep 17 00:00:00 2001 From: PThorpe92 Date: Wed, 8 Jan 2025 16:54:41 -0500 Subject: [PATCH 23/28] Add macro and helper to clean up expression translations --- core/translate/expr.rs | 324 ++++++++++++++--------------------------- 1 file changed, 107 insertions(+), 217 deletions(-) diff --git a/core/translate/expr.rs b/core/translate/expr.rs index ec2205e7d..aef2fe30d 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -21,6 +21,46 @@ pub struct ConditionMetadata { pub parent_op: Option, } +fn emit_cond_jump(program: &mut ProgramBuilder, cond_meta: ConditionMetadata, reg: usize) { + if cond_meta.jump_if_condition_is_true { + program.emit_insn(Insn::If { + reg, + target_pc: cond_meta.jump_target_when_true, + null_reg: reg, + }); + } else { + program.emit_insn(Insn::IfNot { + reg, + target_pc: cond_meta.jump_target_when_false, + null_reg: reg, + }); + } +} +macro_rules! emit_cmp_insn { + ( + $program:expr, + $cond:expr, + $op_true:ident, + $op_false:ident, + $lhs:expr, + $rhs:expr + ) => {{ + if $cond.jump_if_condition_is_true { + $program.emit_insn(Insn::$op_true { + lhs: $lhs, + rhs: $rhs, + target_pc: $cond.jump_target_when_true, + }); + } else { + $program.emit_insn(Insn::$op_false { + lhs: $lhs, + rhs: $rhs, + target_pc: $cond.jump_target_when_false, + }); + } + }}; +} + pub fn translate_condition_expr( program: &mut ProgramBuilder, referenced_tables: &[TableReference], @@ -63,7 +103,7 @@ pub fn translate_condition_expr( let local_true_label = program.allocate_label(); let local_false_label = program.allocate_label(); - // evaluate LHS in normal OR fashion, short-circuit if true + // evaluate LHS in normal OR fashion, short-circuit local if true let lhs_metadata = ConditionMetadata { jump_if_condition_is_true: true, jump_target_when_true: local_true_label, @@ -89,8 +129,8 @@ pub fn translate_condition_expr( program.emit_insn(Insn::Goto { target_pc: condition_metadata.jump_target_when_false, }); - // local_true means (C OR D) is true => we do *not* jump to parent's "true" label, - // because the parent is an AND, so we want to keep evaluating the rest: + // local_true: we do not jump to parent's "true" label because the parent is AND, + // so we want to keep evaluating the rest program.resolve_label(local_true_label, program.offset()); } else { let jump_target_when_false = program.allocate_label(); @@ -114,106 +154,26 @@ pub fn translate_condition_expr( } } ast::Expr::Binary(lhs, op, rhs) => { - let lhs_reg = program.alloc_register(); - let _ = translate_expr(program, Some(referenced_tables), lhs, lhs_reg, resolver); - if let ast::Expr::Literal(_) = lhs.as_ref() { - program.mark_last_insn_constant() - } - let rhs_reg = program.alloc_register(); - let _ = translate_expr(program, Some(referenced_tables), rhs, rhs_reg, resolver); - if let ast::Expr::Literal(_) = rhs.as_ref() { - program.mark_last_insn_constant() - } + let lhs_reg = translate_and_mark(program, Some(referenced_tables), lhs, resolver)?; + let rhs_reg = translate_and_mark(program, Some(referenced_tables), rhs, resolver)?; match op { ast::Operator::Greater => { - if condition_metadata.jump_if_condition_is_true { - program.emit_insn(Insn::Gt { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }) - } else { - program.emit_insn(Insn::Le { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }) - } + emit_cmp_insn!(program, condition_metadata, Gt, Le, lhs_reg, rhs_reg) } ast::Operator::GreaterEquals => { - if condition_metadata.jump_if_condition_is_true { - program.emit_insn(Insn::Ge { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }) - } else { - program.emit_insn(Insn::Lt { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }) - } + emit_cmp_insn!(program, condition_metadata, Ge, Lt, lhs_reg, rhs_reg) } ast::Operator::Less => { - if condition_metadata.jump_if_condition_is_true { - program.emit_insn(Insn::Lt { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }) - } else { - program.emit_insn(Insn::Ge { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }) - } + emit_cmp_insn!(program, condition_metadata, Lt, Ge, lhs_reg, rhs_reg) } ast::Operator::LessEquals => { - if condition_metadata.jump_if_condition_is_true { - program.emit_insn(Insn::Le { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }) - } else { - program.emit_insn(Insn::Gt { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }) - } + emit_cmp_insn!(program, condition_metadata, Le, Gt, lhs_reg, rhs_reg) } ast::Operator::Equals => { - if condition_metadata.jump_if_condition_is_true { - program.emit_insn(Insn::Eq { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }) - } else { - program.emit_insn(Insn::Ne { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }) - } + emit_cmp_insn!(program, condition_metadata, Eq, Ne, lhs_reg, rhs_reg) } ast::Operator::NotEquals => { - if condition_metadata.jump_if_condition_is_true { - program.emit_insn(Insn::Ne { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_true, - }) - } else { - program.emit_insn(Insn::Eq { - lhs: lhs_reg, - rhs: rhs_reg, - target_pc: condition_metadata.jump_target_when_false, - }) - } + emit_cmp_insn!(program, condition_metadata, Ne, Eq, lhs_reg, rhs_reg) } ast::Operator::Is => todo!(), ast::Operator::IsNot => todo!(), @@ -231,19 +191,7 @@ pub fn translate_condition_expr( value: int_value, dest: reg, }); - if condition_metadata.jump_if_condition_is_true { - program.emit_insn(Insn::If { - reg, - target_pc: condition_metadata.jump_target_when_true, - null_reg: reg, - }) - } else { - program.emit_insn(Insn::IfNot { - reg, - target_pc: condition_metadata.jump_target_when_false, - null_reg: reg, - }) - } + emit_cond_jump(program, condition_metadata, reg); } else { crate::bail_parse_error!("unsupported literal type in condition"); } @@ -254,19 +202,7 @@ pub fn translate_condition_expr( value: string.clone(), dest: reg, }); - if condition_metadata.jump_if_condition_is_true { - program.emit_insn(Insn::If { - reg, - target_pc: condition_metadata.jump_target_when_true, - null_reg: reg, - }) - } else { - program.emit_insn(Insn::IfNot { - reg, - target_pc: condition_metadata.jump_target_when_false, - null_reg: reg, - }) - } + emit_cond_jump(program, condition_metadata, reg); } unimpl => todo!("literal {:?} not implemented", unimpl), }, @@ -392,18 +328,8 @@ pub fn translate_condition_expr( match op { ast::LikeOperator::Like | ast::LikeOperator::Glob => { let pattern_reg = program.alloc_register(); - let column_reg = program.alloc_register(); let mut constant_mask = 0; - let _ = translate_expr( - program, - Some(referenced_tables), - lhs, - column_reg, - resolver, - )?; - if let ast::Expr::Literal(_) = lhs.as_ref() { - program.mark_last_insn_constant(); - } + let _ = translate_and_mark(program, Some(referenced_tables), lhs, resolver); let _ = translate_expr( program, Some(referenced_tables), @@ -411,7 +337,7 @@ pub fn translate_condition_expr( pattern_reg, resolver, )?; - if let ast::Expr::Literal(_) = rhs.as_ref() { + if matches!(rhs.as_ref(), ast::Expr::Literal(_)) { program.mark_last_insn_constant(); constant_mask = 1; } @@ -434,19 +360,7 @@ pub fn translate_condition_expr( ast::LikeOperator::Regexp => todo!(), } if !*not { - if condition_metadata.jump_if_condition_is_true { - program.emit_insn(Insn::If { - reg: cur_reg, - target_pc: condition_metadata.jump_target_when_true, - null_reg: cur_reg, - }); - } else { - program.emit_insn(Insn::IfNot { - reg: cur_reg, - target_pc: condition_metadata.jump_target_when_false, - null_reg: cur_reg, - }); - } + emit_cond_jump(program, condition_metadata, cur_reg); } else if condition_metadata.jump_if_condition_is_true { program.emit_insn(Insn::IfNot { reg: cur_reg, @@ -462,16 +376,18 @@ pub fn translate_condition_expr( } } ast::Expr::Parenthesized(exprs) => { - // TODO: this is probably not correct; multiple expressions in a parenthesized expression - // are reserved for special cases like `(a, b) IN ((1, 2), (3, 4))`. - for expr in exprs { + if exprs.len() == 1 { let _ = translate_condition_expr( program, referenced_tables, - expr, + &exprs[0], condition_metadata, resolver, ); + } else { + crate::bail_parse_error!( + "parenthesized condtional should have exactly one expression" + ); } } _ => todo!("op {:?} not implemented", expr), @@ -875,7 +791,7 @@ pub fn translate_expr( unreachable!("this is always ast::Expr::Cast") } ScalarFunc::Changes => { - if let Some(_) = args { + if args.is_some() { crate::bail_parse_error!( "{} fucntion with more than 0 arguments", srf @@ -1110,12 +1026,8 @@ pub fn translate_expr( ); }; for arg in args { - let reg = program.alloc_register(); let _ = - translate_expr(program, referenced_tables, arg, reg, resolver)?; - if let ast::Expr::Literal(_) = arg { - program.mark_last_insn_constant() - } + translate_and_mark(program, referenced_tables, arg, resolver); } program.emit_insn(Insn::Function { // Only constant patterns for LIKE are supported currently, so this @@ -1153,12 +1065,11 @@ pub fn translate_expr( srf.to_string() ); }; - - let regs = program.alloc_register(); - translate_expr(program, referenced_tables, &args[0], regs, resolver)?; + let reg = + translate_and_mark(program, referenced_tables, &args[0], resolver)?; program.emit_insn(Insn::Function { constant_mask: 0, - start_reg: regs, + start_reg: reg, dest: target_register, func: func_ctx, }); @@ -1184,12 +1095,10 @@ pub fn translate_expr( if let Some(args) = args { for arg in args.iter() { // register containing result of each argument expression - let target_reg = program.alloc_register(); - _ = translate_expr( + let _ = translate_and_mark( program, referenced_tables, arg, - target_reg, resolver, )?; } @@ -1221,15 +1130,14 @@ pub fn translate_expr( let str_reg = program.alloc_register(); let start_reg = program.alloc_register(); let length_reg = program.alloc_register(); - - translate_expr( + let str_reg = translate_expr( program, referenced_tables, &args[0], str_reg, resolver, )?; - translate_expr( + let _ = translate_expr( program, referenced_tables, &args[1], @@ -1245,7 +1153,6 @@ pub fn translate_expr( resolver, )?; } - program.emit_insn(Insn::Function { constant_mask: 0, start_reg: str_reg, @@ -1265,8 +1172,8 @@ pub fn translate_expr( } else { crate::bail_parse_error!("hex function with no arguments",); }; - let regs = program.alloc_register(); - translate_expr(program, referenced_tables, &args[0], regs, resolver)?; + let regs = + translate_and_mark(program, referenced_tables, &args[0], resolver)?; program.emit_insn(Insn::Function { constant_mask: 0, start_reg: regs, @@ -1306,12 +1213,10 @@ pub fn translate_expr( if let Some(args) = args { for arg in args.iter() { // register containing result of each argument expression - let target_reg = program.alloc_register(); - _ = translate_expr( + let _ = translate_and_mark( program, referenced_tables, arg, - target_reg, resolver, )?; } @@ -1325,7 +1230,7 @@ pub fn translate_expr( Ok(target_register) } ScalarFunc::TotalChanges => { - if let Some(_) = args { + if args.is_some() { crate::bail_parse_error!( "{} fucntion with more than 0 arguments", srf.to_string() @@ -1361,11 +1266,7 @@ pub fn translate_expr( }; for arg in args.iter() { - let reg = program.alloc_register(); - translate_expr(program, referenced_tables, arg, reg, resolver)?; - if let ast::Expr::Literal(_) = arg { - program.mark_last_insn_constant(); - } + translate_and_mark(program, referenced_tables, arg, resolver)?; } program.emit_insn(Insn::Function { constant_mask: 0, @@ -1387,12 +1288,7 @@ pub fn translate_expr( crate::bail_parse_error!("min function with no arguments"); }; for arg in args { - let reg = program.alloc_register(); - let _ = - translate_expr(program, referenced_tables, arg, reg, resolver)?; - if let ast::Expr::Literal(_) = arg { - program.mark_last_insn_constant() - } + translate_and_mark(program, referenced_tables, arg, resolver)?; } program.emit_insn(Insn::Function { @@ -1415,12 +1311,7 @@ pub fn translate_expr( crate::bail_parse_error!("max function with no arguments"); }; for arg in args { - let reg = program.alloc_register(); - let _ = - translate_expr(program, referenced_tables, arg, reg, resolver)?; - if let ast::Expr::Literal(_) = arg { - program.mark_last_insn_constant() - } + translate_and_mark(program, referenced_tables, arg, resolver)?; } program.emit_insn(Insn::Function { @@ -1456,7 +1347,7 @@ pub fn translate_expr( resolver, )?; let second_reg = program.alloc_register(); - translate_expr( + let _ = translate_expr( program, referenced_tables, &args[1], @@ -1507,34 +1398,30 @@ pub fn translate_expr( srf.to_string() ); }; - let str_reg = program.alloc_register(); let pattern_reg = program.alloc_register(); let replacement_reg = program.alloc_register(); - - translate_expr( + let _ = translate_expr( program, referenced_tables, &args[0], str_reg, resolver, )?; - translate_expr( + let _ = translate_expr( program, referenced_tables, &args[1], pattern_reg, resolver, )?; - - translate_expr( + let _ = translate_expr( program, referenced_tables, &args[2], replacement_reg, resolver, )?; - program.emit_insn(Insn::Function { constant_mask: 0, start_reg: str_reg, @@ -1601,12 +1488,12 @@ pub fn translate_expr( }; let mut start_reg = None; if let Some(arg) = args.first() { - let reg = program.alloc_register(); - start_reg = Some(reg); - translate_expr(program, referenced_tables, arg, reg, resolver)?; - if let ast::Expr::Literal(_) = arg { - program.mark_last_insn_constant() - } + start_reg = Some(translate_and_mark( + program, + referenced_tables, + arg, + resolver, + )?); } program.emit_insn(Insn::Function { constant_mask: 0, @@ -1648,10 +1535,8 @@ pub fn translate_expr( crate::bail_parse_error!("{} function with no arguments", math_func); }; - let reg = program.alloc_register(); - - translate_expr(program, referenced_tables, &args[0], reg, resolver)?; - + let reg = + translate_and_mark(program, referenced_tables, &args[0], resolver)?; program.emit_insn(Insn::Function { constant_mask: 0, start_reg: reg, @@ -1673,20 +1558,12 @@ pub fn translate_expr( } else { crate::bail_parse_error!("{} function with no arguments", math_func); }; - let reg1 = program.alloc_register(); + let _ = + translate_expr(program, referenced_tables, &args[0], reg1, resolver)?; let reg2 = program.alloc_register(); - - translate_expr(program, referenced_tables, &args[0], reg1, resolver)?; - if let ast::Expr::Literal(_) = &args[0] { - program.mark_last_insn_constant(); - } - - translate_expr(program, referenced_tables, &args[1], reg2, resolver)?; - if let ast::Expr::Literal(_) = &args[1] { - program.mark_last_insn_constant(); - } - + let _ = + translate_expr(program, referenced_tables, &args[1], reg2, resolver)?; program.emit_insn(Insn::Function { constant_mask: 0, start_reg: target_register + 1, @@ -1710,7 +1587,6 @@ pub fn translate_expr( }; let regs = program.alloc_registers(args.len()); - for (i, arg) in args.iter().enumerate() { translate_expr(program, referenced_tables, arg, regs + i, resolver)?; } @@ -1989,6 +1865,20 @@ pub fn maybe_apply_affinity(col_type: Type, target_register: usize, program: &mu } } +pub fn translate_and_mark( + program: &mut ProgramBuilder, + referenced_tables: Option<&[TableReference]>, + expr: &ast::Expr, + resolver: &Resolver, +) -> Result { + let target_register = program.alloc_register(); + translate_expr(program, referenced_tables, expr, target_register, resolver)?; + if matches!(expr, ast::Expr::Literal(_)) { + program.mark_last_insn_constant(); + } + Ok(target_register) +} + /// Get an appropriate name for an expression. /// If the query provides an alias (e.g. `SELECT a AS b FROM t`), use that (e.g. `b`). /// If the expression is a column from a table, use the column name (e.g. `a`). From eebf9bfaac220b3e1612c7c1157d892069d0ab7e Mon Sep 17 00:00:00 2001 From: Kacper Madej Date: Wed, 8 Jan 2025 19:15:35 +0700 Subject: [PATCH 24/28] Implement json_type --- COMPAT.md | 4 +- core/function.rs | 4 ++ core/json/mod.rs | 85 +++++++++++++++++++++++++++++------------- core/translate/expr.rs | 4 +- core/vdbe/mod.rs | 18 ++++++--- testing/json.test | 48 ++++++++++++++++++++++++ 6 files changed, 129 insertions(+), 34 deletions(-) diff --git a/COMPAT.md b/COMPAT.md index 83888f87c..4ddabe10f 100644 --- a/COMPAT.md +++ b/COMPAT.md @@ -281,8 +281,8 @@ Feature support of [sqlite expr syntax](https://www.sqlite.org/lang_expr.html). | jsonb_replace(json,path,value,...) | | | | json_set(json,path,value,...) | | | | jsonb_set(json,path,value,...) | | | -| json_type(json) | | | -| json_type(json,path) | | | +| json_type(json) | Yes | | +| json_type(json,path) | Yes | | | json_valid(json) | | | | json_valid(json,flags) | | | | json_quote(value) | | | diff --git a/core/function.rs b/core/function.rs index 7385b237f..94d752eb5 100644 --- a/core/function.rs +++ b/core/function.rs @@ -27,6 +27,7 @@ pub enum JsonFunc { JsonArray, JsonExtract, JsonArrayLength, + JsonType, } #[cfg(feature = "json")] @@ -40,6 +41,7 @@ impl Display for JsonFunc { Self::JsonArray => "json_array".to_string(), Self::JsonExtract => "json_extract".to_string(), Self::JsonArrayLength => "json_array_length".to_string(), + Self::JsonType => "json_type".to_string(), } ) } @@ -371,6 +373,8 @@ impl Func { "json_array" => Ok(Self::Json(JsonFunc::JsonArray)), #[cfg(feature = "json")] "json_extract" => Ok(Func::Json(JsonFunc::JsonExtract)), + #[cfg(feature = "json")] + "json_type" => Ok(Func::Json(JsonFunc::JsonType)), "unixepoch" => Ok(Self::Scalar(ScalarFunc::UnixEpoch)), "julianday" => Ok(Self::Scalar(ScalarFunc::JulianDay)), "hex" => Ok(Self::Scalar(ScalarFunc::Hex)), diff --git a/core/json/mod.rs b/core/json/mod.rs index eda97bf2b..8ee36b33a 100644 --- a/core/json/mod.rs +++ b/core/json/mod.rs @@ -121,17 +121,13 @@ pub fn json_array_length( json_value: &OwnedValue, json_path: Option<&OwnedValue>, ) -> crate::Result { - let path = match json_path { - Some(OwnedValue::Text(t)) => Some(t.value.to_string()), - Some(OwnedValue::Integer(i)) => Some(i.to_string()), - Some(OwnedValue::Float(f)) => Some(f.to_string()), - _ => None::, - }; - let json = get_json_value(json_value)?; - let arr_val = if let Some(path) = path { - &json_extract_single(&json, path.as_str())? + let arr_val = if let Some(path) = json_path { + match json_extract_single(&json, path)? { + Some(val) => val, + None => return Ok(OwnedValue::Null), + } } else { &json }; @@ -161,10 +157,13 @@ pub fn json_extract(value: &OwnedValue, paths: &[OwnedValue]) -> crate::Result { - let extracted = json_extract_single(&json, p.value.as_str())?; + OwnedValue::Null => { + return Ok(OwnedValue::Null); + } + _ => { + let extracted = json_extract_single(&json, path)?.unwrap_or_else(|| &Val::Null); - if paths.len() == 1 && extracted == Val::Null { + if paths.len() == 1 && extracted == &Val::Null { return Ok(OwnedValue::Null); } @@ -173,8 +172,6 @@ pub fn json_extract(value: &OwnedValue, paths: &[OwnedValue]) -> crate::Result return Ok(OwnedValue::Null), - _ => crate::bail_constraint_error!("JSON path error near: {:?}", path.to_string()), } } @@ -186,8 +183,49 @@ pub fn json_extract(value: &OwnedValue, paths: &[OwnedValue]) -> crate::Result crate::Result { - let json_path = json_path(path)?; +pub fn json_type(value: &OwnedValue, path: Option<&OwnedValue>) -> crate::Result { + if let OwnedValue::Null = value { + return Ok(OwnedValue::Null); + } + + let json = get_json_value(value)?; + + let json = if let Some(path) = path { + match json_extract_single(&json, path)? { + Some(val) => val, + None => return Ok(OwnedValue::Null), + } + } else { + &json + }; + + let val = match json { + Val::Null => "null", + Val::Bool(v) => { + if *v { + "true" + } else { + "false" + } + } + Val::Integer(_) => "integer", + Val::Float(_) => "real", + Val::String(_) => "text", + Val::Array(_) => "array", + Val::Object(_) => "object", + }; + + Ok(OwnedValue::Text(LimboText::json(Rc::new(val.to_string())))) +} + +/// Returns the value at the given JSON path. If the path does not exist, it returns None. +/// If the path is an invalid path, returns an error. +fn json_extract_single<'a>(json: &'a Val, path: &OwnedValue) -> crate::Result> { + let json_path = match path { + OwnedValue::Text(t) => json_path(t.value.as_str())?, + OwnedValue::Null => return Ok(None), + _ => crate::bail_constraint_error!("JSON path error near: {:?}", path.to_string()), + }; let mut current_element = &Val::Null; @@ -204,12 +242,10 @@ fn json_extract_single(json: &Val, path: &str) -> crate::Result { if let Some(value) = map.get(key) { current_element = value; } else { - return Ok(Val::Null); + return Ok(None); } } - _ => { - return Ok(Val::Null); - } + _ => return Ok(None), } } PathElement::ArrayLocator(idx) => match current_element { @@ -223,16 +259,15 @@ fn json_extract_single(json: &Val, path: &str) -> crate::Result { if idx < array.len() as i32 { current_element = &array[idx as usize]; } else { - return Ok(Val::Null); + return Ok(None); } } - _ => { - return Ok(Val::Null); - } + _ => return Ok(None), }, } } - Ok(current_element.clone()) + + Ok(Some(¤t_element)) } #[cfg(test)] diff --git a/core/translate/expr.rs b/core/translate/expr.rs index 4c4ee72b2..e5abbc233 100644 --- a/core/translate/expr.rs +++ b/core/translate/expr.rs @@ -789,7 +789,7 @@ pub fn translate_expr( }); Ok(target_register) } - JsonFunc::JsonArrayLength => { + JsonFunc::JsonArrayLength | JsonFunc::JsonType => { let args = if let Some(args) = args { if args.len() > 2 { crate::bail_parse_error!( @@ -837,7 +837,7 @@ pub fn translate_expr( ScalarFunc::Changes => { if let Some(_) = args { crate::bail_parse_error!( - "{} fucntion with more than 0 arguments", + "{} function with more than 0 arguments", srf ); } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index c9151e934..73557303e 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -41,7 +41,7 @@ use crate::vdbe::insn::Insn; #[cfg(feature = "json")] use crate::{ function::JsonFunc, json::get_json, json::json_array, json::json_array_length, - json::json_extract, + json::json_extract, json::json_type, }; use crate::{Connection, Result, Rows, TransactionState, DATABASE_VERSION}; use datetime::{exec_date, exec_datetime_full, exec_julianday, exec_time, exec_unixepoch}; @@ -1381,17 +1381,25 @@ impl Program { } } #[cfg(feature = "json")] - crate::function::Func::Json(JsonFunc::JsonArrayLength) => { + crate::function::Func::Json( + func @ (JsonFunc::JsonArrayLength | JsonFunc::JsonType), + ) => { let json_value = &state.registers[*start_reg]; let path_value = if arg_count > 1 { Some(&state.registers[*start_reg + 1]) } else { None }; - let json_array_length = json_array_length(json_value, path_value); + let func_result = match func { + JsonFunc::JsonArrayLength => { + json_array_length(json_value, path_value) + } + JsonFunc::JsonType => json_type(json_value, path_value), + _ => unreachable!(), + }; - match json_array_length { - Ok(length) => state.registers[*dest] = length, + match func_result { + Ok(result) => state.registers[*dest] = result, Err(e) => return Err(e), } } diff --git a/testing/json.test b/testing/json.test index 5340f7049..fd2cdb54c 100755 --- a/testing/json.test +++ b/testing/json.test @@ -221,3 +221,51 @@ do_execsql_test json_array_length_via_bad_prop { do_execsql_test json_array_length_nested { SELECT json_array_length('{"one":[[1,2,3],2,3]}', '$.one[0]'); } {{3}} + +do_execsql_test json_type_no_path { + select json_type('{"a":[2,3.5,true,false,null,"x"]}') +} {{object}} + +do_execsql_test json_type_root_path { + select json_type('{"a":[2,3.5,true,false,null,"x"]}','$') +} {{object}} + +do_execsql_test json_type_array { + select json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a') +} {{array}} + +do_execsql_test json_type_integer { + select json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[0]') +} {{integer}} + +do_execsql_test json_type_real { + select json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[1]') +} {{real}} + +do_execsql_test json_type_true { + select json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[2]') +} {{true}} + +do_execsql_test json_type_false { + select json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[3]') +} {{false}} + +do_execsql_test json_type_null { + select json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[4]') +} {{null}} + +do_execsql_test json_type_text { + select json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[5]') +} {{text}} + +do_execsql_test json_type_NULL { + select json_type('{"a":[2,3.5,true,false,null,"x"]}','$.a[6]') +} {{}} + +do_execsql_test json_type_cast { + select json_type(1) +} {{integer}} + +do_execsql_test json_type_null_arg { + select json_type(null) +} {{}} From 3e26e7ebc5d941127bfb8d047d591b20be9e6b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?= Date: Fri, 10 Jan 2025 14:58:35 +0900 Subject: [PATCH 25/28] Remove newSQLException and add throwLimboException in the LimboDB.java --- bindings/java/rs_src/limbo_db.rs | 23 +++-------- .../github/tursodatabase/core/AbstractDB.java | 31 --------------- .../github/tursodatabase/core/LimboDB.java | 38 +++++++++++++++---- 3 files changed, 37 insertions(+), 55 deletions(-) diff --git a/bindings/java/rs_src/limbo_db.rs b/bindings/java/rs_src/limbo_db.rs index 4357b7a8a..b6b4bf848 100644 --- a/bindings/java/rs_src/limbo_db.rs +++ b/bindings/java/rs_src/limbo_db.rs @@ -56,12 +56,14 @@ fn set_err_msg_and_throw_exception<'local>( err_code: i32, err_msg: String, ) { - let error_message_pointer = Box::into_raw(Box::new(err_msg)) as i64; + let error_message_bytes = env + .byte_array_from_slice(err_msg.as_bytes()) + .expect("Failed to convert to byte array"); match env.call_method( obj, - "newSQLException", - "(IJ)Lorg/github/tursodatabase/exceptions/LimboException;", - &[err_code.into(), error_message_pointer.into()], + "throwLimboException", + "(I[B)V", + &[err_code.into(), (&error_message_bytes).into()], ) { Ok(_) => { // do nothing because above method will always return Err @@ -71,16 +73,3 @@ fn set_err_msg_and_throw_exception<'local>( } } } - -#[no_mangle] -pub unsafe extern "system" fn Java_org_github_tursodatabase_core_LimboDB_getErrorMessageUtf8< - 'local, ->( - env: JNIEnv<'local>, - _obj: JObject<'local>, - error_message_ptr: jlong, -) -> JByteArray<'local> { - let error_message = Box::from_raw(error_message_ptr as *mut String); - let error_message_bytes = error_message.as_bytes(); - env.byte_array_from_slice(error_message_bytes).unwrap() -} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java b/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java index adea4cb20..62bd86de3 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/AbstractDB.java @@ -172,35 +172,4 @@ public abstract class AbstractDB { // TODO: add implementation throw new SQLFeatureNotSupportedException(); } - - /** - * Throws SQL Exception with error code. - * - * @param errorCode Error code to be passed. - * @throws SQLException Formatted SQLException with error code - */ - @NativeInvocation - private LimboException newSQLException(int errorCode, long errorMessagePointer) throws SQLException { - throw newSQLException(errorCode, getErrorMessage(errorMessagePointer)); - } - - /** - * Throws formatted SQLException with error code and message. - * - * @param errorCode Error code to be passed. - * @param errorMessage throw newSQLException(errorCode);Error message to be passed. - * @return Formatted SQLException with error code and message. - */ - public static LimboException newSQLException(int errorCode, String errorMessage) { - LimboErrorCode code = LimboErrorCode.getErrorCode(errorCode); - String msg; - if (code == LimboErrorCode.UNKNOWN_ERROR) { - msg = String.format("%s:%s (%s)", code, errorCode, errorMessage); - } else { - msg = String.format("%s (%s)", code, errorMessage); - } - return new LimboException(msg, code); - } - - protected abstract String getErrorMessage(long errorMessagePointer); } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java index 2ed082ce6..2b6c387cb 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java @@ -2,6 +2,8 @@ package org.github.tursodatabase.core; import org.github.tursodatabase.LimboErrorCode; +import org.github.tursodatabase.NativeInvocation; +import org.github.tursodatabase.exceptions.LimboException; import java.nio.charset.StandardCharsets; import java.sql.SQLException; @@ -30,8 +32,7 @@ public final class LimboDB extends AbstractDB { // url example: "jdbc:sqlite:{fileName} /** - * - * @param url e.g. "jdbc:sqlite:fileName + * @param url e.g. "jdbc:sqlite:fileName * @param fileName e.g. path to file */ public static LimboDB create(String url, String fileName) throws SQLException { @@ -83,7 +84,7 @@ public final class LimboDB extends AbstractDB { @Override protected void _open(String fileName, int openFlags) throws SQLException { if (isOpen) { - throw newSQLException(LimboErrorCode.UNKNOWN_ERROR.code, "Already opened"); + throwLimboException(LimboErrorCode.UNKNOWN_ERROR.code, "Already opened"); } dbPtr = _open_utf8(stringToUtf8ByteArray(fileName), openFlags); isOpen = true; @@ -103,12 +104,35 @@ public final class LimboDB extends AbstractDB { @Override public synchronized native int step(long stmt); - @Override - protected String getErrorMessage(long errorMessagePointer) { - return utf8ByteBufferToString(getErrorMessageUtf8(errorMessagePointer)); + /** + * Throws formatted SQLException with error code and message. + * + * @param errorCode Error code. + * @param errorMessageBytes Error message. + */ + @NativeInvocation + private void throwLimboException(int errorCode, byte[] errorMessageBytes) throws SQLException { + String errorMessage = utf8ByteBufferToString(errorMessageBytes); + throwLimboException(errorCode, errorMessage); } - private native byte[] getErrorMessageUtf8(long errorMessagePointer); + /** + * Throws formatted SQLException with error code and message. + * + * @param errorCode Error code. + * @param errorMessage Error message. + */ + public void throwLimboException(int errorCode, String errorMessage) throws SQLException { + LimboErrorCode code = LimboErrorCode.getErrorCode(errorCode); + String msg; + if (code == LimboErrorCode.UNKNOWN_ERROR) { + msg = String.format("%s:%s (%s)", code, errorCode, errorMessage); + } else { + msg = String.format("%s (%s)", code, errorMessage); + } + + throw new LimboException(msg, code); + } private static String utf8ByteBufferToString(byte[] buffer) { if (buffer == null) { From 1bcdf99eab506b2a44aecc3eb6c225629735b270 Mon Sep 17 00:00:00 2001 From: Jussi Saurio Date: Fri, 10 Jan 2025 10:04:07 +0200 Subject: [PATCH 26/28] core/optimizer: do expression rewriting on all expressions --- core/translate/optimizer.rs | 91 +++++++++++++++++++++++++------------ core/translate/planner.rs | 8 ++++ testing/select.test | 17 +++++++ 3 files changed, 86 insertions(+), 30 deletions(-) diff --git a/core/translate/optimizer.rs b/core/translate/optimizer.rs index ec657c58c..d6caba85e 100644 --- a/core/translate/optimizer.rs +++ b/core/translate/optimizer.rs @@ -24,7 +24,7 @@ pub fn optimize_plan(plan: &mut Plan) -> Result<()> { */ fn optimize_select_plan(plan: &mut SelectPlan) -> Result<()> { optimize_subqueries(&mut plan.source)?; - rewrite_exprs(&mut plan.source, &mut plan.where_clause)?; + rewrite_exprs_select(plan)?; if let ConstantConditionEliminationResult::ImpossibleCondition = eliminate_constants(&mut plan.source, &mut plan.where_clause)? { @@ -55,7 +55,7 @@ fn optimize_select_plan(plan: &mut SelectPlan) -> Result<()> { } fn optimize_delete_plan(plan: &mut DeletePlan) -> Result<()> { - rewrite_exprs(&mut plan.source, &mut plan.where_clause)?; + rewrite_exprs_delete(plan)?; if let ConstantConditionEliminationResult::ImpossibleCondition = eliminate_constants(&mut plan.source, &mut plan.where_clause)? { @@ -603,16 +603,45 @@ fn push_scan_direction(operator: &mut SourceOperator, direction: &Direction) { } } -fn rewrite_exprs( - operator: &mut SourceOperator, - where_clauses: &mut Option>, -) -> Result<()> { - if let Some(predicates) = where_clauses { - for expr in predicates.iter_mut() { +fn rewrite_exprs_select(plan: &mut SelectPlan) -> Result<()> { + rewrite_source_operator_exprs(&mut plan.source)?; + for rc in plan.result_columns.iter_mut() { + rewrite_expr(&mut rc.expr)?; + } + for agg in plan.aggregates.iter_mut() { + rewrite_expr(&mut agg.original_expr)?; + } + if let Some(predicates) = &mut plan.where_clause { + for expr in predicates { + rewrite_expr(expr)?; + } + } + if let Some(group_by) = &mut plan.group_by { + for expr in group_by.exprs.iter_mut() { + rewrite_expr(expr)?; + } + } + if let Some(order_by) = &mut plan.order_by { + for (expr, _) in order_by.iter_mut() { rewrite_expr(expr)?; } } + Ok(()) +} + +fn rewrite_exprs_delete(plan: &mut DeletePlan) -> Result<()> { + rewrite_source_operator_exprs(&mut plan.source)?; + if let Some(predicates) = &mut plan.where_clause { + for expr in predicates { + rewrite_expr(expr)?; + } + } + + Ok(()) +} + +fn rewrite_source_operator_exprs(operator: &mut SourceOperator) -> Result<()> { match operator { SourceOperator::Join { left, @@ -620,35 +649,37 @@ fn rewrite_exprs( predicates, .. } => { - rewrite_exprs(left, where_clauses)?; - rewrite_exprs(right, where_clauses)?; + rewrite_source_operator_exprs(left)?; + rewrite_source_operator_exprs(right)?; if let Some(predicates) = predicates { for expr in predicates.iter_mut() { rewrite_expr(expr)?; } } - } - SourceOperator::Scan { - predicates: Some(preds), - .. - } => { - for expr in preds.iter_mut() { - rewrite_expr(expr)?; - } - } - SourceOperator::Search { - predicates: Some(preds), - .. - } => { - for expr in preds.iter_mut() { - rewrite_expr(expr)?; - } - } - _ => (), - } - Ok(()) + Ok(()) + } + SourceOperator::Scan { predicates, .. } | SourceOperator::Search { predicates, .. } => { + if let Some(predicates) = predicates { + for expr in predicates.iter_mut() { + rewrite_expr(expr)?; + } + } + + Ok(()) + } + SourceOperator::Subquery { predicates, .. } => { + if let Some(predicates) = predicates { + for expr in predicates.iter_mut() { + rewrite_expr(expr)?; + } + } + + Ok(()) + } + SourceOperator::Nothing { .. } => Ok(()), + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/core/translate/planner.rs b/core/translate/planner.rs index abd224d64..64a0ffb04 100644 --- a/core/translate/planner.rs +++ b/core/translate/planner.rs @@ -544,6 +544,14 @@ fn parse_join( pub fn parse_limit(limit: Limit) -> Option { if let Expr::Literal(ast::Literal::Numeric(n)) = limit.expr { n.parse().ok() + } else if let Expr::Id(id) = limit.expr { + if id.0.eq_ignore_ascii_case("true") { + Some(1) + } else if id.0.eq_ignore_ascii_case("false") { + Some(0) + } else { + None + } } else { None } diff --git a/testing/select.test b/testing/select.test index c6d403a6a..49f8021bc 100755 --- a/testing/select.test +++ b/testing/select.test @@ -11,6 +11,14 @@ do_execsql_test select-const-2 { SELECT 2 } {2} +do_execsql_test select-true { + SELECT true +} {1} + +do_execsql_test select-false { + SELECT false +} {0} + do_execsql_test select-text-escape-1 { SELECT '''a' } {'a} @@ -31,6 +39,15 @@ do_execsql_test select-limit-0 { SELECT id FROM users LIMIT 0; } {} +# ORDER BY id here because sqlite uses age_idx here and we (yet) don't so force it to evaluate in ID order +do_execsql_test select-limit-true { + SELECT id FROM users ORDER BY id LIMIT true; +} {1} + +do_execsql_test select-limit-false { + SELECT id FROM users ORDER BY id LIMIT false; +} {} + do_execsql_test realify { select price from products limit 1; } {79.0} From 90258a44b4a6c598ffae77fd51f379ab56deec4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?= Date: Fri, 10 Jan 2025 17:47:27 +0900 Subject: [PATCH 27/28] Add throwJavaException --- bindings/java/rs_src/limbo_db.rs | 14 ++++++++++++++ .../tursodatabase/NativeInvocation.java | 2 +- .../tursodatabase/VisibleForTesting.java | 14 ++++++++++++++ .../github/tursodatabase/core/LimboDB.java | 4 ++++ .../tursodatabase/core/LimboDBTest.java | 19 +++++++++++++++++++ 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 bindings/java/src/main/java/org/github/tursodatabase/VisibleForTesting.java diff --git a/bindings/java/rs_src/limbo_db.rs b/bindings/java/rs_src/limbo_db.rs index b6b4bf848..e03e76fa3 100644 --- a/bindings/java/rs_src/limbo_db.rs +++ b/bindings/java/rs_src/limbo_db.rs @@ -50,6 +50,20 @@ pub extern "system" fn Java_org_github_tursodatabase_core_LimboDB__1open_1utf8<' Box::into_raw(Box::new(db)) as jlong } +#[no_mangle] +pub extern "system" fn Java_org_github_tursodatabase_core_LimboDB_throwJavaException<'local>( + mut env: JNIEnv<'local>, + obj: JObject<'local>, + error_code: jint, +) { + set_err_msg_and_throw_exception( + &mut env, + obj, + error_code, + "throw java exception".to_string(), + ); +} + fn set_err_msg_and_throw_exception<'local>( env: &mut JNIEnv<'local>, obj: JObject<'local>, diff --git a/bindings/java/src/main/java/org/github/tursodatabase/NativeInvocation.java b/bindings/java/src/main/java/org/github/tursodatabase/NativeInvocation.java index ee91caf53..70fd6c100 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/NativeInvocation.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/NativeInvocation.java @@ -9,7 +9,7 @@ import java.lang.annotation.Target; /** * Annotation to mark methods that are called by native functions. */ -@Retention(RetentionPolicy.RUNTIME) +@Retention(RetentionPolicy.SOURCE) @Target(ElementType.METHOD) public @interface NativeInvocation { } diff --git a/bindings/java/src/main/java/org/github/tursodatabase/VisibleForTesting.java b/bindings/java/src/main/java/org/github/tursodatabase/VisibleForTesting.java new file mode 100644 index 000000000..1afd119c3 --- /dev/null +++ b/bindings/java/src/main/java/org/github/tursodatabase/VisibleForTesting.java @@ -0,0 +1,14 @@ +package org.github.tursodatabase; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to mark methods that use larger visibility for testing purposes. + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +public @interface VisibleForTesting { +} diff --git a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java index 2b6c387cb..80c3fbe8b 100644 --- a/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java +++ b/bindings/java/src/main/java/org/github/tursodatabase/core/LimboDB.java @@ -3,6 +3,7 @@ package org.github.tursodatabase.core; import org.github.tursodatabase.LimboErrorCode; import org.github.tursodatabase.NativeInvocation; +import org.github.tursodatabase.VisibleForTesting; import org.github.tursodatabase.exceptions.LimboException; import java.nio.charset.StandardCharsets; @@ -104,6 +105,9 @@ public final class LimboDB extends AbstractDB { @Override public synchronized native int step(long stmt); + @VisibleForTesting + native void throwJavaException(int errorCode) throws SQLException; + /** * Throws formatted SQLException with error code and message. * diff --git a/bindings/java/src/test/java/org/github/tursodatabase/core/LimboDBTest.java b/bindings/java/src/test/java/org/github/tursodatabase/core/LimboDBTest.java index 8a62ea083..feeeff060 100644 --- a/bindings/java/src/test/java/org/github/tursodatabase/core/LimboDBTest.java +++ b/bindings/java/src/test/java/org/github/tursodatabase/core/LimboDBTest.java @@ -1,10 +1,13 @@ package org.github.tursodatabase.core; +import org.github.tursodatabase.LimboErrorCode; import org.github.tursodatabase.TestUtils; +import org.github.tursodatabase.exceptions.LimboException; import org.junit.jupiter.api.Test; import java.sql.SQLException; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; public class LimboDBTest { @@ -26,4 +29,20 @@ public class LimboDBTest { assertThatThrownBy(() -> db.open(0)).isInstanceOf(SQLException.class); } + + @Test + void throwJavaException_should_throw_appropriate_java_exception() throws Exception { + String dbPath = TestUtils.createTempFile(); + LimboDB db = LimboDB.create("jdbc:sqlite:" + dbPath, dbPath); + db.load(); + + final int limboExceptionCode = LimboErrorCode.ETC.code; + try { + db.throwJavaException(limboExceptionCode); + } catch (Exception e) { + assertThat(e).isInstanceOf(LimboException.class); + LimboException limboException = (LimboException) e; + assertThat(limboException.getResultCode().code).isEqualTo(limboExceptionCode); + } + } } From 071d9513aba30733da64339f5bcc5f92d4809181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=84=A0=EC=9A=B0?= Date: Fri, 10 Jan 2025 17:51:16 +0900 Subject: [PATCH 28/28] Add CI for java testing --- .github/workflows/java.yml | 38 ++++++++++++++++++++++++++++++++++++++ bindings/java/Makefile | 4 ++-- 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/java.yml diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml new file mode 100644 index 000000000..7c3c6b7ba --- /dev/null +++ b/.github/workflows/java.yml @@ -0,0 +1,38 @@ +name: Java Tests + +on: + push: + branches: + - main + tags: + - v* + pull_request: + branches: + - main + +env: + working-directory: bindings/java + +jobs: + test: + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ${{ env.working-directory }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust(stable) + uses: dtolnay/rust-toolchain@stable + + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '11' + + - name: Run Java tests + run: make test diff --git a/bindings/java/Makefile b/bindings/java/Makefile index a8e979dbc..5d091145d 100644 --- a/bindings/java/Makefile +++ b/bindings/java/Makefile @@ -1,6 +1,6 @@ -.PHONY: lib +.PHONY: test build_test -run_test: build_test +test: build_test ./gradlew test build_test: