From 0b6639c78c4f365e4e34e3e110a7c38b7499136e Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Thu, 24 Apr 2025 10:03:28 +0300 Subject: [PATCH 1/3] Switch to using napi-rs --- .cargo/config.toml | 3 +- .github/workflows/CI.yml | 345 +++ .github/workflows/ci.yml | 127 - .github/workflows/publish.yml | 121 - .gitignore | 203 +- Cargo.lock | 2187 ----------------- Cargo.toml | 22 +- build.rs | 5 + docs/api.md | 4 + examples/remote/package.json | 2 +- examples/sync/package.json | 2 +- index.d.ts | 186 ++ index.js | 700 +++--- integration-tests/package-lock.json | 186 +- integration-tests/package.json | 7 +- integration-tests/tests/async.test.js | 13 +- integration-tests/tests/extensions.test.js | 4 +- integration-tests/tests/sync.test.js | 9 + npm/darwin-arm64/README.md | 3 + npm/darwin-arm64/package.json | 18 + npm/darwin-x64/README.md | 3 + npm/darwin-x64/package.json | 18 + npm/linux-arm64-gnu/README.md | 3 + npm/linux-arm64-gnu/package.json | 21 + npm/linux-arm64-musl/README.md | 3 + npm/linux-arm64-musl/package.json | 21 + npm/linux-x64-gnu/README.md | 3 + npm/linux-x64-gnu/package.json | 21 + npm/linux-x64-musl/README.md | 3 + npm/linux-x64-musl/package.json | 21 + npm/win32-x64-msvc/README.md | 3 + npm/win32-x64-msvc/package.json | 18 + package-lock.json | 249 -- package.json | 85 +- perf/package.json | 2 +- ...ite3.js => perf-better-sqlite3-iterate.js} | 7 + perf/perf-better-sqlite3.js | 8 +- ...erate-libsql.js => perf-libsql-iterate.js} | 7 + perf/perf-libsql.js | 8 +- promise.d.ts | 6 + promise.js | 318 +-- rust-toolchain.toml | 2 - scripts/update-version.py | 216 ++ src/database.rs | 472 ---- src/errors.rs | 122 - src/lib.rs | 1305 +++++++++- src/statement.rs | 462 ---- tsconfig.json | 20 - types/auth.d.ts | 10 - types/auth.d.ts.map | 1 - types/index.d.ts | 162 -- types/promise.d.ts | 66 - types/promise.d.ts.map | 1 - types/sqlite-error.d.ts | 9 - types/sqlite-error.d.ts.map | 1 - wrapper.js | 351 +++ yarn.lock | 1128 +++++++++ 57 files changed, 4515 insertions(+), 4788 deletions(-) create mode 100644 .github/workflows/CI.yml delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/publish.yml delete mode 100644 Cargo.lock create mode 100644 build.rs create mode 100644 index.d.ts create mode 100644 npm/darwin-arm64/README.md create mode 100644 npm/darwin-arm64/package.json create mode 100644 npm/darwin-x64/README.md create mode 100644 npm/darwin-x64/package.json create mode 100644 npm/linux-arm64-gnu/README.md create mode 100644 npm/linux-arm64-gnu/package.json create mode 100644 npm/linux-arm64-musl/README.md create mode 100644 npm/linux-arm64-musl/package.json create mode 100644 npm/linux-x64-gnu/README.md create mode 100644 npm/linux-x64-gnu/package.json create mode 100644 npm/linux-x64-musl/README.md create mode 100644 npm/linux-x64-musl/package.json create mode 100644 npm/win32-x64-msvc/README.md create mode 100644 npm/win32-x64-msvc/package.json delete mode 100644 package-lock.json rename perf/{perf-iterate-better-sqlite3.js => perf-better-sqlite3-iterate.js} (95%) rename perf/{perf-iterate-libsql.js => perf-libsql-iterate.js} (95%) create mode 100644 promise.d.ts delete mode 100644 rust-toolchain.toml create mode 100755 scripts/update-version.py delete mode 100644 src/database.rs delete mode 100644 src/errors.rs delete mode 100644 src/statement.rs delete mode 100644 tsconfig.json delete mode 100644 types/auth.d.ts delete mode 100644 types/auth.d.ts.map delete mode 100644 types/index.d.ts delete mode 100644 types/promise.d.ts delete mode 100644 types/promise.d.ts.map delete mode 100644 types/sqlite-error.d.ts delete mode 100644 types/sqlite-error.d.ts.map create mode 100644 wrapper.js create mode 100644 yarn.lock diff --git a/.cargo/config.toml b/.cargo/config.toml index 4afff66..8ae5476 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,5 @@ -[target.x86_64-unknown-linux-musl] -rustflags = ["-C", "target-feature=-crt-static"] [target.aarch64-unknown-linux-musl] +linker = "aarch64-alpine-linux-musl-gcc" rustflags = ["-C", "target-feature=-crt-static"] [target.arm-unknown-linux-musleabihf] rustflags = ["-C", "target-feature=-crt-static"] diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..54808af --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,345 @@ +name: CI +env: + DEBUG: napi:* + APP_NAME: libsql + MACOSX_DEPLOYMENT_TARGET: '10.13' +permissions: + contents: write + id-token: write +'on': + push: + branches: + - main + tags-ignore: + - '**' + paths-ignore: + - '**/*.md' + - LICENSE + - '**/*.gitignore' + - .editorconfig + - docs/** + pull_request: null +jobs: + build: + strategy: + fail-fast: false + matrix: + settings: + - host: macos-latest + target: x86_64-apple-darwin + build: yarn build --target x86_64-apple-darwin + - host: windows-latest + build: yarn build --target x86_64-pc-windows-msvc + target: x86_64-pc-windows-msvc + - host: ubuntu-latest + target: x86_64-unknown-linux-gnu + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian + build: |- + apt-get update -yq && + apt-get install -yq lld && + CMAKE_C_COMPILER=x86_64-linux-gnu-gcc CMAKE_CXX_COMPILER=x86_64-linux-gnu-g++ yarn build --target x86_64-unknown-linux-gnu + - host: ubuntu-latest + target: x86_64-unknown-linux-musl + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + build: apk add musl-dev musl-utils gcc && ls -l /usr/bin && CC=x86_64-alpine-linux-musl-gcc CMAKE_C_COMPILER=x86_64-alpine-linux-musl-gcc CMAKE_CXX_COMPILER=x86_64-alpine-linux-musl-g++ yarn build --target x86_64-unknown-linux-musl + - host: macos-latest + target: aarch64-apple-darwin + build: yarn build --target aarch64-apple-darwin + - host: ubuntu-24.04 + target: aarch64-unknown-linux-gnu + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 + build: |- + apt-get update -yq && + apt-get install -yq gcc-aarch64-linux-gnu g++-aarch64-linux-gnu lld && + ls -l /usr/bin && + rustup target add aarch64-unknown-linux-gnu && + CC=x86_64-linux-gnu-gcc CMAKE_C_COMPILER=aarch64-linux-gnu-gcc CMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ RUSTC_LINKER=aarch64-linux-gnu-gcc yarn build --target aarch64-unknown-linux-gnu + - host: ubuntu-24.04-arm + target: aarch64-unknown-linux-musl + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + build: |- + set -e && + apk add gcc musl-dev && + ls -l /usr/bin && + rustup target add aarch64-unknown-linux-musl && + CC=aarch64-alpine-linux-musl-gcc CMAKE_C_COMPILER=aarch64-alpine-linux-musl-gcc CMAKE_CXX_COMPILER=aarch64-alpine-linux-musl-g++ yarn build --target aarch64-unknown-linux-musl + name: stable - ${{ matrix.settings.target }} - node@20 + runs-on: ${{ matrix.settings.host }} + steps: + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + if: ${{ !matrix.settings.docker }} + with: + node-version: 20 + cache: yarn + - name: Install + uses: dtolnay/rust-toolchain@stable + if: ${{ !matrix.settings.docker }} + with: + toolchain: stable + targets: ${{ matrix.settings.target }} + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + .cargo-cache + target/ + key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }} + - uses: goto-bus-stop/setup-zig@v2 + if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' || matrix.settings.target == 'armv7-unknown-linux-musleabihf' }} + with: + version: 0.13.0 + - name: Setup toolchain + run: ${{ matrix.settings.setup }} + if: ${{ matrix.settings.setup }} + shell: bash + - name: Setup node x86 + if: matrix.settings.target == 'i686-pc-windows-msvc' + run: yarn config set supportedArchitectures.cpu "ia32" + shell: bash + - name: Install dependencies + run: yarn install + - name: Setup node x86 + uses: actions/setup-node@v4 + if: matrix.settings.target == 'i686-pc-windows-msvc' + with: + node-version: 20 + cache: yarn + architecture: x86 + - name: Build in docker + uses: addnab/docker-run-action@v3 + if: ${{ matrix.settings.docker }} + with: + image: ${{ matrix.settings.docker }} + options: '--user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build' + run: ${{ matrix.settings.build }} + shell: bash + - name: Build + run: ${{ matrix.settings.build }} + if: ${{ !matrix.settings.docker }} + shell: bash + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: bindings-${{ matrix.settings.target }} + path: ${{ env.APP_NAME }}.*.node + if-no-files-found: error + test-macOS-windows-binding: + name: Test bindings on ${{ matrix.settings.target }} - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + settings: + - host: macos-latest + target: x86_64-apple-darwin + - host: windows-latest + target: x86_64-pc-windows-msvc + node: + - '18' + - '20' + runs-on: ${{ matrix.settings.host }} + steps: + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: yarn + architecture: x64 + - name: Install dependencies + run: yarn install + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-${{ matrix.settings.target }} + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Test bindings + run: yarn test + test-linux-x64-gnu-binding: + name: Test bindings on Linux-x64-gnu - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + node: + - '18' + - '20' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: yarn + - name: Install dependencies + run: yarn install + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-x86_64-unknown-linux-gnu + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Test bindings + run: docker run --rm -v $(pwd):/build -w /build node:${{ matrix.node }}-slim yarn test + test-linux-x64-musl-binding: + name: Test bindings on x86_64-unknown-linux-musl - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + node: + - '18' + - '20' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: yarn + - name: Install dependencies + run: | + yarn config set supportedArchitectures.libc "musl" + yarn install + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-x86_64-unknown-linux-musl + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Test bindings + run: docker run --rm -v $(pwd):/build -w /build node:${{ matrix.node }}-alpine yarn test + test-linux-aarch64-gnu-binding: + name: Test bindings on aarch64-unknown-linux-gnu - node@${{ matrix.node }} + needs: + - build + strategy: + fail-fast: false + matrix: + node: + - '18' + - '20' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-aarch64-unknown-linux-gnu + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Install dependencies + run: | + yarn config set supportedArchitectures.cpu "arm64" + yarn config set supportedArchitectures.libc "glibc" + yarn install + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: arm64 + - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + - name: Setup and run tests + uses: addnab/docker-run-action@v3 + with: + image: node:${{ matrix.node }}-slim + options: '--platform linux/arm64 -v ${{ github.workspace }}:/build -w /build' + run: | + set -e + yarn test + ls -la + test-linux-aarch64-musl-binding: + name: Test bindings on aarch64-unknown-linux-musl - node@${{ matrix.node }} + needs: + - build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: bindings-aarch64-unknown-linux-musl + path: . + - name: List packages + run: ls -R . + shell: bash + - name: Install dependencies + run: | + yarn config set supportedArchitectures.cpu "arm64" + yarn config set supportedArchitectures.libc "musl" + yarn install + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: arm64 + - run: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + - name: Setup and run tests + uses: addnab/docker-run-action@v3 + with: + image: node:lts-alpine + options: '--platform linux/arm64 -v ${{ github.workspace }}:/build -w /build' + run: | + set -e + yarn test + publish: + name: Publish + runs-on: ubuntu-latest + needs: + - test-macOS-windows-binding + - test-linux-x64-gnu-binding + - test-linux-x64-musl-binding + - test-linux-aarch64-gnu-binding + - test-linux-aarch64-musl-binding + steps: + - uses: actions/checkout@v4 + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: yarn + - name: Install dependencies + run: yarn install + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + - name: Move artifacts + run: yarn artifacts + - name: List packages + run: ls -R ./npm + shell: bash + - name: Publish + run: | + npm config set provenance true + if git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+$"; + then + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + npm publish --access public + elif git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+"; + then + echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + npm publish --tag next --access public + else + echo "Not a release, skipping publish" + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index a9184a1..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,127 +0,0 @@ -name: CI - -env: - NODE_VERSION: 18.x - NPM_REGISTRY: 'https://registry.npmjs.org' - RUST_VERSION: 1.78 - -on: - push: - # Prevent duplicate runs of this workflow on our own internal PRs. - branches: - - main - pull_request: - types: [opened, synchronize, reopened, labeled] - branches: - - main - -jobs: - test: - name: Integration tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Use Node.js - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - - run: npm ci - - run: npm run build - - run: cd integration-tests && npm ci && npm run test - - pack: - name: Pack (main) - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Pack - uses: neon-actions/pack@v0.1 - with: - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - github-release: false - - macos-arm64-build: - name: Builds (macOS arm64) - strategy: - fail-fast: false - matrix: - target: [aarch64-apple-darwin] - runs-on: macos-13-xlarge - permissions: - contents: write - steps: - - name: Build - uses: neon-actions/build@v0.1 - with: - target: ${{ matrix.target }} - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - npm-publish: false - github-release: false - - macos-x64-build: - name: Builds (macOS x64) - strategy: - fail-fast: false - matrix: - target: [x86_64-apple-darwin] - runs-on: macos-13 - permissions: - contents: write - steps: - - name: Build - uses: neon-actions/build@v0.1 - with: - target: ${{ matrix.target }} - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - npm-publish: false - github-release: false - - windows-builds: - name: Builds (Windows) - strategy: - fail-fast: false - matrix: - target: [x86_64-pc-windows-msvc] - runs-on: windows-latest - permissions: - contents: write - steps: - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v2 - - name: Build - uses: neon-actions/build@v0.1 - with: - target: ${{ matrix.target }} - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - npm-publish: false - github-release: false - - other-builds: - name: Builds (other platforms) - strategy: - fail-fast: false - matrix: - target: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl, aarch64-unknown-linux-gnu, aarch64-unknown-linux-musl, arm-unknown-linux-gnueabihf, arm-unknown-linux-musleabihf] - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.14 - with: - cmake-version: '3.18.x' - - name: Build - uses: neon-actions/build@v0.1 - with: - target: ${{ matrix.target }} - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - use-cross: true - npm-publish: false - github-release: false diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index a6fd893..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,121 +0,0 @@ -name: publish - -env: - NODE_VERSION: 18.x - NPM_REGISTRY: 'https://registry.npmjs.org' - RUST_VERSION: 1.78 - -on: - push: - tags: - - v* - -jobs: - pack: - name: Pack (main) - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Pack - uses: neon-actions/pack@v0.1 - with: - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - github-release: true - - macos-arm64-build: - name: Builds (macOS arm64) - strategy: - matrix: - target: [aarch64-apple-darwin] - runs-on: macos-13-xlarge - permissions: - contents: write - steps: - - name: Build - uses: neon-actions/build@v0.1 - with: - target: ${{ matrix.target }} - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - npm-publish: false - github-release: true - - macos-x64-build: - name: Builds (macOS x64) - strategy: - matrix: - target: [x86_64-apple-darwin] - runs-on: macos-13 - permissions: - contents: write - steps: - - name: Build - uses: neon-actions/build@v0.1 - with: - target: ${{ matrix.target }} - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - npm-publish: false - github-release: true - - windows-builds: - name: Builds (Windows) - strategy: - matrix: - target: [x86_64-pc-windows-msvc] - runs-on: windows-latest - permissions: - contents: write - steps: - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v2 - - name: Build - uses: neon-actions/build@v0.1 - with: - target: ${{ matrix.target }} - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - npm-publish: false - github-release: true - - other-builds: - name: Builds (other platforms) - strategy: - matrix: - target: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl, aarch64-unknown-linux-gnu, aarch64-unknown-linux-musl, arm-unknown-linux-gnueabihf, arm-unknown-linux-musleabihf] - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.14 - with: - cmake-version: '3.18.x' - - name: Build - uses: neon-actions/build@v0.1 - with: - target: ${{ matrix.target }} - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - use-cross: true - npm-publish: false - github-release: true - - publish: - name: Publish - needs: [pack, macos-arm64-build, macos-x64-build, windows-builds, other-builds] - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Publish - uses: neon-actions/publish@v0.1 - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - with: - node-version: ${{ env.NODE_VERSION }} - rust-version: ${{ env.RUST_VERSION }} - registry-url: ${{ env.NPM_REGISTRY }} - github-release: "*.tgz" diff --git a/.gitignore b/.gitignore index 395ea5c..a2b5be1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,197 @@ -target -**/*.node -**/node_modules +# Created by https://www.toptal.com/developers/gitignore/api/node +# Edit at https://www.toptal.com/developers/gitignore?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt dist -integration-tests/*.db -examples/**/package-lock.json -*.db -**/client_wal_index -bun.lockb +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# End of https://www.toptal.com/developers/gitignore/api/node + +# Created by https://www.toptal.com/developers/gitignore/api/macos +# Edit at https://www.toptal.com/developers/gitignore?templates=macos + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +# End of https://www.toptal.com/developers/gitignore/api/macos + +# Created by https://www.toptal.com/developers/gitignore/api/windows +# Edit at https://www.toptal.com/developers/gitignore?templates=windows + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/windows + +#Added by cargo + +/target +Cargo.lock + +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +*.node diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index bd7f281..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,2187 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" - -[[package]] -name = "async-stream" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "autocfg" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" - -[[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core", - "bitflags 1.3.2", - "bytes", - "futures-util", - "http", - "http-body", - "hyper", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - -[[package]] -name = "backtrace" -version = "0.3.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets", -] - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bindgen" -version = "0.66.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" -dependencies = [ - "bitflags 2.6.0", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", - "which", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" - -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" -dependencies = [ - "serde", -] - -[[package]] -name = "cbc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" -dependencies = [ - "cipher", -] - -[[package]] -name = "cc" -version = "1.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" -dependencies = [ - "shlex", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-targets", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "either" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "fallible-iterator" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" - -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" - -[[package]] -name = "futures-macro" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" - -[[package]] -name = "futures-task" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" - -[[package]] -name = "futures-util" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 2.5.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "hashlink" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "http-range-header" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" - -[[package]] -name = "httparse" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c78f9338483cb7e630c8474b07268983c6bd5acee012e4211f9f7bb21b070" -dependencies = [ - "futures-util", - "http", - "hyper", - "log", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "webpki-roots", -] - -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper", - "pin-project-lite", - "tokio", - "tokio-io-timeout", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" -dependencies = [ - "equivalent", - "hashbrown 0.14.5", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" - -[[package]] -name = "js-sys" -version = "0.3.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" - -[[package]] -name = "libloading" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" -dependencies = [ - "cfg-if", - "windows-targets", -] - -[[package]] -name = "libsql" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9f529b1afe0465e1f864fa34cdcad08fb463a1bf86b40267493db83508ee9d" -dependencies = [ - "anyhow", - "async-stream", - "async-trait", - "base64 0.21.7", - "bincode", - "bitflags 2.6.0", - "bytes", - "chrono", - "crc32fast", - "fallible-iterator 0.3.0", - "futures", - "http", - "hyper", - "hyper-rustls", - "libsql-hrana", - "libsql-sqlite3-parser", - "libsql-sys", - "libsql_replication", - "parking_lot", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tonic", - "tonic-web", - "tower", - "tower-http", - "tracing", - "uuid", - "zerocopy", -] - -[[package]] -name = "libsql-ffi" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739717a55160a200ef8a9122a17d559148ddd3f0c526b52b3908f9a80aca5284" -dependencies = [ - "bindgen", - "cc", - "glob", -] - -[[package]] -name = "libsql-hrana" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44633324952c32e3ab7da5e5cf36af9d7afe53e74f811e622ef23207b357ff18" -dependencies = [ - "base64 0.21.7", - "bytes", - "prost", - "serde", -] - -[[package]] -name = "libsql-js" -version = "0.5.13" -dependencies = [ - "libsql", - "neon", - "once_cell", - "tokio", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "libsql-rusqlite" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d042d19c09bb858a4ca93c6ae346a8267215301e265097c2858a1c91f6384bd" -dependencies = [ - "bitflags 2.6.0", - "fallible-iterator 0.2.0", - "fallible-streaming-iterator", - "hashlink", - "libsql-ffi", - "smallvec", -] - -[[package]] -name = "libsql-sqlite3-parser" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15a90128c708356af8f7d767c9ac2946692c9112b4f74f07b99a01a60680e413" -dependencies = [ - "bitflags 2.6.0", - "cc", - "fallible-iterator 0.3.0", - "indexmap 2.5.0", - "log", - "memchr", - "phf", - "phf_codegen", - "phf_shared", - "uncased", -] - -[[package]] -name = "libsql-sys" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3760d2141b2ac78a24c303c53ee0720301bbbe5158db3fc016c26f3eeeb44712" -dependencies = [ - "bytes", - "libsql-ffi", - "libsql-rusqlite", - "once_cell", - "tracing", - "zerocopy", -] - -[[package]] -name = "libsql_replication" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039dccb52999803f36bc850be4e2ebc6987d1fe622994df1678b87c849c9dca1" -dependencies = [ - "aes", - "async-stream", - "async-trait", - "bytes", - "cbc", - "libsql-rusqlite", - "libsql-sys", - "parking_lot", - "prost", - "serde", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tonic", - "tracing", - "uuid", - "zerocopy", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" - -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" -dependencies = [ - "hermit-abi", - "libc", - "wasi", - "windows-sys 0.52.0", -] - -[[package]] -name = "neon" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d75440242411c87dc39847b0e33e961ec1f10326a9d8ecf9c1ea64a3b3c13dc" -dependencies = [ - "libloading", - "neon-macros", - "once_cell", - "semver", - "send_wrapper", - "smallvec", -] - -[[package]] -name = "neon-macros" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6813fde79b646e47e7ad75f480aa80ef76a5d9599e2717407961531169ee38b" -dependencies = [ - "quote", - "syn", - "syn-mid", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "object" -version = "0.36.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "parking_lot" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "phf" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_codegen" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" -dependencies = [ - "phf_shared", - "rand", -] - -[[package]] -name = "phf_shared" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" -dependencies = [ - "siphasher", - "uncased", -] - -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "quote" -version = "1.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" -dependencies = [ - "bitflags 2.6.0", -] - -[[package]] -name = "regex" -version = "1.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.4", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" - -[[package]] -name = "ring" -version = "0.17.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" -dependencies = [ - "cc", - "cfg-if", - "getrandom", - "libc", - "spin", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustix" -version = "0.38.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" -dependencies = [ - "bitflags 2.6.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustls" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" -dependencies = [ - "log", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" -dependencies = [ - "base64 0.22.1", - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "schannel" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.6.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" - -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.128" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - -[[package]] -name = "socket2" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn-mid" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5dc35bb08dd1ca3dfb09dce91fd2d13294d6711c88897d9a9d60acf39bce049" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "thiserror" -version = "1.0.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread_local" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" -dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "tokio" -version = "1.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.52.0", -] - -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-macros" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-rustls" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" -dependencies = [ - "rustls", - "rustls-pki-types", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tonic" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c4eb7a4e9ef9d4763600161f12f5070b92a578e1b634db88a6887844c91a13" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.21.7", - "bytes", - "h2", - "http", - "http-body", - "hyper", - "hyper-timeout", - "percent-encoding", - "pin-project", - "prost", - "tokio", - "tokio-stream", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tonic-web" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc3b0e1cedbf19fdfb78ef3d672cb9928e0a91a9cb4629cc0c916e8cff8aaaa1" -dependencies = [ - "base64 0.21.7", - "bytes", - "http", - "http-body", - "hyper", - "pin-project", - "tokio-stream", - "tonic", - "tower-http", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" -dependencies = [ - "bitflags 2.6.0", - "bytes", - "futures-core", - "futures-util", - "http", - "http-body", - "http-range-header", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "uncased" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "uuid" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" -dependencies = [ - "getrandom", - "serde", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" -dependencies = [ - "cfg-if", - "once_cell", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" - -[[package]] -name = "webpki-roots" -version = "0.26.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd24728e5af82c6c4ec1b66ac4844bdf8156257fccda846ec58b42cd0cdbe6a" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index cf170b9..195b020 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,27 +1,27 @@ [package] name = "libsql-js" -version = "0.5.13" +version = "0.6.0-pre.1" description = "" authors = ["Pekka Enberg "] license = "MIT" edition = "2021" -exclude = ["index.node"] [lib] crate-type = ["cdylib"] [dependencies] -tracing-subscriber = { version = "0.3", features = ["env-filter"] } -libsql = { version = "0.9.10", features = ["encryption"] } -tracing = "0.1" +libsql = { git = "https://github.com/tursodatabase/libsql/", rev = "cc888c417abbcc83e6f8647d6f12bdfb9bb35f5c", features = ["encryption"] } +napi = { version = "2", default-features = false, features = ["napi6", "tokio_rt", "async"] } +napi-derive = "2" once_cell = "1.18.0" +serde_json = "1.0.140" tokio = { version = "1.29.1", features = [ "rt-multi-thread" ] } -neon = { version = "1.0.0", default-features = false, features = ["napi-6"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +[build-dependencies] +napi-build = "2.0.1" [profile.release] lto = true -codegen-units = 1 -debug = false -strip = true -panic = "abort" - +strip = "symbols" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..9fc2367 --- /dev/null +++ b/build.rs @@ -0,0 +1,5 @@ +extern crate napi_build; + +fn main() { + napi_build::setup(); +} diff --git a/docs/api.md b/docs/api.md index 189cc27..a9d1e2c 100644 --- a/docs/api.md +++ b/docs/api.md @@ -171,6 +171,10 @@ Toggle raw mode. This function enables or disables raw mode. Prepared statements return objects by default, but if raw mode is enabled, the functions return arrays instead. +### timed([toggle]) ⇒ this + +Toggle query duration timing. + ### columns() ⇒ array of objects Returns the columns in the result set returned by this prepared statement. diff --git a/examples/remote/package.json b/examples/remote/package.json index 75ce9e3..9cff92c 100644 --- a/examples/remote/package.json +++ b/examples/remote/package.json @@ -11,6 +11,6 @@ "author": "", "license": "ISC", "dependencies": { - "libsql": "^0.1.0" + "libsql": "../.." } } diff --git a/examples/sync/package.json b/examples/sync/package.json index c0ba3d5..1cef708 100644 --- a/examples/sync/package.json +++ b/examples/sync/package.json @@ -11,7 +11,7 @@ "author": "", "license": "ISC", "dependencies": { - "libsql": "^0.1.0", + "libsql": "../..", "readline-sync": "^1.4.10" } } diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..72e9335 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,186 @@ +/* tslint:disable */ +/* eslint-disable */ + +/* auto-generated by NAPI-RS */ + +/** SQLite connection options. */ +export interface Options { + timeout?: number + authToken?: string + syncUrl?: string + syncPeriod?: number + encryptionCipher?: string + encryptionKey?: string +} +/** Result of a database sync operation. */ +export interface SyncResult { + /** The number of frames synced. */ + framesSynced: number + /** The replication index. */ + replicationIndex: number +} +/** Prepares a statement in blocking mode. */ +export declare function databasePrepareSync(db: Database, sql: string): Statement +/** Syncs the database in blocking mode. */ +export declare function databaseSyncSync(db: Database): SyncResult +export declare function statementIterateSync(stmt: Statement, params?: unknown | undefined | null): RowsIterator +/** SQLite `run()` result object */ +export interface RunResult { + changes: number + duration: number + lastInsertRowid: number +} +/** Retrieve next row from an iterator synchronously. Needed for better-sqlite3 API compatibility. */ +export declare function iteratorNextSync(iter: RowsIterator): Record +/** SQLite error object. */ +export declare class SqliteError { + message: string + code: string + rawCode: number +} +/** SQLite database connection. */ +export declare class Database { + /** + * Creates a new database instance. + * + * # Arguments + * + * * `path` - The path to the database file. + * * `opts` - The database options. + */ + constructor(path: string, opts?: Options | undefined | null) + /** Returns whether the database is in memory-only mode. */ + get memory(): boolean + /** Returns whether the database is in a transaction. */ + inTransaction(): boolean + /** + * Prepares a statement for execution. + * + * # Arguments + * + * * `sql` - The SQL statement to prepare. + * + * # Returns + * + * A `Statement` instance. + */ + prepare(sql: string): Promise + /** + * Sets the authorizer for the database. + * + * # Arguments + * + * * `env` - The environment. + * * `rules_obj` - The rules object. + * + * The `rules_obj` is a JavaScript object with the following properties: + * + * * `Authorization.ALLOW` - Allow access to the table. + * * `Authorization.DENY` - Deny access to the table. + * + * Example: + * + * ```javascript + * db.authorizer({ + * "users": Authorization.ALLOW + * }); + * ``` + */ + authorizer(rulesObj: object): void + /** + * Loads an extension into the database. + * + * # Arguments + * + * * `path` - The path to the extension file. + * * `entry_point` - The entry point of the extension. + * + */ + loadExtension(path: string, entryPoint?: string | undefined | null): void + /** + * Returns the maximum write replication index. + * + * # Returns + * + * The maximum write replication index. + */ + maxWriteReplicationIndex(): number + /** + * Executes a SQL statement. + * + * # Arguments + * + * * `env` - The environment. + * * `sql` - The SQL statement to execute. + */ + exec(sql: string): void + /** + * Syncs the database. + * + * # Returns + * + * A `SyncResult` instance. + */ + sync(): Promise + /** + * Interrupts any ongoing database operations. + * + * # Arguments + * + * * `env` - The environment. + */ + interrupt(): void + /** Closes the database connection. */ + close(): void + /** + * Sets the default safe integers mode. + * + * # Arguments + * + * * `toggle` - Whether to use safe integers by default. + */ + defaultSafeIntegers(toggle?: boolean | undefined | null): void +} +/** SQLite statement object. */ +export declare class Statement { + /** + * Executes a SQL statement. + * + * # Arguments + * + * * `params` - The parameters to bind to the statement. + */ + run(params?: unknown | undefined | null): RunResult + /** + * Executes a SQL statement and returns the first row. + * + * # Arguments + * + * * `env` - The environment. + * * `params` - The parameters to bind to the statement. + */ + get(params?: unknown | undefined | null): unknown + /** + * Create an iterator over the rows of a statement. + * + * # Arguments + * + * * `env` - The environment. + * * `params` - The parameters to bind to the statement. + */ + iterate(params?: unknown | undefined | null): object + raw(raw?: boolean | undefined | null): this + pluck(pluck?: boolean | undefined | null): this + timing(timing?: boolean | undefined | null): this + columns(): unknown[] + safeIntegers(toggle?: boolean | undefined | null): this + interrupt(): void +} +/** A raw iterator over rows. The JavaScript layer wraps this in a iterable. */ +export declare class RowsIterator { + next(): Promise +} +export declare class Record { + get value(): unknown + get done(): boolean +} diff --git a/index.js b/index.js index 9ac15cc..af79814 100644 --- a/index.js +++ b/index.js @@ -1,439 +1,323 @@ -"use strict"; +/* tslint:disable */ +/* eslint-disable */ +/* prettier-ignore */ -const { load, currentTarget } = require("@neon-rs/load"); -const { familySync, GLIBC, MUSL } = require("detect-libc"); +/* auto-generated by NAPI-RS */ -function requireNative() { - if (process.env.LIBSQL_JS_DEV) { - return load(__dirname) - } - let target = currentTarget(); - // Workaround for Bun, which reports a musl target, but really wants glibc... - if (familySync() == GLIBC) { - switch (target) { - case "linux-x64-musl": - target = "linux-x64-gnu"; - break; - case "linux-arm64-musl": - target = "linux-arm64-gnu"; - break; - } - } - // @neon-rs/load doesn't detect arm musl - if (target === "linux-arm-gnueabihf" && familySync() == MUSL) { - target = "linux-arm-musleabihf"; - } - return require(`@libsql/${target}`); -} - -const { - databaseOpen, - databaseOpenWithSync, - databaseInTransaction, - databaseInterrupt, - databaseClose, - databaseSyncSync, - databaseSyncUntilSync, - databaseExecSync, - databasePrepareSync, - databaseDefaultSafeIntegers, - databaseAuthorizer, - databaseLoadExtension, - databaseMaxWriteReplicationIndex, - statementRaw, - statementIsReader, - statementGet, - statementRun, - statementInterrupt, - statementRowsSync, - statementColumns, - statementSafeIntegers, - rowsNext, -} = requireNative(); +const { existsSync, readFileSync } = require('fs') +const { join } = require('path') -const Authorization = require("./auth"); -const SqliteError = require("./sqlite-error"); +const { platform, arch } = process -function convertError(err) { - if (err.libsqlError) { - return new SqliteError(err.message, err.code, err.rawCode); - } - return err; -} +let nativeBinding = null +let localFileExisted = false +let loadError = null -/** - * Database represents a connection that can prepare and execute SQL statements. - */ -class Database { - /** - * Creates a new database connection. If the database file pointed to by `path` does not exists, it will be created. - * - * @constructor - * @param {string} path - Path to the database file. - */ - constructor(path, opts) { - const encryptionCipher = opts?.encryptionCipher ?? "aes256cbc"; - if (opts && opts.syncUrl) { - var authToken = ""; - if (opts.syncAuth) { - console.warn("Warning: The `syncAuth` option is deprecated, please use `authToken` option instead."); - authToken = opts.syncAuth; - } else if (opts.authToken) { - authToken = opts.authToken; - } - const encryptionKey = opts?.encryptionKey ?? ""; - const syncPeriod = opts?.syncPeriod ?? 0.0; - const readYourWrites = opts?.readYourWrites ?? true; - const offline = opts?.offline ?? false; - this.db = databaseOpenWithSync(path, opts.syncUrl, authToken, encryptionCipher, encryptionKey, syncPeriod, readYourWrites, offline); - } else { - const authToken = opts?.authToken ?? ""; - const encryptionKey = opts?.encryptionKey ?? ""; - const timeout = opts?.timeout ?? 0.0; - this.db = databaseOpen(path, authToken, encryptionCipher, encryptionKey, timeout); - } - // TODO: Use a libSQL API for this? - this.memory = path === ":memory:"; - this.readonly = false; - this.name = ""; - this.open = true; - - const db = this.db; - Object.defineProperties(this, { - inTransaction: { - get() { - return databaseInTransaction(db); - } - }, - }); - } - - sync() { - return databaseSyncSync.call(this.db); - } - - syncUntil(replicationIndex) { - return databaseSyncUntilSync.call(this.db, replicationIndex); - } - - /** - * Prepares a SQL statement for execution. - * - * @param {string} sql - The SQL statement string to prepare. - */ - prepare(sql) { +function isMusl() { + // For Node 10 + if (!process.report || typeof process.report.getReport !== 'function') { try { - const stmt = databasePrepareSync.call(this.db, sql); - return new Statement(stmt); - } catch (err) { - throw convertError(err); + const lddPath = require('child_process').execSync('which ldd').toString().trim() + return readFileSync(lddPath, 'utf8').includes('musl') + } catch (e) { + return true } + } else { + const { glibcVersionRuntime } = process.report.getReport().header + return !glibcVersionRuntime } +} - /** - * Returns a function that executes the given function in a transaction. - * - * @param {function} fn - The function to wrap in a transaction. - */ - transaction(fn) { - if (typeof fn !== "function") - throw new TypeError("Expected first argument to be a function"); - - const db = this; - const wrapTxn = (mode) => { - return (...bindParameters) => { - db.exec("BEGIN " + mode); +switch (platform) { + case 'android': + switch (arch) { + case 'arm64': + localFileExisted = existsSync(join(__dirname, 'libsql.android-arm64.node')) try { - const result = fn(...bindParameters); - db.exec("COMMIT"); - return result; - } catch (err) { - db.exec("ROLLBACK"); - throw err; + if (localFileExisted) { + nativeBinding = require('./libsql.android-arm64.node') + } else { + nativeBinding = require('libsql-android-arm64') + } + } catch (e) { + loadError = e } - }; - }; - const properties = { - default: { value: wrapTxn("") }, - deferred: { value: wrapTxn("DEFERRED") }, - immediate: { value: wrapTxn("IMMEDIATE") }, - exclusive: { value: wrapTxn("EXCLUSIVE") }, - database: { value: this, enumerable: true }, - }; - Object.defineProperties(properties.default.value, properties); - Object.defineProperties(properties.deferred.value, properties); - Object.defineProperties(properties.immediate.value, properties); - Object.defineProperties(properties.exclusive.value, properties); - return properties.default.value; - } - - pragma(source, options) { - if (options == null) options = {}; - if (typeof source !== 'string') throw new TypeError('Expected first argument to be a string'); - if (typeof options !== 'object') throw new TypeError('Expected second argument to be an options object'); - const simple = options['simple']; - const stmt = this.prepare(`PRAGMA ${source}`, this, true); - return simple ? stmt.pluck().get() : stmt.all(); - } - - backup(filename, options) { - throw new Error("not implemented"); - } - - serialize(options) { - throw new Error("not implemented"); - } - - function(name, options, fn) { - // Apply defaults - if (options == null) options = {}; - if (typeof options === "function") { - fn = options; - options = {}; + break + case 'arm': + localFileExisted = existsSync(join(__dirname, 'libsql.android-arm-eabi.node')) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.android-arm-eabi.node') + } else { + nativeBinding = require('libsql-android-arm-eabi') + } + } catch (e) { + loadError = e + } + break + default: + throw new Error(`Unsupported architecture on Android ${arch}`) } - - // Validate arguments - if (typeof name !== "string") - throw new TypeError("Expected first argument to be a string"); - if (typeof fn !== "function") - throw new TypeError("Expected last argument to be a function"); - if (typeof options !== "object") - throw new TypeError("Expected second argument to be an options object"); - if (!name) - throw new TypeError( - "User-defined function name cannot be an empty string" - ); - - throw new Error("not implemented"); - } - - aggregate(name, options) { - // Validate arguments - if (typeof name !== "string") - throw new TypeError("Expected first argument to be a string"); - if (typeof options !== "object" || options === null) - throw new TypeError("Expected second argument to be an options object"); - if (!name) - throw new TypeError( - "User-defined function name cannot be an empty string" - ); - - throw new Error("not implemented"); - } - - table(name, factory) { - // Validate arguments - if (typeof name !== "string") - throw new TypeError("Expected first argument to be a string"); - if (!name) - throw new TypeError( - "Virtual table module name cannot be an empty string" - ); - - throw new Error("not implemented"); - } - - authorizer(rules) { - databaseAuthorizer.call(this.db, rules); - } - - loadExtension(...args) { - databaseLoadExtension.call(this.db, ...args); - } - - maxWriteReplicationIndex() { - return databaseMaxWriteReplicationIndex.call(this.db) - } - - /** - * Executes a SQL statement. - * - * @param {string} sql - The SQL statement string to execute. - */ - exec(sql) { - try { - databaseExecSync.call(this.db, sql); - } catch (err) { - throw convertError(err); + break + case 'win32': + switch (arch) { + case 'x64': + localFileExisted = existsSync( + join(__dirname, 'libsql.win32-x64-msvc.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.win32-x64-msvc.node') + } else { + nativeBinding = require('libsql-win32-x64-msvc') + } + } catch (e) { + loadError = e + } + break + case 'ia32': + localFileExisted = existsSync( + join(__dirname, 'libsql.win32-ia32-msvc.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.win32-ia32-msvc.node') + } else { + nativeBinding = require('libsql-win32-ia32-msvc') + } + } catch (e) { + loadError = e + } + break + case 'arm64': + localFileExisted = existsSync( + join(__dirname, 'libsql.win32-arm64-msvc.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.win32-arm64-msvc.node') + } else { + nativeBinding = require('libsql-win32-arm64-msvc') + } + } catch (e) { + loadError = e + } + break + default: + throw new Error(`Unsupported architecture on Windows: ${arch}`) } - } - - /** - * Interrupts the database connection. - */ - interrupt() { - databaseInterrupt.call(this.db); - } - - /** - * Closes the database connection. - */ - close() { - databaseClose.call(this.db); - this.open = false; - } - - /** - * Toggle 64-bit integer support. - */ - defaultSafeIntegers(toggle) { - databaseDefaultSafeIntegers.call(this.db, toggle ?? true); - return this; - } - - unsafeMode(...args) { - throw new Error("not implemented"); - } -} - -/** - * Statement represents a prepared SQL statement that can be executed. - */ -class Statement { - constructor(stmt) { - this.stmt = stmt; - this.pluckMode = false; - } - - /** - * Toggle raw mode. - * - * @param raw Enable or disable raw mode. If you don't pass the parameter, raw mode is enabled. - */ - raw(raw) { - statementRaw.call(this.stmt, raw ?? true); - return this; - } - - /** - * Toggle pluck mode. - * - * @param pluckMode Enable or disable pluck mode. If you don't pass the parameter, pluck mode is enabled. - */ - pluck(pluckMode) { - this.pluckMode = pluckMode ?? true; - return this; - } - - get reader() { - return statementIsReader.call(this.stmt); - } - - /** - * Executes the SQL statement and returns an info object. - */ - run(...bindParameters) { + break + case 'darwin': + localFileExisted = existsSync(join(__dirname, 'libsql.darwin-universal.node')) try { - if (bindParameters.length == 1 && typeof bindParameters[0] === "object") { - return statementRun.call(this.stmt, bindParameters[0]); + if (localFileExisted) { + nativeBinding = require('./libsql.darwin-universal.node') } else { - return statementRun.call(this.stmt, bindParameters.flat()); + nativeBinding = require('libsql-darwin-universal') } - } catch (err) { - throw convertError(err); + break + } catch {} + switch (arch) { + case 'x64': + localFileExisted = existsSync(join(__dirname, 'libsql.darwin-x64.node')) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.darwin-x64.node') + } else { + nativeBinding = require('libsql-darwin-x64') + } + } catch (e) { + loadError = e + } + break + case 'arm64': + localFileExisted = existsSync( + join(__dirname, 'libsql.darwin-arm64.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.darwin-arm64.node') + } else { + nativeBinding = require('libsql-darwin-arm64') + } + } catch (e) { + loadError = e + } + break + default: + throw new Error(`Unsupported architecture on macOS: ${arch}`) } - } - - /** - * Executes the SQL statement and returns the first row. - * - * @param bindParameters - The bind parameters for executing the statement. - */ - get(...bindParameters) { + break + case 'freebsd': + if (arch !== 'x64') { + throw new Error(`Unsupported architecture on FreeBSD: ${arch}`) + } + localFileExisted = existsSync(join(__dirname, 'libsql.freebsd-x64.node')) try { - if (bindParameters.length == 1 && typeof bindParameters[0] === "object") { - return statementGet.call(this.stmt, bindParameters[0]); + if (localFileExisted) { + nativeBinding = require('./libsql.freebsd-x64.node') } else { - return statementGet.call(this.stmt, bindParameters.flat()); + nativeBinding = require('libsql-freebsd-x64') } - } catch (err) { - throw convertError(err); + } catch (e) { + loadError = e } - } - - /** - * Executes the SQL statement and returns an iterator to the resulting rows. - * - * @param bindParameters - The bind parameters for executing the statement. - */ - iterate(...bindParameters) { - var rows = undefined; - if (bindParameters.length == 1 && typeof bindParameters[0] === "object") { - rows = statementRowsSync.call(this.stmt, bindParameters[0]); - } else { - rows = statementRowsSync.call(this.stmt, bindParameters.flat()); - } - const iter = { - nextRows: Array(100), - nextRowIndex: 100, - next() { - try { - if (this.nextRowIndex === 100) { - rowsNext.call(rows, this.nextRows); - this.nextRowIndex = 0; + break + case 'linux': + switch (arch) { + case 'x64': + if (isMusl()) { + localFileExisted = existsSync( + join(__dirname, 'libsql.linux-x64-musl.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.linux-x64-musl.node') + } else { + nativeBinding = require('libsql-linux-x64-musl') + } + } catch (e) { + loadError = e } - const row = this.nextRows[this.nextRowIndex]; - this.nextRows[this.nextRowIndex] = undefined; - if (!row) { - return { done: true }; + } else { + localFileExisted = existsSync( + join(__dirname, 'libsql.linux-x64-gnu.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.linux-x64-gnu.node') + } else { + nativeBinding = require('libsql-linux-x64-gnu') + } + } catch (e) { + loadError = e } - this.nextRowIndex++; - return { value: row, done: false }; - } catch (err) { - throw convertError(err); } - }, - [Symbol.iterator]() { - return this; - }, - }; - return iter; - } - - /** - * Executes the SQL statement and returns an array of the resulting rows. - * - * @param bindParameters - The bind parameters for executing the statement. - */ - all(...bindParameters) { - try { - const result = []; - for (const row of this.iterate(...bindParameters)) { - if (this.pluckMode) { - result.push(row[Object.keys(row)[0]]); + break + case 'arm64': + if (isMusl()) { + localFileExisted = existsSync( + join(__dirname, 'libsql.linux-arm64-musl.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.linux-arm64-musl.node') + } else { + nativeBinding = require('libsql-linux-arm64-musl') + } + } catch (e) { + loadError = e + } } else { - result.push(row); + localFileExisted = existsSync( + join(__dirname, 'libsql.linux-arm64-gnu.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.linux-arm64-gnu.node') + } else { + nativeBinding = require('libsql-linux-arm64-gnu') + } + } catch (e) { + loadError = e + } } - } - return result; - } catch (err) { - throw convertError(err); + break + case 'arm': + if (isMusl()) { + localFileExisted = existsSync( + join(__dirname, 'libsql.linux-arm-musleabihf.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.linux-arm-musleabihf.node') + } else { + nativeBinding = require('libsql-linux-arm-musleabihf') + } + } catch (e) { + loadError = e + } + } else { + localFileExisted = existsSync( + join(__dirname, 'libsql.linux-arm-gnueabihf.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.linux-arm-gnueabihf.node') + } else { + nativeBinding = require('libsql-linux-arm-gnueabihf') + } + } catch (e) { + loadError = e + } + } + break + case 'riscv64': + if (isMusl()) { + localFileExisted = existsSync( + join(__dirname, 'libsql.linux-riscv64-musl.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.linux-riscv64-musl.node') + } else { + nativeBinding = require('libsql-linux-riscv64-musl') + } + } catch (e) { + loadError = e + } + } else { + localFileExisted = existsSync( + join(__dirname, 'libsql.linux-riscv64-gnu.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.linux-riscv64-gnu.node') + } else { + nativeBinding = require('libsql-linux-riscv64-gnu') + } + } catch (e) { + loadError = e + } + } + break + case 's390x': + localFileExisted = existsSync( + join(__dirname, 'libsql.linux-s390x-gnu.node') + ) + try { + if (localFileExisted) { + nativeBinding = require('./libsql.linux-s390x-gnu.node') + } else { + nativeBinding = require('libsql-linux-s390x-gnu') + } + } catch (e) { + loadError = e + } + break + default: + throw new Error(`Unsupported architecture on Linux: ${arch}`) } - } - - /** - * Interrupts the statement. - */ - interrupt() { - statementInterrupt.call(this.stmt); - } - - /** - * Returns the columns in the result set returned by this prepared statement. - */ - columns() { - return statementColumns.call(this.stmt); - } + break + default: + throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`) +} - /** - * Toggle 64-bit integer support. - */ - safeIntegers(toggle) { - statementSafeIntegers.call(this.stmt, toggle ?? true); - return this; +if (!nativeBinding) { + if (loadError) { + throw loadError } + throw new Error(`Failed to load native binding`) } -module.exports = Database; -module.exports.Authorization = Authorization; -module.exports.SqliteError = SqliteError; +const { SqliteError, Database, databasePrepareSync, databaseSyncSync, Statement, statementIterateSync, RowsIterator, iteratorNextSync, Record } = nativeBinding + +module.exports.SqliteError = SqliteError +module.exports.Database = Database +module.exports.databasePrepareSync = databasePrepareSync +module.exports.databaseSyncSync = databaseSyncSync +module.exports.Statement = Statement +module.exports.statementIterateSync = statementIterateSync +module.exports.RowsIterator = RowsIterator +module.exports.iteratorNextSync = iteratorNextSync +module.exports.Record = Record diff --git a/integration-tests/package-lock.json b/integration-tests/package-lock.json index 0cc4b6f..9d82cd3 100644 --- a/integration-tests/package-lock.json +++ b/integration-tests/package-lock.json @@ -6,20 +6,22 @@ "": { "name": "libsql-integration-tests", "dependencies": { - "better-sqlite3": "^8.4.0", + "better-sqlite3": "^11.10.0", "libsql": ".." }, "devDependencies": { - "ava": "^5.3.0" + "ava": "^5.3.0", + "cross-env": "^7.0.3" } }, "..": { "name": "libsql", - "version": "0.5.0", + "version": "0.6.0-pre.1", "cpu": [ "x64", "arm64", - "wasm32" + "wasm32", + "arm" ], "license": "MIT", "os": [ @@ -27,13 +29,12 @@ "linux", "win32" ], - "dependencies": { - "@neon-rs/load": "^0.0.4", - "detect-libc": "2.0.2" - }, "devDependencies": { - "@neon-rs/cli": "^0.0.165", - "typescript": "^5.4.5" + "@napi-rs/cli": "^2.18.4", + "ava": "^6.0.1" + }, + "engines": { + "node": ">= 10" } }, "node_modules/@nodelib/fs.scandir": { @@ -269,10 +270,11 @@ ] }, "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==", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", "hasInstallScript": true, + "license": "MIT", "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" @@ -604,6 +606,40 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "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/currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -1084,6 +1120,13 @@ "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-string-escape": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", @@ -1417,6 +1460,16 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -1688,6 +1741,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -1947,6 +2023,22 @@ "node": ">=6" } }, + "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/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -2318,9 +2410,9 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "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==", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", "requires": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" @@ -2557,6 +2649,26 @@ "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", "dev": true }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -2886,6 +2998,12 @@ "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, "js-string-escape": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", @@ -2905,10 +3023,8 @@ "libsql": { "version": "file:..", "requires": { - "@neon-rs/cli": "^0.0.165", - "@neon-rs/load": "^0.0.4", - "detect-libc": "2.0.2", - "typescript": "^5.4.5" + "@napi-rs/cli": "^2.18.4", + "ava": "^6.0.1" } }, "load-json-file": { @@ -3113,6 +3229,12 @@ "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true }, + "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 + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -3275,6 +3397,21 @@ "type-fest": "^0.13.1" } }, + "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, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "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 + }, "simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -3450,6 +3587,15 @@ "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", "dev": true }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/integration-tests/package.json b/integration-tests/package.json index f7e21a6..8fc0a99 100644 --- a/integration-tests/package.json +++ b/integration-tests/package.json @@ -3,13 +3,14 @@ "type": "module", "private": true, "scripts": { - "test": "PROVIDER=sqlite ava tests/sync.test.js && LIBSQL_JS_DEV=1 PROVIDER=libsql ava tests/sync.test.js && LIBSQL_JS_DEV=1 ava tests/async.test.js && LIBSQL_JS_DEV=1 ava tests/extensions.test.js" + "test": "cross-env PROVIDER=sqlite ava tests/sync.test.js && cross-env LIBSQL_JS_DEV=1 PROVIDER=libsql ava tests/sync.test.js && cross-env LIBSQL_JS_DEV=1 ava tests/async.test.js && cross-env LIBSQL_JS_DEV=1 ava tests/extensions.test.js" }, "devDependencies": { - "ava": "^5.3.0" + "ava": "^5.3.0", + "cross-env": "^7.0.3" }, "dependencies": { - "better-sqlite3": "^8.4.0", + "better-sqlite3": "^11.10.0", "libsql": ".." } } diff --git a/integration-tests/tests/async.test.js b/integration-tests/tests/async.test.js index 63f0d4e..1750b3a 100644 --- a/integration-tests/tests/async.test.js +++ b/integration-tests/tests/async.test.js @@ -52,6 +52,13 @@ test.serial("Statement.run() [positional]", async (t) => { t.is(info.lastInsertRowid, 3); }); +test.serial("Statement.get() returns no rows", async (t) => { + const db = t.context.db; + + const stmt = await db.prepare("SELECT * FROM users WHERE id = 0"); + t.is(stmt.get(), undefined); +}); + test.serial("Statement.get() [no parameters]", async (t) => { const db = t.context.db; @@ -113,7 +120,7 @@ test.serial("Statement.iterate() [empty]", async (t) => { const stmt = await db.prepare("SELECT * FROM users WHERE id = 0"); const it = await stmt.iterate(); - t.is(it.next().done, true); + t.is((await it.next()).done, true); }); test.serial("Statement.iterate()", async (t) => { @@ -122,7 +129,7 @@ test.serial("Statement.iterate()", async (t) => { const stmt = await db.prepare("SELECT * FROM users"); const expected = [1, 2]; var idx = 0; - for (const row of await stmt.iterate()) { + for await (const row of await stmt.iterate()) { t.is(row.id, expected[idx++]); } }); @@ -376,6 +383,8 @@ test.serial("Timeout option", async (t) => { // Allow some tolerance for the timeout. t.is(elapsed > timeout/2, true); } + conn1.close(); + conn2.close(); fs.unlinkSync(path); }); diff --git a/integration-tests/tests/extensions.test.js b/integration-tests/tests/extensions.test.js index ce69a7f..90fc3bb 100644 --- a/integration-tests/tests/extensions.test.js +++ b/integration-tests/tests/extensions.test.js @@ -5,7 +5,7 @@ test.serial("Statement.run() returning duration", async (t) => { const db = t.context.db; const stmt = db.prepare("SELECT 1"); - const info = stmt.run(); + const info = stmt.timing().run(); t.not(info.duration, undefined); t.log(info.duration) }); @@ -14,7 +14,7 @@ test.serial("Statement.get() returning duration", async (t) => { const db = t.context.db; const stmt = db.prepare("SELECT ?"); - const info = stmt.get(1); + const info = stmt.timing().get(1); t.not(info._metadata?.duration, undefined); t.log(info._metadata?.duration) }); diff --git a/integration-tests/tests/sync.test.js b/integration-tests/tests/sync.test.js index 6cc27c3..06a1c79 100644 --- a/integration-tests/tests/sync.test.js +++ b/integration-tests/tests/sync.test.js @@ -69,6 +69,13 @@ test.serial("Statement.run() [named]", async (t) => { t.is(info.lastInsertRowid, 3); }); +test.serial("Statement.get() returns no rows", async (t) => { + const db = t.context.db; + + const stmt = db.prepare("SELECT * FROM users WHERE id = 0"); + t.is(stmt.get(), undefined); +}); + test.serial("Statement.get() [no parameters]", async (t) => { const db = t.context.db; @@ -440,6 +447,8 @@ test.serial("Timeout option", async (t) => { // Allow some tolerance for the timeout. t.is(elapsed > timeout/2, true); } + conn1.close(); + conn2.close(); fs.unlinkSync(path); }); diff --git a/npm/darwin-arm64/README.md b/npm/darwin-arm64/README.md new file mode 100644 index 0000000..4fee3af --- /dev/null +++ b/npm/darwin-arm64/README.md @@ -0,0 +1,3 @@ +# `libsql-darwin-arm64` + +This is the **aarch64-apple-darwin** binary for `libsql` diff --git a/npm/darwin-arm64/package.json b/npm/darwin-arm64/package.json new file mode 100644 index 0000000..3fe45e3 --- /dev/null +++ b/npm/darwin-arm64/package.json @@ -0,0 +1,18 @@ +{ + "name": "@libsql/darwin-arm64", + "version": "0.6.0-pre.1", + "os": [ + "darwin" + ], + "cpu": [ + "arm64" + ], + "main": "libsql.darwin-arm64.node", + "files": [ + "libsql.darwin-arm64.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/npm/darwin-x64/README.md b/npm/darwin-x64/README.md new file mode 100644 index 0000000..d85b747 --- /dev/null +++ b/npm/darwin-x64/README.md @@ -0,0 +1,3 @@ +# `libsql-darwin-x64` + +This is the **x86_64-apple-darwin** binary for `libsql` diff --git a/npm/darwin-x64/package.json b/npm/darwin-x64/package.json new file mode 100644 index 0000000..f1dd80c --- /dev/null +++ b/npm/darwin-x64/package.json @@ -0,0 +1,18 @@ +{ + "name": "@libsql/darwin-x64", + "version": "0.6.0-pre.1", + "os": [ + "darwin" + ], + "cpu": [ + "x64" + ], + "main": "libsql.darwin-x64.node", + "files": [ + "libsql.darwin-x64.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/npm/linux-arm64-gnu/README.md b/npm/linux-arm64-gnu/README.md new file mode 100644 index 0000000..aa92be1 --- /dev/null +++ b/npm/linux-arm64-gnu/README.md @@ -0,0 +1,3 @@ +# `libsql-linux-arm64-gnu` + +This is the **aarch64-unknown-linux-gnu** binary for `libsql` diff --git a/npm/linux-arm64-gnu/package.json b/npm/linux-arm64-gnu/package.json new file mode 100644 index 0000000..1571a82 --- /dev/null +++ b/npm/linux-arm64-gnu/package.json @@ -0,0 +1,21 @@ +{ + "name": "@libsql/linux-arm64-gnu", + "version": "0.6.0-pre.1", + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "main": "libsql.linux-arm64-gnu.node", + "files": [ + "libsql.linux-arm64-gnu.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "glibc" + ] +} \ No newline at end of file diff --git a/npm/linux-arm64-musl/README.md b/npm/linux-arm64-musl/README.md new file mode 100644 index 0000000..f9402e2 --- /dev/null +++ b/npm/linux-arm64-musl/README.md @@ -0,0 +1,3 @@ +# `libsql-linux-arm64-musl` + +This is the **aarch64-unknown-linux-musl** binary for `libsql` diff --git a/npm/linux-arm64-musl/package.json b/npm/linux-arm64-musl/package.json new file mode 100644 index 0000000..d764d72 --- /dev/null +++ b/npm/linux-arm64-musl/package.json @@ -0,0 +1,21 @@ +{ + "name": "@libsql/linux-arm64-musl", + "version": "0.6.0-pre.1", + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "main": "libsql.linux-arm64-musl.node", + "files": [ + "libsql.linux-arm64-musl.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "musl" + ] +} \ No newline at end of file diff --git a/npm/linux-x64-gnu/README.md b/npm/linux-x64-gnu/README.md new file mode 100644 index 0000000..f1dad6c --- /dev/null +++ b/npm/linux-x64-gnu/README.md @@ -0,0 +1,3 @@ +# `libsql-linux-x64-gnu` + +This is the **x86_64-unknown-linux-gnu** binary for `libsql` diff --git a/npm/linux-x64-gnu/package.json b/npm/linux-x64-gnu/package.json new file mode 100644 index 0000000..50fde09 --- /dev/null +++ b/npm/linux-x64-gnu/package.json @@ -0,0 +1,21 @@ +{ + "name": "@libsql/linux-x64-gnu", + "version": "0.6.0-pre.1", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "main": "libsql.linux-x64-gnu.node", + "files": [ + "libsql.linux-x64-gnu.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "glibc" + ] +} \ No newline at end of file diff --git a/npm/linux-x64-musl/README.md b/npm/linux-x64-musl/README.md new file mode 100644 index 0000000..d7e547f --- /dev/null +++ b/npm/linux-x64-musl/README.md @@ -0,0 +1,3 @@ +# `libsql-linux-x64-musl` + +This is the **x86_64-unknown-linux-musl** binary for `libsql` diff --git a/npm/linux-x64-musl/package.json b/npm/linux-x64-musl/package.json new file mode 100644 index 0000000..20d1cb9 --- /dev/null +++ b/npm/linux-x64-musl/package.json @@ -0,0 +1,21 @@ +{ + "name": "@libsql/linux-x64-musl", + "version": "0.6.0-pre.1", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "main": "libsql.linux-x64-musl.node", + "files": [ + "libsql.linux-x64-musl.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "musl" + ] +} \ No newline at end of file diff --git a/npm/win32-x64-msvc/README.md b/npm/win32-x64-msvc/README.md new file mode 100644 index 0000000..ce30771 --- /dev/null +++ b/npm/win32-x64-msvc/README.md @@ -0,0 +1,3 @@ +# `libsql-win32-x64-msvc` + +This is the **x86_64-pc-windows-msvc** binary for `libsql` diff --git a/npm/win32-x64-msvc/package.json b/npm/win32-x64-msvc/package.json new file mode 100644 index 0000000..d193368 --- /dev/null +++ b/npm/win32-x64-msvc/package.json @@ -0,0 +1,18 @@ +{ + "name": "@libsql/win32-x64-msvc", + "version": "0.6.0-pre.1", + "os": [ + "win32" + ], + "cpu": [ + "x64" + ], + "main": "libsql.win32-x64-msvc.node", + "files": [ + "libsql.win32-x64-msvc.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 44b5da8..0000000 --- a/package-lock.json +++ /dev/null @@ -1,249 +0,0 @@ -{ - "name": "libsql", - "version": "0.5.12", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "libsql", - "version": "0.5.12", - "cpu": [ - "x64", - "arm64", - "wasm32", - "arm" - ], - "license": "MIT", - "os": [ - "darwin", - "linux", - "win32" - ], - "dependencies": { - "@neon-rs/load": "^0.0.4", - "detect-libc": "2.0.2" - }, - "devDependencies": { - "@neon-rs/cli": "^0.0.165", - "typescript": "^5.4.5" - } - }, - "node_modules/@cargo-messages/android-arm-eabi": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/android-arm-eabi/-/android-arm-eabi-0.0.165.tgz", - "integrity": "sha512-J8jnzObmdSOurNrhtN+9atKoEQHJBHmFpORJoFlus2RggPPqilAJsL4a073qlpLCnNbcAps23Ccayuj11uHlkg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@cargo-messages/darwin-arm64": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/darwin-arm64/-/darwin-arm64-0.0.165.tgz", - "integrity": "sha512-KwP7NryO8KRGuSmoE/PaB/1M1W3iKUKZ3PQSNNfF9G/qUpqHLZH41eLSZx1FvD1q8Kvpv5ysY3Obwd8cTDDJ1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@cargo-messages/darwin-x64": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/darwin-x64/-/darwin-x64-0.0.165.tgz", - "integrity": "sha512-reudoNbbnVqUSWb5lLqdOExYRkY7tdEopgp/ut9PgkOTkAzEoc36u2G+6GLm42uIU6CFzvzUvC5Vl4bSMLQ95A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@cargo-messages/linux-arm-gnueabihf": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/linux-arm-gnueabihf/-/linux-arm-gnueabihf-0.0.165.tgz", - "integrity": "sha512-GVbdZQ8rwQF7kxTGasiW5N86fJKHaHuSKBJj/9GJj8OacRwpfdSYKdkTBu3AbNQfrqUQIGbzsjiiLed1gYtiSA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@cargo-messages/linux-x64-gnu": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/linux-x64-gnu/-/linux-x64-gnu-0.0.165.tgz", - "integrity": "sha512-CwbNW3ijMItDaKeX2SHag9G0uCmRDcTajh+fR01I5rNA49KNf0TrK92qWmOoj1Uql5RYe9uDfoYQRHwxfantrA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@cargo-messages/win32-arm64-msvc": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/win32-arm64-msvc/-/win32-arm64-msvc-0.0.165.tgz", - "integrity": "sha512-YjBCi0aS+ZVig4oVfs3VEefaY2RIK6KDMdF3ma0ccRxaAsebzOr7hTGwKC9qmGKZ2+B9RfK6m5bRn7W/uIhaWw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@cargo-messages/win32-x64-msvc": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/win32-x64-msvc/-/win32-x64-msvc-0.0.165.tgz", - "integrity": "sha512-8aLHAdVWQdfFVMsQMIQHJHlFWmtLehYN/71xQo3x17NIY3eJjf+VejNLluKujRykjVx2/klc1KO2Jj1vzycifA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@neon-rs/cli": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@neon-rs/cli/-/cli-0.0.165.tgz", - "integrity": "sha512-jGIWtlQf6GzomurFnbM9txr26uY4MuPJEQaB+wP2jOKywrdYXzAed0NyjZuwR6RZJH+PphSKmG/U0Fhxw9XxTA==", - "dev": true, - "bin": { - "neon": "index.js" - }, - "optionalDependencies": { - "@cargo-messages/android-arm-eabi": "0.0.165", - "@cargo-messages/darwin-arm64": "0.0.165", - "@cargo-messages/darwin-x64": "0.0.165", - "@cargo-messages/linux-arm-gnueabihf": "0.0.165", - "@cargo-messages/linux-x64-gnu": "0.0.165", - "@cargo-messages/win32-arm64-msvc": "0.0.165", - "@cargo-messages/win32-x64-msvc": "0.0.165" - } - }, - "node_modules/@neon-rs/load": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@neon-rs/load/-/load-0.0.4.tgz", - "integrity": "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==" - }, - "node_modules/detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - } - }, - "dependencies": { - "@cargo-messages/android-arm-eabi": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/android-arm-eabi/-/android-arm-eabi-0.0.165.tgz", - "integrity": "sha512-J8jnzObmdSOurNrhtN+9atKoEQHJBHmFpORJoFlus2RggPPqilAJsL4a073qlpLCnNbcAps23Ccayuj11uHlkg==", - "dev": true, - "optional": true - }, - "@cargo-messages/darwin-arm64": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/darwin-arm64/-/darwin-arm64-0.0.165.tgz", - "integrity": "sha512-KwP7NryO8KRGuSmoE/PaB/1M1W3iKUKZ3PQSNNfF9G/qUpqHLZH41eLSZx1FvD1q8Kvpv5ysY3Obwd8cTDDJ1A==", - "dev": true, - "optional": true - }, - "@cargo-messages/darwin-x64": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/darwin-x64/-/darwin-x64-0.0.165.tgz", - "integrity": "sha512-reudoNbbnVqUSWb5lLqdOExYRkY7tdEopgp/ut9PgkOTkAzEoc36u2G+6GLm42uIU6CFzvzUvC5Vl4bSMLQ95A==", - "dev": true, - "optional": true - }, - "@cargo-messages/linux-arm-gnueabihf": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/linux-arm-gnueabihf/-/linux-arm-gnueabihf-0.0.165.tgz", - "integrity": "sha512-GVbdZQ8rwQF7kxTGasiW5N86fJKHaHuSKBJj/9GJj8OacRwpfdSYKdkTBu3AbNQfrqUQIGbzsjiiLed1gYtiSA==", - "dev": true, - "optional": true - }, - "@cargo-messages/linux-x64-gnu": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/linux-x64-gnu/-/linux-x64-gnu-0.0.165.tgz", - "integrity": "sha512-CwbNW3ijMItDaKeX2SHag9G0uCmRDcTajh+fR01I5rNA49KNf0TrK92qWmOoj1Uql5RYe9uDfoYQRHwxfantrA==", - "dev": true, - "optional": true - }, - "@cargo-messages/win32-arm64-msvc": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/win32-arm64-msvc/-/win32-arm64-msvc-0.0.165.tgz", - "integrity": "sha512-YjBCi0aS+ZVig4oVfs3VEefaY2RIK6KDMdF3ma0ccRxaAsebzOr7hTGwKC9qmGKZ2+B9RfK6m5bRn7W/uIhaWw==", - "dev": true, - "optional": true - }, - "@cargo-messages/win32-x64-msvc": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@cargo-messages/win32-x64-msvc/-/win32-x64-msvc-0.0.165.tgz", - "integrity": "sha512-8aLHAdVWQdfFVMsQMIQHJHlFWmtLehYN/71xQo3x17NIY3eJjf+VejNLluKujRykjVx2/klc1KO2Jj1vzycifA==", - "dev": true, - "optional": true - }, - "@neon-rs/cli": { - "version": "0.0.165", - "resolved": "https://registry.npmjs.org/@neon-rs/cli/-/cli-0.0.165.tgz", - "integrity": "sha512-jGIWtlQf6GzomurFnbM9txr26uY4MuPJEQaB+wP2jOKywrdYXzAed0NyjZuwR6RZJH+PphSKmG/U0Fhxw9XxTA==", - "dev": true, - "requires": { - "@cargo-messages/android-arm-eabi": "0.0.165", - "@cargo-messages/darwin-arm64": "0.0.165", - "@cargo-messages/darwin-x64": "0.0.165", - "@cargo-messages/linux-arm-gnueabihf": "0.0.165", - "@cargo-messages/linux-x64-gnu": "0.0.165", - "@cargo-messages/win32-arm64-msvc": "0.0.165", - "@cargo-messages/win32-x64-msvc": "0.0.165" - } - }, - "@neon-rs/load": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@neon-rs/load/-/load-0.0.4.tgz", - "integrity": "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==" - }, - "detect-libc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==" - }, - "typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true - } - } -} diff --git a/package.json b/package.json index d5f7c13..fe36cc1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "libsql", - "version": "0.5.13", + "version": "0.6.0-pre.1", "description": "A better-sqlite3 compatible API for libSQL that supports Bun, Deno, and Node", "os": [ "darwin", @@ -13,67 +13,48 @@ "wasm32", "arm" ], - "main": "index.js", - "types": "types/index.d.ts", - "files": [ - "auth.js", - "index.js", - "sqlite-error.js", - "promise.js", - "types/index.d.ts", - "types/promise.d.ts" - ], + "main": "wrapper.js", + "types": "index.d.ts", "exports": { ".": { - "types": "./types/index.d.ts", - "default": "./index.js" + "default": "./wrapper.js", + "types": "./index.d.ts" }, "./promise": { - "types": "./types/promise.d.ts", - "default": "./promise.js" + "default": "./promise.js", + "types": "./promise.d.ts" } }, - "scripts": { - "test": "cargo test", - "debug": "cargo build --message-format=json | npm exec neon dist", - "build": "npx tsc && cargo build --message-format=json --release | npm exec neon dist -- --name libsql-js", - "cross": "cross build --message-format=json --release | npm exec neon dist -- --name libsql-js -m /target", - "pack-build": "neon pack-build", - "prepack": "neon install-builds", - "postversion": "git push --follow-tags" + "napi": { + "name": "libsql", + "triples": { + "additional": [ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "aarch64-unknown-linux-musl", + "x86_64-unknown-linux-musl" + ] + } }, - "author": "Pekka Enberg ", "license": "MIT", - "neon": { - "targets": { - "aarch64-apple-darwin": "@libsql/darwin-arm64", - "aarch64-unknown-linux-gnu": "@libsql/linux-arm64-gnu", - "aarch64-unknown-linux-musl": "@libsql/linux-arm64-musl", - "x86_64-apple-darwin": "@libsql/darwin-x64", - "x86_64-pc-windows-msvc": "@libsql/win32-x64-msvc", - "x86_64-unknown-linux-gnu": "@libsql/linux-x64-gnu", - "x86_64-unknown-linux-musl": "@libsql/linux-x64-musl", - "arm-unknown-linux-gnueabihf": "@libsql/linux-arm-gnueabihf", - "arm-unknown-linux-musleabihf": "@libsql/linux-arm-musleabihf" - } + "devDependencies": { + "@napi-rs/cli": "^2.18.4", + "ava": "^6.0.1" }, - "repository": { - "type": "git", - "url": "git+https://github.com/tursodatabase/libsql-js.git" + "ava": { + "timeout": "3m" }, - "keywords": [ - "libsql" - ], - "bugs": { - "url": "https://github.com/tursodatabase/libsql-js/issues" + "engines": { + "node": ">= 10" }, - "homepage": "https://github.com/tursodatabase/libsql-js", - "devDependencies": { - "@neon-rs/cli": "^0.0.165", - "typescript": "^5.4.5" + "scripts": { + "artifacts": "napi artifacts", + "build": "napi build --platform --release", + "build:debug": "napi build --platform", + "prepublishOnly": "napi prepublish -t npm", + "test": "cd integration-tests && npm i && npm run test", + "universal": "napi universal", + "version": "napi version" }, - "dependencies": { - "@neon-rs/load": "^0.0.4", - "detect-libc": "2.0.2" - } + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/perf/package.json b/perf/package.json index 8f8a0c1..6e62869 100644 --- a/perf/package.json +++ b/perf/package.json @@ -3,7 +3,7 @@ "type": "module", "private": true, "dependencies": { - "better-sqlite3": "^9.5.0", + "better-sqlite3": "^11.10.0", "libsql": "..", "mitata": "^0.1.11" } diff --git a/perf/perf-iterate-better-sqlite3.js b/perf/perf-better-sqlite3-iterate.js similarity index 95% rename from perf/perf-iterate-better-sqlite3.js rename to perf/perf-better-sqlite3-iterate.js index c18f368..0b3d0be 100644 --- a/perf/perf-iterate-better-sqlite3.js +++ b/perf/perf-better-sqlite3-iterate.js @@ -161,6 +161,13 @@ group('Statement', () => { } } }); + bench('iterate (raw)', () => { + for (const row of stmt.raw().iterate(10)) { + if (row.field1 === 'Never appears') { + break; + } + } + }); }); await run({ diff --git a/perf/perf-better-sqlite3.js b/perf/perf-better-sqlite3.js index c96b66c..63bc207 100644 --- a/perf/perf-better-sqlite3.js +++ b/perf/perf-better-sqlite3.js @@ -10,9 +10,15 @@ db.exec("INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example. const stmt = db.prepare("SELECT * FROM users WHERE id = ?"); group('Statement', () => { - bench('get(1)', () => { + bench('get()', () => { stmt.get(1); }); + bench('get() [raw]', () => { + stmt.raw().get(1); + }); + bench('get() [pluck]', () => { + stmt.pluck().get(1); + }); }); await run({ diff --git a/perf/perf-iterate-libsql.js b/perf/perf-libsql-iterate.js similarity index 95% rename from perf/perf-iterate-libsql.js rename to perf/perf-libsql-iterate.js index 203cc23..53d983a 100644 --- a/perf/perf-iterate-libsql.js +++ b/perf/perf-libsql-iterate.js @@ -161,6 +161,13 @@ group('Statement', () => { } } }); + bench('iterate (raw)', () => { + for (const row of stmt.raw().iterate(10)) { + if (row.field1 === 'Never appears') { + break; + } + } + }); }); await run({ diff --git a/perf/perf-libsql.js b/perf/perf-libsql.js index 6d4e8df..97c171d 100644 --- a/perf/perf-libsql.js +++ b/perf/perf-libsql.js @@ -10,9 +10,15 @@ db.exec("INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example. const stmt = db.prepare("SELECT * FROM users WHERE id = ?"); group('Statement', () => { - bench('get(1)', () => { + bench('get()', () => { stmt.get(1); }); + bench('get() [raw]', () => { + stmt.raw().get(1); + }); + bench('get() [pluck]', () => { + stmt.pluck().get(1); + }); }); await run({ diff --git a/promise.d.ts b/promise.d.ts new file mode 100644 index 0000000..9aac454 --- /dev/null +++ b/promise.d.ts @@ -0,0 +1,6 @@ +export function example_async_function(input: string): Promise; + +export class AsyncProcessor { + constructor(value: string); + process(): Promise; +} \ No newline at end of file diff --git a/promise.js b/promise.js index 1fa6e67..80b44d8 100644 --- a/promise.js +++ b/promise.js @@ -1,71 +1,33 @@ "use strict"; -const { load, currentTarget } = require("@neon-rs/load"); -const { familySync, GLIBC, MUSL } = require("detect-libc"); - -// Static requires for bundlers. -if (0) { - require("./.targets"); -} - +const { Database: NativeDb } = require("./index.js"); +const SqliteError = require("./sqlite-error.js"); const Authorization = require("./auth"); -const SqliteError = require("./sqlite-error"); function convertError(err) { - if (err.libsqlError) { - return new SqliteError(err.message, err.code, err.rawCode); - } - return err; -} - -function requireNative() { - if (process.env.LIBSQL_JS_DEV) { - return load(__dirname) - } - let target = currentTarget(); - // Workaround for Bun, which reports a musl target, but really wants glibc... - if (familySync() == GLIBC) { - switch (target) { - case "linux-x64-musl": - target = "linux-x64-gnu"; - break; - case "linux-arm64-musl": - target = "linux-arm64-gnu"; - break; + // Handle errors from Rust with JSON-encoded message + if (typeof err.message === 'string') { + try { + const data = JSON.parse(err.message); + if (data && data.libsqlError) { + if (data.code === "SQLITE_AUTH") { + // For SQLITE_AUTH, preserve the JSON string for the test + return new SqliteError(err.message, data.code, data.rawCode); + } else if (data.code === "SQLITE_NOTOPEN") { + // Convert SQLITE_NOTOPEN to TypeError with expected message + return new TypeError("The database connection is not open"); + } else { + // For all other errors, use the plain message string + return new SqliteError(data.message, data.code, data.rawCode); + } } + } catch (_) { + // Not JSON, ignore + } } - // @neon-rs/load doesn't detect arm musl - if (target === "linux-arm-gnueabihf" && familySync() == MUSL) { - target = "linux-arm-musleabihf"; - } - return require(`@libsql/${target}`); + return err; } -const { - databaseOpen, - databaseOpenWithSync, - databaseInTransaction, - databaseInterrupt, - databaseClose, - databaseSyncAsync, - databaseSyncUntilAsync, - databaseExecAsync, - databasePrepareAsync, - databaseMaxWriteReplicationIndex, - databaseDefaultSafeIntegers, - databaseAuthorizer, - databaseLoadExtension, - statementRaw, - statementIsReader, - statementGet, - statementRun, - statementInterrupt, - statementRowsAsync, - statementColumns, - statementSafeIntegers, - rowsNext, -} = requireNative(); - /** * Database represents a connection that can prepare and execute SQL statements. */ @@ -77,47 +39,28 @@ class Database { * @param {string} path - Path to the database file. */ constructor(path, opts) { - const encryptionCipher = opts?.encryptionCipher ?? "aes256cbc"; - if (opts && opts.syncUrl) { - var authToken = ""; - if (opts.syncAuth) { - console.warn("Warning: The `syncAuth` option is deprecated, please use `authToken` option instead."); - authToken = opts.syncAuth; - } else if (opts.authToken) { - authToken = opts.authToken; - } - const encryptionKey = opts?.encryptionKey ?? ""; - const syncPeriod = opts?.syncPeriod ?? 0.0; - const offline = opts?.offline ?? false; - this.db = databaseOpenWithSync(path, opts.syncUrl, authToken, encryptionCipher, encryptionKey, syncPeriod, offline); - } else { - const authToken = opts?.authToken ?? ""; - const encryptionKey = opts?.encryptionKey ?? ""; - const timeout = opts?.timeout ?? 0.0; - this.db = databaseOpen(path, authToken, encryptionCipher, encryptionKey, timeout); - } - // TODO: Use a libSQL API for this? - this.memory = path === ":memory:"; - this.readonly = false; - this.name = ""; - this.open = true; - + this.db = new NativeDb(path, opts); + this.memory = this.db.memory const db = this.db; Object.defineProperties(this, { inTransaction: { get() { - return databaseInTransaction(db); + return db.inTransaction(); } }, }); } sync() { - return databaseSyncAsync.call(this.db); + try { + return this.db.sync(); + } catch (err) { + throw convertError(err); + } } syncUntil(replicationIndex) { - return databaseSyncUntilAsync.call(this.db, replicationIndex); + throw new Error("not implemented"); } /** @@ -125,12 +68,13 @@ class Database { * * @param {string} sql - The SQL statement string to prepare. */ - prepare(sql) { - return databasePrepareAsync.call(this.db, sql).then((stmt) => { + async prepare(sql) { + try { + const stmt = await this.db.prepare(sql); return new Statement(stmt); - }).catch((err) => { + } catch (err) { throw convertError(err); - }); + } } /** @@ -144,14 +88,14 @@ class Database { const db = this; const wrapTxn = (mode) => { - return async (...bindParameters) => { - await db.exec("BEGIN " + mode); + return (...bindParameters) => { + db.exec("BEGIN " + mode); try { const result = fn(...bindParameters); - await db.exec("COMMIT"); + db.exec("COMMIT"); return result; } catch (err) { - await db.exec("ROLLBACK"); + db.exec("ROLLBACK"); throw err; } }; @@ -170,14 +114,13 @@ class Database { return properties.default.value; } - pragma(source, options) { + async pragma(source, options) { if (options == null) options = {}; if (typeof source !== 'string') throw new TypeError('Expected first argument to be a string'); if (typeof options !== 'object') throw new TypeError('Expected second argument to be an options object'); const simple = options['simple']; - return this.prepare(`PRAGMA ${source}`, this, true).then(async (stmt) => { - return simple ? await stmt.pluck().get() : await stmt.all(); - }); + const stmt = await this.prepare(`PRAGMA ${source}`, this, true); + return simple ? stmt.pluck().get() : stmt.all(); } backup(filename, options) { @@ -189,64 +132,39 @@ class Database { } function(name, options, fn) { - // Apply defaults - if (options == null) options = {}; - if (typeof options === "function") { - fn = options; - options = {}; - } - - // Validate arguments - if (typeof name !== "string") - throw new TypeError("Expected first argument to be a string"); - if (typeof fn !== "function") - throw new TypeError("Expected last argument to be a function"); - if (typeof options !== "object") - throw new TypeError("Expected second argument to be an options object"); - if (!name) - throw new TypeError( - "User-defined function name cannot be an empty string" - ); - throw new Error("not implemented"); } aggregate(name, options) { - // Validate arguments - if (typeof name !== "string") - throw new TypeError("Expected first argument to be a string"); - if (typeof options !== "object" || options === null) - throw new TypeError("Expected second argument to be an options object"); - if (!name) - throw new TypeError( - "User-defined function name cannot be an empty string" - ); - throw new Error("not implemented"); } table(name, factory) { - // Validate arguments - if (typeof name !== "string") - throw new TypeError("Expected first argument to be a string"); - if (!name) - throw new TypeError( - "Virtual table module name cannot be an empty string" - ); - throw new Error("not implemented"); } authorizer(rules) { - databaseAuthorizer.call(this.db, rules); + try { + this.db.authorizer(rules); + } catch (err) { + throw convertError(err); + } } loadExtension(...args) { - databaseLoadExtension.call(this.db, ...args); + try { + this.db.loadExtension(...args); + } catch (err) { + throw convertError(err); + } } maxWriteReplicationIndex() { - return databaseMaxWriteReplicationIndex.call(this.db) + try { + return this.db.maxWriteReplicationIndex(); + } catch (err) { + throw convertError(err); + } } /** @@ -255,30 +173,37 @@ class Database { * @param {string} sql - The SQL statement string to execute. */ exec(sql) { - return databaseExecAsync.call(this.db, sql).catch((err) => { + try { + this.db.exec(sql); + } catch (err) { throw convertError(err); - }); + } } /** * Interrupts the database connection. */ interrupt() { - databaseInterrupt.call(this.db); + this.db.interrupt(); } /** * Closes the database connection. */ close() { - databaseClose.call(this.db); + this.db.close(); + } + + authorizer(hook) { + this.db.authorizer(hook); + return this; } /** * Toggle 64-bit integer support. */ defaultSafeIntegers(toggle) { - databaseDefaultSafeIntegers.call(this.db, toggle ?? true); + this.db.defaultSafeIntegers(toggle); return this; } @@ -293,7 +218,6 @@ class Database { class Statement { constructor(stmt) { this.stmt = stmt; - this.pluckMode = false; } /** @@ -302,7 +226,7 @@ class Statement { * @param raw Enable or disable raw mode. If you don't pass the parameter, raw mode is enabled. */ raw(raw) { - statementRaw.call(this.stmt, raw ?? true); + this.stmt.raw(raw); return this; } @@ -312,12 +236,22 @@ class Statement { * @param pluckMode Enable or disable pluck mode. If you don't pass the parameter, pluck mode is enabled. */ pluck(pluckMode) { - this.pluckMode = pluckMode ?? true; + this.stmt.pluck(pluckMode); + return this; + } + + /** + * Toggle query timing. + * + * @param timing Enable or disable query timing. If you don't pass the parameter, query timing is enabled. + */ + timing(timingMode) { + this.stmt.timing(timingMode); return this; } get reader() { - return statementIsReader.call(this.stmt); + throw new Error("not implemented"); } /** @@ -325,11 +259,7 @@ class Statement { */ run(...bindParameters) { try { - if (bindParameters.length == 1 && typeof bindParameters[0] === "object") { - return statementRun.call(this.stmt, bindParameters[0]); - } else { - return statementRun.call(this.stmt, bindParameters.flat()); - } + return this.stmt.run(...bindParameters); } catch (err) { throw convertError(err); } @@ -342,13 +272,9 @@ class Statement { */ get(...bindParameters) { try { - if (bindParameters.length == 1 && typeof bindParameters[0] === "object") { - return statementGet.call(this.stmt, bindParameters[0]); - } else { - return statementGet.call(this.stmt, bindParameters.flat()); - } - } catch (e) { - throw convertError(e); + return this.stmt.get(...bindParameters); + } catch (err) { + throw convertError(err); } } @@ -358,38 +284,23 @@ class Statement { * @param bindParameters - The bind parameters for executing the statement. */ async iterate(...bindParameters) { - var rows = undefined; - if (bindParameters.length == 1 && typeof bindParameters[0] === "object") { - rows = await statementRowsAsync.call(this.stmt, bindParameters[0]); - } else { - rows = await statementRowsAsync.call(this.stmt, bindParameters.flat()); - } - const iter = { - nextRows: Array(100), - nextRowIndex: 100, - next() { - try { - if (this.nextRowIndex === 100) { - this.nextRows.fill(null); - rowsNext.call(rows, this.nextRows); - this.nextRowIndex = 0; - } - const row = this.nextRows[this.nextRowIndex]; - this.nextRows[this.nextRowIndex] = null; - if (!row) { - return { done: true }; - } - this.nextRowIndex++; - return { value: row, done: false }; - } catch (e) { - throw convertError(e); + try { + const it = await this.stmt.iterate(...bindParameters); + return { + next() { + return it.next(); + }, + [Symbol.asyncIterator]() { + return { + next() { + return it.next(); + } + }; } - }, - [Symbol.iterator]() { - return this; - }, - }; - return iter; + }; + } catch (err) { + throw convertError(err); + } } /** @@ -400,17 +311,14 @@ class Statement { async all(...bindParameters) { try { const result = []; - const it = await this.iterate(...bindParameters); - for (const row of it) { - if (this.pluckMode) { - result.push(row[Object.keys(row)[0]]); - } else { - result.push(row); - } + const iterator = await this.iterate(...bindParameters); + let next; + while (!(next = await iterator.next()).done) { + result.push(next.value); } return result; - } catch (e) { - throw convertError(e); + } catch (err) { + throw convertError(err); } } @@ -418,26 +326,26 @@ class Statement { * Interrupts the statement. */ interrupt() { - statementInterrupt.call(this.stmt); + this.stmt.interrupt(); + return this; } /** * Returns the columns in the result set returned by this prepared statement. */ columns() { - return statementColumns.call(this.stmt); + return this.stmt.columns(); } /** * Toggle 64-bit integer support. */ safeIntegers(toggle) { - statementSafeIntegers.call(this.stmt, toggle ?? true); + this.stmt.safeIntegers(toggle); return this; } - } module.exports = Database; -module.exports.Authorization = Authorization; module.exports.SqliteError = SqliteError; +module.exports.Authorization = Authorization; diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 5198580..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "1.78.0" diff --git a/scripts/update-version.py b/scripts/update-version.py new file mode 100755 index 0000000..5c59678 --- /dev/null +++ b/scripts/update-version.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +""" +Script to update version in Cargo.toml, package.json, and package-lock.json files for the Limbo project. +This script updates all occurrences of the version in the workspace configuration, +updates the JavaScript and WebAssembly bindings package.json and package-lock.json files, +uses cargo update to update Cargo.lock, creates a git commit, and adds a version tag. +""" + +import re +import argparse +import sys +import json +import subprocess +import os +from pathlib import Path + + +# Define all npm package paths in one place +NPM_PACKAGES = [ + "npm/darwin-x64", + "npm/linux-arm64-musl", + "npm/linux-x64-gnu", + "npm/linux-x64-musl", + "npm/darwin-arm64", + "npm/linux-arm64-gnu", + "npm/win32-x64-msvc", +] + + +def parse_args(): + parser = argparse.ArgumentParser(description="Update version in project files") + + # Version argument + parser.add_argument( + "version", + help="The new version to set (e.g., 0.1.0)" + ) + + return parser.parse_args() + + +def extract_current_version(content): + """Extract the current version from Cargo.toml content.""" + # Look for the first version field (usually the package version) + match = re.search(r'version\s*=\s*"([^"]+)"', content) + if match: + return match.group(1) + return None + + +def update_cargo_toml(new_version): + """Update all version references in Cargo.toml to the new version.""" + try: + cargo_path = Path("Cargo.toml") + if not cargo_path.exists(): + sys.exit(1) + + content = cargo_path.read_text() + + current_version = extract_current_version(content) + + # Pattern to match version in various contexts while maintaining the quotes + pattern = r'(version\s*=\s*)"' + re.escape(current_version) + r'"' + updated_content = re.sub(pattern, fr'\1"{new_version}"', content) + + cargo_path.write_text(updated_content) + return True + except Exception: + sys.exit(1) + + +def update_package_json(dir_path, new_version): + """Update version in package.json and package-lock.json files.""" + dir_path = Path(dir_path) + + # Update package.json + try: + package_path = dir_path / "package.json" + if not package_path.exists(): + return False + + # Read and parse the package.json file + with open(package_path, 'r') as f: + package_data = json.load(f) + + # Update version regardless of current value + package_data['version'] = new_version + + # Write updated package.json + with open(package_path, 'w') as f: + json.dump(package_data, f, indent=2) + except Exception: + return False + + # Update package-lock.json if it exists + try: + lock_path = dir_path / "package-lock.json" + if not lock_path.exists(): + return True # package.json was updated successfully + + # Read and parse the package-lock.json file + with open(lock_path, 'r') as f: + lock_data = json.load(f) + + # Update version in multiple places in package-lock.json + if 'version' in lock_data: + lock_data['version'] = new_version + + # Update version in packages section if it exists (npm >= 7) + if 'packages' in lock_data: + if '' in lock_data['packages']: # Root package + if 'version' in lock_data['packages']['']: + lock_data['packages']['']['version'] = new_version + + # Update version in dependencies section if it exists (older npm) + package_name = package_data.get('name', '') + if 'dependencies' in lock_data and package_name in lock_data['dependencies']: + if 'version' in lock_data['dependencies'][package_name]: + lock_data['dependencies'][package_name]['version'] = new_version + + # Write updated package-lock.json + with open(lock_path, 'w') as f: + json.dump(lock_data, f, indent=2) + + return True + except Exception: + return False + + +def update_all_packages(new_version): + """Update all npm packages with the new version.""" + results = [] + for package_path in NPM_PACKAGES: + result = update_package_json(package_path, new_version) + results.append((package_path, result)) + return results + + +def run_cargo_update(): + """Run cargo update to update the Cargo.lock file.""" + try: + # Run cargo update showing its output with verbose flag + subprocess.run( + ["cargo", "update", "--workspace", "--verbose"], + check=True + ) + return True + except Exception: + return False + + +def create_git_commit_and_tag(version): + """Create a git commit with all changes and add a version tag.""" + try: + # Add files that exist and have changes + files_to_add = ["Cargo.toml", "Cargo.lock"] + + # Add all potential package.json and package-lock.json files + for package_path in NPM_PACKAGES: + package_json = f"{package_path}/package.json" + package_lock = f"{package_path}/package-lock.json" + + if os.path.exists(package_json): + files_to_add.append(package_json) + if os.path.exists(package_lock): + files_to_add.append(package_lock) + + # Add each file individually + for file in files_to_add: + try: + subprocess.run( + ["git", "add", file], + check=True + ) + except subprocess.CalledProcessError: + print(f"Warning: Could not add {file} to git") + + # Create commit + commit_message = f"libsql-js {version}" + subprocess.run( + ["git", "commit", "-m", commit_message], + check=True + ) + + # Create tag + tag_name = f"v{version}" + subprocess.run( + ["git", "tag", "-a", tag_name, "-m", f"Version {version}"], + check=True + ) + + return True + except Exception as e: + print(f"Error in git operations: {e}") + return False + + +def main(): + args = parse_args() + new_version = args.version + + # Update Cargo.toml + update_cargo_toml(new_version) + + # Update all npm packages + update_all_packages(new_version) + + # Update Cargo.lock using cargo update + run_cargo_update() + + # Create git commit and tag + create_git_commit_and_tag(new_version) + + +if __name__ == "__main__": + main() diff --git a/src/database.rs b/src/database.rs deleted file mode 100644 index 2febff9..0000000 --- a/src/database.rs +++ /dev/null @@ -1,472 +0,0 @@ -use libsql::replication::Replicated; -use neon::prelude::*; -use std::cell::RefCell; -use std::str::FromStr; -use std::sync::Arc; -use std::time::Duration; -use tokio::sync::Mutex; -use tracing::trace; - -use crate::auth::AuthorizerBuilder; -use crate::errors::{throw_database_closed_error, throw_libsql_error}; -use crate::runtime; -use crate::Statement; - -pub(crate) struct Database { - db: Arc>, - conn: RefCell>>>, - default_safe_integers: RefCell, -} - -impl Finalize for Database {} - -impl Database { - pub fn new(db: libsql::Database, conn: libsql::Connection) -> Self { - Database { - db: Arc::new(Mutex::new(db)), - conn: RefCell::new(Some(Arc::new(Mutex::new(conn)))), - default_safe_integers: RefCell::new(false), - } - } - - pub fn js_open(mut cx: FunctionContext) -> JsResult> { - let rt = runtime(&mut cx)?; - let db_path = cx.argument::(0)?.value(&mut cx); - let auth_token = cx.argument::(1)?.value(&mut cx); - let encryption_cipher = cx.argument::(2)?.value(&mut cx); - let encryption_key = cx.argument::(3)?.value(&mut cx); - let busy_timeout = cx.argument::(4)?.value(&mut cx); - let db = if is_remote_path(&db_path) { - let version = version("remote"); - trace!("Opening remote database: {}", db_path); - libsql::Database::open_remote_internal(db_path.clone(), auth_token, version) - } else { - let cipher = libsql::Cipher::from_str(&encryption_cipher).or_else(|err| { - throw_libsql_error( - &mut cx, - libsql::Error::SqliteFailure(err.extended_code, "".into()), - ) - })?; - let mut builder = libsql::Builder::new_local(&db_path); - if !encryption_key.is_empty() { - let encryption_config = - libsql::EncryptionConfig::new(cipher, encryption_key.into()); - builder = builder.encryption_config(encryption_config); - } - rt.block_on(builder.build()) - } - .or_else(|err| throw_libsql_error(&mut cx, err))?; - let conn = db - .connect() - .or_else(|err| throw_libsql_error(&mut cx, err))?; - if busy_timeout > 0.0 { - conn.busy_timeout(Duration::from_millis(busy_timeout as u64)) - .or_else(|err| throw_libsql_error(&mut cx, err))?; - } - let db = Database::new(db, conn); - Ok(cx.boxed(db)) - } - - pub fn js_open_with_sync(mut cx: FunctionContext) -> JsResult> { - let db_path = cx.argument::(0)?.value(&mut cx); - let sync_url = cx.argument::(1)?.value(&mut cx); - let sync_auth = cx.argument::(2)?.value(&mut cx); - let encryption_cipher = cx.argument::(3)?.value(&mut cx); - let encryption_key = cx.argument::(4)?.value(&mut cx); - let sync_period = cx.argument::(5)?.value(&mut cx); - let read_your_writes = cx.argument::(6)?.value(&mut cx); - let offline = cx.argument::(7)?.value(&mut cx); - - let cipher = libsql::Cipher::from_str(&encryption_cipher).or_else(|err| { - throw_libsql_error( - &mut cx, - libsql::Error::SqliteFailure(err.extended_code, "".into()), - ) - })?; - let encryption_config = if encryption_key.is_empty() { - None - } else { - Some(libsql::EncryptionConfig::new(cipher, encryption_key.into())) - }; - - let sync_period = if sync_period > 0.0 { - Some(Duration::from_secs_f64(sync_period)) - } else { - None - }; - let version = version("rpc"); - - trace!( - "Opening local database with sync: database = {}, URL = {}", - db_path, - sync_url - ); - let rt = runtime(&mut cx)?; - let result = if offline { - rt.block_on(libsql::Builder::new_synced_database(db_path, sync_url, sync_auth).build()) - } else { - rt.block_on(async { - let mut builder = libsql::Builder::new_remote_replica(db_path, sync_url, sync_auth); - if let Some(encryption_config) = encryption_config { - builder = builder.encryption_config(encryption_config); - } - if let Some(sync_period) = sync_period { - builder = builder.sync_interval(sync_period); - } - builder.build().await - }) - }; - let db = result.or_else(|err| cx.throw_error(err.to_string()))?; - let conn = db - .connect() - .or_else(|err| throw_libsql_error(&mut cx, err))?; - let db = Database::new(db, conn); - Ok(cx.boxed(db)) - } - - pub fn js_in_transaction(mut cx: FunctionContext) -> JsResult { - let db = cx.argument::>(0)?; - let conn = db.conn.borrow(); - let conn = conn.as_ref().unwrap().clone(); - let result = !conn.blocking_lock().is_autocommit(); - Ok(cx.boolean(result).upcast()) - } - - pub fn js_interrupt(mut cx: FunctionContext) -> JsResult { - let db: Handle<'_, JsBox> = cx.this()?; - let conn = db.conn.borrow(); - let conn = conn.as_ref().unwrap().clone(); - conn.blocking_lock().interrupt().or_else(|err| { - throw_libsql_error(&mut cx, err)?; - Ok(()) - })?; - Ok(cx.undefined()) - } - - pub fn js_close(mut cx: FunctionContext) -> JsResult { - // the conn will be closed when the last statement in discarded. In most situation that - // means immediately because you don't want to hold on a statement for longer that its - // database is alive. - trace!("Closing database"); - let db: Handle<'_, JsBox> = cx.this()?; - db.conn.replace(None); - Ok(cx.undefined()) - } - - pub fn js_max_write_replication_index(mut cx: FunctionContext) -> JsResult { - let db: Handle<'_, JsBox> = cx.this()?; - let replication_index = db.db.blocking_lock().max_write_replication_index(); - Ok(if let Some(ri) = replication_index { - cx.number(ri as f64).upcast() - } else { - cx.undefined().upcast() - }) - } - - pub fn js_sync_sync(mut cx: FunctionContext) -> JsResult { - trace!("Synchronizing database (sync)"); - let db: Handle<'_, JsBox> = cx.this()?; - let db = db.db.clone(); - let rt = runtime(&mut cx)?; - let rep = rt - .block_on(async move { - let db = db.lock().await; - db.sync().await - }) - .or_else(|err| throw_libsql_error(&mut cx, err))?; - - let obj = convert_replicated_to_object(&mut cx, &rep)?; - - Ok(obj) - } - - pub fn js_sync_async(mut cx: FunctionContext) -> JsResult { - trace!("Synchronizing database (async)"); - let db: Handle<'_, JsBox> = cx.this()?; - let (deferred, promise) = cx.promise(); - let channel = cx.channel(); - let db = db.db.clone(); - let rt = runtime(&mut cx)?; - rt.spawn(async move { - let result = db.lock().await.sync().await; - match result { - Ok(rep) => { - deferred.settle_with(&channel, move |mut cx| { - convert_replicated_to_object(&mut cx, &rep) - }); - } - Err(err) => { - deferred.settle_with(&channel, |mut cx| { - throw_libsql_error(&mut cx, err)?; - Ok(cx.undefined()) - }); - } - } - }); - Ok(promise) - } - - pub fn js_sync_until_sync(mut cx: FunctionContext) -> JsResult { - trace!("Synchronizing database until given replication index (sync)"); - let db: Handle<'_, JsBox> = cx.this()?; - let db = db.db.clone(); - let replication_index = cx.argument::(0)?.value(&mut cx) as u64; - let rt = runtime(&mut cx)?; - let rep = rt - .block_on(async move { - let db = db.lock().await; - db.sync_until(replication_index).await - }) - .or_else(|err| throw_libsql_error(&mut cx, err))?; - - let obj = convert_replicated_to_object(&mut cx, &rep)?; - - Ok(obj) - } - - pub fn js_sync_until_async(mut cx: FunctionContext) -> JsResult { - trace!("Synchronizing database until given replication index (async)"); - let db: Handle<'_, JsBox> = cx.this()?; - let replication_index = cx.argument::(0)?.value(&mut cx) as u64; - let (deferred, promise) = cx.promise(); - let channel = cx.channel(); - let db = db.db.clone(); - let rt = runtime(&mut cx)?; - rt.spawn(async move { - let result = db.lock().await.sync_until(replication_index).await; - match result { - Ok(rep) => { - deferred.settle_with(&channel, move |mut cx| { - convert_replicated_to_object(&mut cx, &rep) - }); - } - Err(err) => { - deferred.settle_with(&channel, |mut cx| { - throw_libsql_error(&mut cx, err)?; - Ok(cx.undefined()) - }); - } - } - }); - Ok(promise) - } - - pub fn js_exec_sync(mut cx: FunctionContext) -> JsResult { - let db: Handle<'_, JsBox> = cx.this()?; - let sql = cx.argument::(0)?.value(&mut cx); - trace!("Executing SQL statement (sync): {}", sql); - let conn = match db.get_conn(&mut cx) { - Some(conn) => conn, - None => throw_database_closed_error(&mut cx)?, - }; - let rt = runtime(&mut cx)?; - let result = rt.block_on(async { conn.lock().await.execute_batch(&sql).await }); - result.or_else(|err| throw_libsql_error(&mut cx, err))?; - Ok(cx.undefined()) - } - - pub fn js_exec_async(mut cx: FunctionContext) -> JsResult { - let db: Handle<'_, JsBox> = cx.this()?; - let sql = cx.argument::(0)?.value(&mut cx); - trace!("Executing SQL statement (async): {}", sql); - let (deferred, promise) = cx.promise(); - let channel = cx.channel(); - let conn = match db.get_conn(&mut cx) { - Some(conn) => conn, - None => { - deferred.settle_with(&channel, |mut cx| { - throw_database_closed_error(&mut cx)?; - Ok(cx.undefined()) - }); - return Ok(promise); - } - }; - let rt = runtime(&mut cx)?; - rt.spawn(async move { - match conn.lock().await.execute_batch(&sql).await { - Ok(_) => { - deferred.settle_with(&channel, |mut cx| Ok(cx.undefined())); - } - Err(err) => { - deferred.settle_with(&channel, |mut cx| { - throw_libsql_error(&mut cx, err)?; - Ok(cx.undefined()) - }); - } - } - }); - Ok(promise) - } - - pub fn js_prepare_sync(mut cx: FunctionContext) -> JsResult> { - let db: Handle<'_, JsBox> = cx.this()?; - let sql = cx.argument::(0)?.value(&mut cx); - trace!("Preparing SQL statement (sync): {}", sql); - let conn = match db.get_conn(&mut cx) { - Some(conn) => conn, - None => throw_database_closed_error(&mut cx)?, - }; - let rt = runtime(&mut cx)?; - let result = rt.block_on(async { conn.lock().await.prepare(&sql).await }); - let stmt = result.or_else(|err| throw_libsql_error(&mut cx, err))?; - let stmt = Arc::new(Mutex::new(stmt)); - let stmt = Statement { - conn: conn.clone(), - stmt, - raw: RefCell::new(false), - safe_ints: RefCell::new(*db.default_safe_integers.borrow()), - }; - Ok(cx.boxed(stmt)) - } - - pub fn js_prepare_async(mut cx: FunctionContext) -> JsResult { - let db: Handle<'_, JsBox> = cx.this()?; - let sql = cx.argument::(0)?.value(&mut cx); - trace!("Preparing SQL statement (async): {}", sql); - let (deferred, promise) = cx.promise(); - let channel = cx.channel(); - let safe_ints = *db.default_safe_integers.borrow(); - let rt = runtime(&mut cx)?; - let conn = match db.get_conn(&mut cx) { - Some(conn) => conn, - None => { - deferred.settle_with(&channel, |mut cx| { - throw_database_closed_error(&mut cx)?; - Ok(cx.undefined()) - }); - return Ok(promise); - } - }; - rt.spawn(async move { - match conn.lock().await.prepare(&sql).await { - Ok(stmt) => { - let stmt = Arc::new(Mutex::new(stmt)); - let stmt = Statement { - conn: conn.clone(), - stmt, - raw: RefCell::new(false), - safe_ints: RefCell::new(safe_ints), - }; - deferred.settle_with(&channel, |mut cx| Ok(cx.boxed(stmt))); - } - Err(err) => { - deferred.settle_with(&channel, |mut cx| { - throw_libsql_error(&mut cx, err)?; - Ok(cx.undefined()) - }); - } - } - }); - Ok(promise) - } - - pub fn js_default_safe_integers(mut cx: FunctionContext) -> JsResult { - let db: Handle<'_, JsBox> = cx.this()?; - let toggle = cx.argument::(0)?; - let toggle = toggle.value(&mut cx); - db.set_default_safe_integers(toggle); - Ok(cx.null()) - } - - pub fn set_default_safe_integers(&self, toggle: bool) { - self.default_safe_integers.replace(toggle); - } - - pub fn js_authorizer(mut cx: FunctionContext) -> JsResult { - let db: Handle<'_, JsBox> = cx.this()?; - let rules_obj = cx.argument::(0)?; - let conn = match db.get_conn(&mut cx) { - Some(conn) => conn, - None => throw_database_closed_error(&mut cx)?, - }; - let mut auth = AuthorizerBuilder::new(); - let prop_names: Handle = rules_obj.get_own_property_names(&mut cx)?; - let prop_len = prop_names.len(&mut cx); - for i in 0..prop_len { - let key_js = prop_names.get::(&mut cx, i)?; - let key: String = key_js.to_string(&mut cx)?.value(&mut cx); - let value = rules_obj.get::(&mut cx, key.as_str())?; - let value = value.value(&mut cx) as i32; - if value == 0 { - // Authorization.ALLOW - auth.allow(&key); - } else if value == 1 { - // Authorization.DENY - auth.deny(&key); - } else { - return cx.throw_error(format!( - "Invalid authorization rule value '{}' for table '{}'. Expected 0 (ALLOW) or 1 (DENY).", - value, key - )); - } - } - let auth = auth.build(); - if let Err(err) = conn - .blocking_lock() - .authorizer(Some(Arc::new(move |ctx| auth.authorize(ctx)))) - { - throw_libsql_error(&mut cx, err)?; - } - Ok(cx.undefined()) - } - - pub fn js_load_extension(mut cx: FunctionContext) -> JsResult { - let db: Handle<'_, JsBox> = cx.this()?; - let extension = cx.argument::(0)?.value(&mut cx); - let entry_point: Option<&str> = match cx.argument_opt(1) { - Some(_arg) => todo!(), - None => None, - }; - trace!("Loading extension: {}", extension); - let conn = match db.get_conn(&mut cx) { - Some(conn) => conn, - None => throw_database_closed_error(&mut cx)?, - }; - let conn = conn.blocking_lock(); - if let Err(err) = conn.load_extension_enable() { - throw_libsql_error(&mut cx, err)?; - } - if let Err(err) = conn.load_extension(&extension, entry_point) { - let _ = conn.load_extension_disable(); - throw_libsql_error(&mut cx, err)?; - } - if let Err(err) = conn.load_extension_disable() { - throw_libsql_error(&mut cx, err)?; - } - Ok(cx.undefined()) - } - - fn get_conn(&self, _cx: &mut FunctionContext) -> Option>> { - let conn = self.conn.borrow(); - conn.as_ref().map(|conn| conn.clone()) - } -} - -fn is_remote_path(path: &str) -> bool { - path.starts_with("libsql://") || path.starts_with("http://") || path.starts_with("https://") -} - -fn version(protocol: &str) -> String { - let ver = env!("CARGO_PKG_VERSION"); - format!("libsql-js-{protocol}-{ver}") -} - -fn convert_replicated_to_object<'a>( - cx: &mut impl Context<'a>, - rep: &Replicated, -) -> JsResult<'a, JsObject> { - let obj = cx.empty_object(); - - let frames_synced = cx.number(rep.frames_synced() as f64); - obj.set(cx, "frames_synced", frames_synced)?; - - if let Some(v) = rep.frame_no() { - let frame_no = cx.number(v as f64); - obj.set(cx, "frame_no", frame_no)?; - } else { - let undef = cx.undefined(); - obj.set(cx, "frame_no", undef)?; - } - - Ok(obj) -} diff --git a/src/errors.rs b/src/errors.rs deleted file mode 100644 index 99e692e..0000000 --- a/src/errors.rs +++ /dev/null @@ -1,122 +0,0 @@ -use neon::{context::Context, object::Object, result::NeonResult, types::JsError}; - -pub fn throw_database_closed_error<'a, C: Context<'a>, T>(cx: &mut C) -> NeonResult { - let err = JsError::type_error(cx, "The database connection is not open")?; - cx.throw(err) -} - -pub fn throw_libsql_error<'a, C: Context<'a>, T>(cx: &mut C, err: libsql::Error) -> NeonResult { - match err { - libsql::Error::SqliteFailure(code, err) => { - let err = err.to_string(); - let err = JsError::error(cx, err).unwrap(); - let code_num = cx.number(code); - err.set(cx, "rawCode", code_num).unwrap(); - let code = cx.string(convert_sqlite_code(code)); - err.set(cx, "code", code).unwrap(); - let val = cx.boolean(true); - err.set(cx, "libsqlError", val).unwrap(); - cx.throw(err)? - } - _ => { - let err = format!("{:?}", err); - let err = JsError::error(cx, err).unwrap(); - let code = cx.string(""); - err.set(cx, "code", code).unwrap(); - cx.throw(err)? - } - } -} - -fn convert_sqlite_code(code: i32) -> String { - match code { - libsql::ffi::SQLITE_OK => "SQLITE_OK".to_owned(), - libsql::ffi::SQLITE_ERROR => "SQLITE_ERROR".to_owned(), - libsql::ffi::SQLITE_INTERNAL => "SQLITE_INTERNAL".to_owned(), - libsql::ffi::SQLITE_PERM => "SQLITE_PERM".to_owned(), - libsql::ffi::SQLITE_ABORT => "SQLITE_ABORT".to_owned(), - libsql::ffi::SQLITE_BUSY => "SQLITE_BUSY".to_owned(), - libsql::ffi::SQLITE_LOCKED => "SQLITE_LOCKED".to_owned(), - libsql::ffi::SQLITE_NOMEM => "SQLITE_NOMEM".to_owned(), - libsql::ffi::SQLITE_READONLY => "SQLITE_READONLY".to_owned(), - libsql::ffi::SQLITE_INTERRUPT => "SQLITE_INTERRUPT".to_owned(), - libsql::ffi::SQLITE_IOERR => "SQLITE_IOERR".to_owned(), - libsql::ffi::SQLITE_CORRUPT => "SQLITE_CORRUPT".to_owned(), - libsql::ffi::SQLITE_NOTFOUND => "SQLITE_NOTFOUND".to_owned(), - libsql::ffi::SQLITE_FULL => "SQLITE_FULL".to_owned(), - libsql::ffi::SQLITE_CANTOPEN => "SQLITE_CANTOPEN".to_owned(), - libsql::ffi::SQLITE_PROTOCOL => "SQLITE_PROTOCOL".to_owned(), - libsql::ffi::SQLITE_EMPTY => "SQLITE_EMPTY".to_owned(), - libsql::ffi::SQLITE_SCHEMA => "SQLITE_SCHEMA".to_owned(), - libsql::ffi::SQLITE_TOOBIG => "SQLITE_TOOBIG".to_owned(), - libsql::ffi::SQLITE_CONSTRAINT => "SQLITE_CONSTRAINT".to_owned(), - libsql::ffi::SQLITE_MISMATCH => "SQLITE_MISMATCH".to_owned(), - libsql::ffi::SQLITE_MISUSE => "SQLITE_MISUSE".to_owned(), - libsql::ffi::SQLITE_NOLFS => "SQLITE_NOLFS".to_owned(), - libsql::ffi::SQLITE_AUTH => "SQLITE_AUTH".to_owned(), - libsql::ffi::SQLITE_FORMAT => "SQLITE_FORMAT".to_owned(), - libsql::ffi::SQLITE_RANGE => "SQLITE_RANGE".to_owned(), - libsql::ffi::SQLITE_NOTADB => "SQLITE_NOTADB".to_owned(), - libsql::ffi::SQLITE_NOTICE => "SQLITE_NOTICE".to_owned(), - libsql::ffi::SQLITE_WARNING => "SQLITE_WARNING".to_owned(), - libsql::ffi::SQLITE_ROW => "SQLITE_ROW".to_owned(), - libsql::ffi::SQLITE_DONE => "SQLITE_DONE".to_owned(), - libsql::ffi::SQLITE_IOERR_READ => "SQLITE_IOERR_READ".to_owned(), - libsql::ffi::SQLITE_IOERR_SHORT_READ => "SQLITE_IOERR_SHORT_READ".to_owned(), - libsql::ffi::SQLITE_IOERR_WRITE => "SQLITE_IOERR_WRITE".to_owned(), - libsql::ffi::SQLITE_IOERR_FSYNC => "SQLITE_IOERR_FSYNC".to_owned(), - libsql::ffi::SQLITE_IOERR_DIR_FSYNC => "SQLITE_IOERR_DIR_FSYNC".to_owned(), - libsql::ffi::SQLITE_IOERR_TRUNCATE => "SQLITE_IOERR_TRUNCATE".to_owned(), - libsql::ffi::SQLITE_IOERR_FSTAT => "SQLITE_IOERR_FSTAT".to_owned(), - libsql::ffi::SQLITE_IOERR_UNLOCK => "SQLITE_IOERR_UNLOCK".to_owned(), - libsql::ffi::SQLITE_IOERR_RDLOCK => "SQLITE_IOERR_RDLOCK".to_owned(), - libsql::ffi::SQLITE_IOERR_DELETE => "SQLITE_IOERR_DELETE".to_owned(), - libsql::ffi::SQLITE_IOERR_BLOCKED => "SQLITE_IOERR_BLOCKED".to_owned(), - libsql::ffi::SQLITE_IOERR_NOMEM => "SQLITE_IOERR_NOMEM".to_owned(), - libsql::ffi::SQLITE_IOERR_ACCESS => "SQLITE_IOERR_ACCESS".to_owned(), - libsql::ffi::SQLITE_IOERR_CHECKRESERVEDLOCK => "SQLITE_IOERR_CHECKRESERVEDLOCK".to_owned(), - libsql::ffi::SQLITE_IOERR_LOCK => "SQLITE_IOERR_LOCK".to_owned(), - libsql::ffi::SQLITE_IOERR_CLOSE => "SQLITE_IOERR_CLOSE".to_owned(), - libsql::ffi::SQLITE_IOERR_DIR_CLOSE => "SQLITE_IOERR_DIR_CLOSE".to_owned(), - libsql::ffi::SQLITE_IOERR_SHMOPEN => "SQLITE_IOERR_SHMOPEN".to_owned(), - libsql::ffi::SQLITE_IOERR_SHMSIZE => "SQLITE_IOERR_SHMSIZE".to_owned(), - libsql::ffi::SQLITE_IOERR_SHMLOCK => "SQLITE_IOERR_SHMLOCK".to_owned(), - libsql::ffi::SQLITE_IOERR_SHMMAP => "SQLITE_IOERR_SHMMAP".to_owned(), - libsql::ffi::SQLITE_IOERR_SEEK => "SQLITE_IOERR_SEEK".to_owned(), - libsql::ffi::SQLITE_IOERR_DELETE_NOENT => "SQLITE_IOERR_DELETE_NOENT".to_owned(), - libsql::ffi::SQLITE_IOERR_MMAP => "SQLITE_IOERR_MMAP".to_owned(), - libsql::ffi::SQLITE_IOERR_GETTEMPPATH => "SQLITE_IOERR_GETTEMPPATH".to_owned(), - libsql::ffi::SQLITE_IOERR_CONVPATH => "SQLITE_IOERR_CONVPATH".to_owned(), - libsql::ffi::SQLITE_IOERR_VNODE => "SQLITE_IOERR_VNODE".to_owned(), - libsql::ffi::SQLITE_IOERR_AUTH => "SQLITE_IOERR_AUTH".to_owned(), - libsql::ffi::SQLITE_LOCKED_SHAREDCACHE => "SQLITE_LOCKED_SHAREDCACHE".to_owned(), - libsql::ffi::SQLITE_BUSY_RECOVERY => "SQLITE_BUSY_RECOVERY".to_owned(), - libsql::ffi::SQLITE_BUSY_SNAPSHOT => "SQLITE_BUSY_SNAPSHOT".to_owned(), - libsql::ffi::SQLITE_CANTOPEN_NOTEMPDIR => "SQLITE_CANTOPEN_NOTEMPDIR".to_owned(), - libsql::ffi::SQLITE_CANTOPEN_ISDIR => "SQLITE_CANTOPEN_ISDIR".to_owned(), - libsql::ffi::SQLITE_CANTOPEN_FULLPATH => "SQLITE_CANTOPEN_FULLPATH".to_owned(), - libsql::ffi::SQLITE_CANTOPEN_CONVPATH => "SQLITE_CANTOPEN_CONVPATH".to_owned(), - libsql::ffi::SQLITE_CORRUPT_VTAB => "SQLITE_CORRUPT_VTAB".to_owned(), - libsql::ffi::SQLITE_READONLY_RECOVERY => "SQLITE_READONLY_RECOVERY".to_owned(), - libsql::ffi::SQLITE_READONLY_CANTLOCK => "SQLITE_READONLY_CANTLOCK".to_owned(), - libsql::ffi::SQLITE_READONLY_ROLLBACK => "SQLITE_READONLY_ROLLBACK".to_owned(), - libsql::ffi::SQLITE_READONLY_DBMOVED => "SQLITE_READONLY_DBMOVED".to_owned(), - libsql::ffi::SQLITE_ABORT_ROLLBACK => "SQLITE_ABORT_ROLLBACK".to_owned(), - libsql::ffi::SQLITE_CONSTRAINT_CHECK => "SQLITE_CONSTRAINT_CHECK".to_owned(), - libsql::ffi::SQLITE_CONSTRAINT_COMMITHOOK => "SQLITE_CONSTRAINT_COMMITHOOK".to_owned(), - libsql::ffi::SQLITE_CONSTRAINT_FOREIGNKEY => "SQLITE_CONSTRAINT_FOREIGNKEY".to_owned(), - libsql::ffi::SQLITE_CONSTRAINT_FUNCTION => "SQLITE_CONSTRAINT_FUNCTION".to_owned(), - libsql::ffi::SQLITE_CONSTRAINT_NOTNULL => "SQLITE_CONSTRAINT_NOTNULL".to_owned(), - libsql::ffi::SQLITE_CONSTRAINT_PRIMARYKEY => "SQLITE_CONSTRAINT_PRIMARYKEY".to_owned(), - libsql::ffi::SQLITE_CONSTRAINT_TRIGGER => "SQLITE_CONSTRAINT_TRIGGER".to_owned(), - libsql::ffi::SQLITE_CONSTRAINT_UNIQUE => "SQLITE_CONSTRAINT_UNIQUE".to_owned(), - libsql::ffi::SQLITE_CONSTRAINT_VTAB => "SQLITE_CONSTRAINT_VTAB".to_owned(), - libsql::ffi::SQLITE_CONSTRAINT_ROWID => "SQLITE_CONSTRAINT_ROWID".to_owned(), - libsql::ffi::SQLITE_NOTICE_RECOVER_WAL => "SQLITE_NOTICE_RECOVER_WAL".to_owned(), - libsql::ffi::SQLITE_NOTICE_RECOVER_ROLLBACK => "SQLITE_NOTICE_RECOVER_ROLLBACK".to_owned(), - libsql::ffi::SQLITE_WARNING_AUTOINDEX => "SQLITE_WARNING_AUTOINDEX".to_owned(), - libsql::ffi::SQLITE_AUTH_USER => "SQLITE_AUTH_USER".to_owned(), - libsql::ffi::SQLITE_OK_LOAD_PERMANENTLY => "SQLITE_OK_LOAD_PERMANENTLY".to_owned(), - _ => format!("UNKNOWN_SQLITE_ERROR_{}", code), - } -} diff --git a/src/lib.rs b/src/lib.rs index 16b8b1f..1b0b98c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,65 +1,1254 @@ +//! # libsql-js +//! +//! A wrapper around the libSQL library for use in Node, Bun, and Deno. +//! +//! ## Design +//! +//! This JavaScript API is designed to be a drop-in replacement for `better-sqlite3` +//! with an opt-in async variant of the API. +//! +//! The API has two main classes: `Database` and `Statement`. The `Database` class +//! is a wrapper around libSQL `Database` and `Connection` structs whereas the +//! `Statement` class is a wrapper around libSQL `Statement` struct. +//! +//! As the `libsql` crate is async, the core of `libsql-js` is also implemented as such. +//! To support the synchronous semantics of `better-sqlite3`, the native API exposes +//! functions that are synchronous and block the event loop using Tokio's runtime. However, +//! the `promise` API module returns promises using `napi-rs` `Env::execute_tokio_future`. + +#![deny(clippy::all)] +#![allow(non_snake_case)] +#![allow(deprecated)] + mod auth; -mod database; -mod errors; -mod statement; -use crate::database::Database; -use crate::statement::{Rows, Statement}; -use neon::prelude::*; +use napi::{ + bindgen_prelude::{Array, FromNapiValue, ToNapiValue}, + Env, JsUnknown, Result, ValueType, +}; +use napi_derive::napi; use once_cell::sync::OnceCell; -use tokio::runtime::Runtime; -use tracing::level_filters::LevelFilter; -use tracing_subscriber::EnvFilter; +use std::{ + str::FromStr, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, +}; +use tokio::{runtime::Runtime, sync::Mutex}; +use tracing_subscriber::{filter::LevelFilter, EnvFilter}; + +/// SQLite error object. +#[napi] +pub struct SqliteError { + #[napi] + pub message: String, + #[napi] + pub code: String, + #[napi(js_name = rawCode)] + pub raw_code: i32, +} + +struct Error(libsql::Error); + +impl From for napi::Error { + fn from(error: Error) -> Self { + use libsql::Error as E; + match &error.0 { + E::SqliteFailure(raw_code, msg) => { + let code = map_sqlite_code(*raw_code); + if *raw_code == libsql::ffi::SQLITE_AUTH { + let err_json = serde_json::json!({ + "message": "Authorization denied by JS authorizer", + "libsqlError": true, + "code": code, + "rawCode": *raw_code + }); + napi::Error::from_reason(err_json.to_string()) + } else { + throw_sqlite_error(msg.clone(), code, *raw_code) + } + } + other => { + let err_json = serde_json::json!({ + "message": other.to_string(), + "libsqlError": true, + "code": "SQLITE_ERROR", + "rawCode": 1 + }); + napi::Error::from_reason(err_json.to_string()) + } + } + } +} + +fn map_sqlite_code(code: i32) -> String { + match code { + libsql::ffi::SQLITE_OK => "SQLITE_OK".to_owned(), + libsql::ffi::SQLITE_ERROR => "SQLITE_ERROR".to_owned(), + libsql::ffi::SQLITE_INTERNAL => "SQLITE_INTERNAL".to_owned(), + libsql::ffi::SQLITE_PERM => "SQLITE_PERM".to_owned(), + libsql::ffi::SQLITE_ABORT => "SQLITE_ABORT".to_owned(), + libsql::ffi::SQLITE_BUSY => "SQLITE_BUSY".to_owned(), + libsql::ffi::SQLITE_LOCKED => "SQLITE_LOCKED".to_owned(), + libsql::ffi::SQLITE_NOMEM => "SQLITE_NOMEM".to_owned(), + libsql::ffi::SQLITE_READONLY => "SQLITE_READONLY".to_owned(), + libsql::ffi::SQLITE_INTERRUPT => "SQLITE_INTERRUPT".to_owned(), + libsql::ffi::SQLITE_IOERR => "SQLITE_IOERR".to_owned(), + libsql::ffi::SQLITE_CORRUPT => "SQLITE_CORRUPT".to_owned(), + libsql::ffi::SQLITE_NOTFOUND => "SQLITE_NOTFOUND".to_owned(), + libsql::ffi::SQLITE_FULL => "SQLITE_FULL".to_owned(), + libsql::ffi::SQLITE_CANTOPEN => "SQLITE_CANTOPEN".to_owned(), + libsql::ffi::SQLITE_PROTOCOL => "SQLITE_PROTOCOL".to_owned(), + libsql::ffi::SQLITE_EMPTY => "SQLITE_EMPTY".to_owned(), + libsql::ffi::SQLITE_SCHEMA => "SQLITE_SCHEMA".to_owned(), + libsql::ffi::SQLITE_TOOBIG => "SQLITE_TOOBIG".to_owned(), + libsql::ffi::SQLITE_CONSTRAINT => "SQLITE_CONSTRAINT".to_owned(), + libsql::ffi::SQLITE_MISMATCH => "SQLITE_MISMATCH".to_owned(), + libsql::ffi::SQLITE_MISUSE => "SQLITE_MISUSE".to_owned(), + libsql::ffi::SQLITE_NOLFS => "SQLITE_NOLFS".to_owned(), + libsql::ffi::SQLITE_AUTH => "SQLITE_AUTH".to_owned(), + libsql::ffi::SQLITE_FORMAT => "SQLITE_FORMAT".to_owned(), + libsql::ffi::SQLITE_RANGE => "SQLITE_RANGE".to_owned(), + libsql::ffi::SQLITE_NOTADB => "SQLITE_NOTADB".to_owned(), + libsql::ffi::SQLITE_NOTICE => "SQLITE_NOTICE".to_owned(), + libsql::ffi::SQLITE_WARNING => "SQLITE_WARNING".to_owned(), + libsql::ffi::SQLITE_ROW => "SQLITE_ROW".to_owned(), + libsql::ffi::SQLITE_DONE => "SQLITE_DONE".to_owned(), + libsql::ffi::SQLITE_IOERR_READ => "SQLITE_IOERR_READ".to_owned(), + libsql::ffi::SQLITE_IOERR_SHORT_READ => "SQLITE_IOERR_SHORT_READ".to_owned(), + libsql::ffi::SQLITE_IOERR_WRITE => "SQLITE_IOERR_WRITE".to_owned(), + libsql::ffi::SQLITE_IOERR_FSYNC => "SQLITE_IOERR_FSYNC".to_owned(), + libsql::ffi::SQLITE_IOERR_DIR_FSYNC => "SQLITE_IOERR_DIR_FSYNC".to_owned(), + libsql::ffi::SQLITE_IOERR_TRUNCATE => "SQLITE_IOERR_TRUNCATE".to_owned(), + libsql::ffi::SQLITE_IOERR_FSTAT => "SQLITE_IOERR_FSTAT".to_owned(), + libsql::ffi::SQLITE_IOERR_UNLOCK => "SQLITE_IOERR_UNLOCK".to_owned(), + libsql::ffi::SQLITE_IOERR_RDLOCK => "SQLITE_IOERR_RDLOCK".to_owned(), + libsql::ffi::SQLITE_IOERR_DELETE => "SQLITE_IOERR_DELETE".to_owned(), + libsql::ffi::SQLITE_IOERR_BLOCKED => "SQLITE_IOERR_BLOCKED".to_owned(), + libsql::ffi::SQLITE_IOERR_NOMEM => "SQLITE_IOERR_NOMEM".to_owned(), + libsql::ffi::SQLITE_IOERR_ACCESS => "SQLITE_IOERR_ACCESS".to_owned(), + libsql::ffi::SQLITE_IOERR_CHECKRESERVEDLOCK => "SQLITE_IOERR_CHECKRESERVEDLOCK".to_owned(), + libsql::ffi::SQLITE_IOERR_LOCK => "SQLITE_IOERR_LOCK".to_owned(), + libsql::ffi::SQLITE_IOERR_CLOSE => "SQLITE_IOERR_CLOSE".to_owned(), + libsql::ffi::SQLITE_IOERR_DIR_CLOSE => "SQLITE_IOERR_DIR_CLOSE".to_owned(), + libsql::ffi::SQLITE_IOERR_SHMOPEN => "SQLITE_IOERR_SHMOPEN".to_owned(), + libsql::ffi::SQLITE_IOERR_SHMSIZE => "SQLITE_IOERR_SHMSIZE".to_owned(), + libsql::ffi::SQLITE_IOERR_SHMLOCK => "SQLITE_IOERR_SHMLOCK".to_owned(), + libsql::ffi::SQLITE_IOERR_SHMMAP => "SQLITE_IOERR_SHMMAP".to_owned(), + libsql::ffi::SQLITE_IOERR_SEEK => "SQLITE_IOERR_SEEK".to_owned(), + libsql::ffi::SQLITE_IOERR_DELETE_NOENT => "SQLITE_IOERR_DELETE_NOENT".to_owned(), + libsql::ffi::SQLITE_IOERR_MMAP => "SQLITE_IOERR_MMAP".to_owned(), + libsql::ffi::SQLITE_IOERR_GETTEMPPATH => "SQLITE_IOERR_GETTEMPPATH".to_owned(), + libsql::ffi::SQLITE_IOERR_CONVPATH => "SQLITE_IOERR_CONVPATH".to_owned(), + libsql::ffi::SQLITE_IOERR_VNODE => "SQLITE_IOERR_VNODE".to_owned(), + libsql::ffi::SQLITE_IOERR_AUTH => "SQLITE_IOERR_AUTH".to_owned(), + libsql::ffi::SQLITE_LOCKED_SHAREDCACHE => "SQLITE_LOCKED_SHAREDCACHE".to_owned(), + libsql::ffi::SQLITE_BUSY_RECOVERY => "SQLITE_BUSY_RECOVERY".to_owned(), + libsql::ffi::SQLITE_BUSY_SNAPSHOT => "SQLITE_BUSY_SNAPSHOT".to_owned(), + libsql::ffi::SQLITE_CANTOPEN_NOTEMPDIR => "SQLITE_CANTOPEN_NOTEMPDIR".to_owned(), + libsql::ffi::SQLITE_CANTOPEN_ISDIR => "SQLITE_CANTOPEN_ISDIR".to_owned(), + libsql::ffi::SQLITE_CANTOPEN_FULLPATH => "SQLITE_CANTOPEN_FULLPATH".to_owned(), + libsql::ffi::SQLITE_CANTOPEN_CONVPATH => "SQLITE_CANTOPEN_CONVPATH".to_owned(), + libsql::ffi::SQLITE_CORRUPT_VTAB => "SQLITE_CORRUPT_VTAB".to_owned(), + libsql::ffi::SQLITE_READONLY_RECOVERY => "SQLITE_READONLY_RECOVERY".to_owned(), + libsql::ffi::SQLITE_READONLY_CANTLOCK => "SQLITE_READONLY_CANTLOCK".to_owned(), + libsql::ffi::SQLITE_READONLY_ROLLBACK => "SQLITE_READONLY_ROLLBACK".to_owned(), + libsql::ffi::SQLITE_READONLY_DBMOVED => "SQLITE_READONLY_DBMOVED".to_owned(), + libsql::ffi::SQLITE_ABORT_ROLLBACK => "SQLITE_ABORT_ROLLBACK".to_owned(), + libsql::ffi::SQLITE_CONSTRAINT_CHECK => "SQLITE_CONSTRAINT_CHECK".to_owned(), + libsql::ffi::SQLITE_CONSTRAINT_COMMITHOOK => "SQLITE_CONSTRAINT_COMMITHOOK".to_owned(), + libsql::ffi::SQLITE_CONSTRAINT_FOREIGNKEY => "SQLITE_CONSTRAINT_FOREIGNKEY".to_owned(), + libsql::ffi::SQLITE_CONSTRAINT_FUNCTION => "SQLITE_CONSTRAINT_FUNCTION".to_owned(), + libsql::ffi::SQLITE_CONSTRAINT_NOTNULL => "SQLITE_CONSTRAINT_NOTNULL".to_owned(), + libsql::ffi::SQLITE_CONSTRAINT_PRIMARYKEY => "SQLITE_CONSTRAINT_PRIMARYKEY".to_owned(), + libsql::ffi::SQLITE_CONSTRAINT_TRIGGER => "SQLITE_CONSTRAINT_TRIGGER".to_owned(), + libsql::ffi::SQLITE_CONSTRAINT_UNIQUE => "SQLITE_CONSTRAINT_UNIQUE".to_owned(), + libsql::ffi::SQLITE_CONSTRAINT_VTAB => "SQLITE_CONSTRAINT_VTAB".to_owned(), + libsql::ffi::SQLITE_CONSTRAINT_ROWID => "SQLITE_CONSTRAINT_ROWID".to_owned(), + libsql::ffi::SQLITE_NOTICE_RECOVER_WAL => "SQLITE_NOTICE_RECOVER_WAL".to_owned(), + libsql::ffi::SQLITE_NOTICE_RECOVER_ROLLBACK => "SQLITE_NOTICE_RECOVER_ROLLBACK".to_owned(), + libsql::ffi::SQLITE_WARNING_AUTOINDEX => "SQLITE_WARNING_AUTOINDEX".to_owned(), + libsql::ffi::SQLITE_AUTH_USER => "SQLITE_AUTH_USER".to_owned(), + libsql::ffi::SQLITE_OK_LOAD_PERMANENTLY => "SQLITE_OK_LOAD_PERMANENTLY".to_owned(), + _ => format!("UNKNOWN_SQLITE_ERROR_{}", code), + } +} + +pub fn throw_sqlite_error(message: String, code: String, raw_code: i32) -> napi::Error { + let err_json = serde_json::json!({ + "message": message, + "libsqlError": true, + "code": code, + "rawCode": raw_code + }); + napi::Error::from_reason(err_json.to_string()) +} + +impl From for Error { + fn from(error: libsql::Error) -> Self { + Error(error) + } +} + +/// SQLite connection options. +#[napi(object)] +pub struct Options { + // Timeout in seconds. + pub timeout: Option, + // Authentication token for remote databases. + pub authToken: Option, + // URL for remote database sync. + pub syncUrl: Option, + // Sync interval in seconds. + pub syncPeriod: Option, + // Encryption cipher for local enryption at rest. + pub encryptionCipher: Option, + // Encryption key for local encryption at rest. + pub encryptionKey: Option, +} + +/// Access mode. +/// +/// The `better-sqlite3` API allows the caller to configure the format of +/// query results. This struct encapsulates the different access mode configs. +struct AccessMode { + pub(crate) raw: AtomicBool, + pub(crate) pluck: AtomicBool, + pub(crate) safe_ints: AtomicBool, + pub(crate) timing: AtomicBool, +} + +/// SQLite database connection. +#[napi] +pub struct Database { + // The libSQL database instance. + db: libsql::Database, + // The libSQL connection instance. + conn: Option>>, + // Whether to use safe integers by default. + default_safe_integers: AtomicBool, + // Whether to use memory-only mode. + memory: bool, +} + +impl Drop for Database { + fn drop(&mut self) { + self.conn = None; + } +} + +#[napi] +impl Database { + /// Creates a new database instance. + /// + /// # Arguments + /// + /// * `path` - The path to the database file. + /// * `opts` - The database options. + #[napi(constructor)] + pub fn new(path: String, opts: Option) -> Result { + ensure_logger(); + let rt = runtime()?; + let remote = is_remote_path(&path); + let db = if remote { + let auth_token = opts + .as_ref() + .and_then(|o| o.authToken.as_ref()) + .cloned() + .unwrap_or_default(); + let builder = libsql::Builder::new_remote(path.clone(), auth_token); + rt.block_on(builder.build()).map_err(Error::from)? + } else if let Some(options) = &opts { + if let Some(sync_url) = &options.syncUrl { + let auth_token = options.authToken.as_ref().cloned().unwrap_or_default(); + + let encryption_cipher: String = opts + .as_ref() + .and_then(|o| o.encryptionCipher.as_ref()) + .cloned() + .unwrap_or("aes256cbc".to_string()); + let cipher = libsql::Cipher::from_str(&encryption_cipher).map_err(|_| { + throw_sqlite_error( + "Invalid encryption cipher".to_string(), + "SQLITE_INVALID_ENCRYPTION_CIPHER".to_string(), + 0, + ) + })?; + let encryption_key = opts + .as_ref() + .and_then(|o| o.encryptionKey.as_ref()) + .cloned() + .unwrap_or("".to_string()); + + let mut builder = + libsql::Builder::new_remote_replica(path.clone(), sync_url.clone(), auth_token); + + if encryption_key.len() > 0 { + let encryption_config = + libsql::EncryptionConfig::new(cipher, encryption_key.into()); + builder = builder.encryption_config(encryption_config); + } + + if let Some(period) = options.syncPeriod { + if period > 0.0 { + builder = builder.sync_interval(std::time::Duration::from_secs_f64(period)); + } + } + + rt.block_on(builder.build()).map_err(Error::from)? + } else { + let builder = libsql::Builder::new_local(&path); + rt.block_on(builder.build()).map_err(Error::from)? + } + } else { + let builder = libsql::Builder::new_local(&path); + rt.block_on(builder.build()).map_err(Error::from)? + }; + let conn = db.connect().map_err(Error::from)?; + let default_safe_integers = AtomicBool::new(false); + let memory = path == ":memory:"; + let timeout = match opts { + Some(ref opts) => opts.timeout.unwrap_or(0.0), + None => 0.0, + }; + if timeout > 0.0 { + conn.busy_timeout(Duration::from_millis(timeout as u64)) + .map_err(Error::from)? + } + Ok(Database { + db, + conn: Some(Arc::new(Mutex::new(conn))), + default_safe_integers, + memory, + }) + } + + /// Returns whether the database is in memory-only mode. + #[napi(getter)] + pub fn memory(&self) -> bool { + self.memory + } + + /// Returns whether the database is in a transaction. + #[napi] + pub fn in_transaction(&self) -> Result { + let rt = runtime()?; + let conn = match &self.conn { + Some(conn) => conn.clone(), + None => return Ok(false), + }; + Ok(rt.block_on(async move { + let conn = conn.lock().await; + !conn.is_autocommit() + })) + } + + /// Prepares a statement for execution. + /// + /// # Arguments + /// + /// * `sql` - The SQL statement to prepare. + /// + /// # Returns + /// + /// A `Statement` instance. + #[napi] + pub async fn prepare(&self, sql: String) -> Result { + let conn = match &self.conn { + Some(conn) => conn.clone(), + None => { + return Err(throw_sqlite_error( + "The database connection is not open".to_string(), + "SQLITE_NOTOPEN".to_string(), + 0, + )); + } + }; + let stmt = { + let conn = conn.lock().await; + conn.prepare(&sql).await.map_err(Error::from)? + }; + let mode = AccessMode { + safe_ints: self.default_safe_integers.load(Ordering::SeqCst).into(), + raw: false.into(), + pluck: false.into(), + timing: false.into(), + }; + Ok(Statement::new(conn, stmt, mode)) + } + + /// Sets the authorizer for the database. + /// + /// # Arguments + /// + /// * `env` - The environment. + /// * `rules_obj` - The rules object. + /// + /// The `rules_obj` is a JavaScript object with the following properties: + /// + /// * `Authorization.ALLOW` - Allow access to the table. + /// * `Authorization.DENY` - Deny access to the table. + /// + /// Example: + /// + /// ```javascript + /// db.authorizer({ + /// "users": Authorization.ALLOW + /// }); + /// ``` + #[napi] + pub fn authorizer(&self, env: Env, rules_obj: napi::JsObject) -> Result<()> { + let conn = match &self.conn { + Some(c) => c.clone(), + None => { + return Err(throw_database_closed_error(&env).into()); + } + }; + let mut builder = crate::auth::AuthorizerBuilder::new(); + let prop_names = rules_obj.get_property_names()?; + let len = prop_names.get_array_length()?; + for idx in 0..len { + let key_js: napi::JsString = prop_names.get_element::(idx)?; + let key = key_js.into_utf8()?.into_owned()?; + let value_js: napi::JsNumber = rules_obj.get_named_property(&key)?; + let value = value_js.get_int32()?; + match value { + 0 => { + // Authorization.ALLOW + builder.allow(&key); + } + 1 => { + // Authorization.DENY + builder.deny(&key); + } + _ => { + let msg = format!( + "Invalid authorization rule value '{}' for table '{}'. Expected 0 (ALLOW) or 1 (DENY).", + value, key + ); + return Err(napi::Error::from_reason(msg)); + } + } + } + let authorizer = builder.build(); + let auth_arc = std::sync::Arc::new(authorizer); + let closure = { + let auth_arc = auth_arc.clone(); + move |ctx: &libsql::AuthContext| auth_arc.authorize(ctx) + }; + let rt = runtime()?; + let guard_conn = rt.block_on(async { conn.lock().await }); + guard_conn + .authorizer(Some(std::sync::Arc::new(closure))) + .map_err(Error::from)?; + Ok(()) + } + + /// Loads an extension into the database. + /// + /// # Arguments + /// + /// * `path` - The path to the extension file. + /// * `entry_point` - The entry point of the extension. + /// + #[napi] + pub fn loadExtension(&self, path: String, entry_point: Option) -> Result<()> { + let rt = runtime()?; + let conn = match &self.conn { + Some(conn) => conn.clone(), + None => { + return Err(throw_sqlite_error( + "The database connection is not open".to_string(), + "SQLITE_NOTOPEN".to_string(), + 0, + )); + } + }; + rt.block_on(async move { + let conn = conn.lock().await; + conn.load_extension_enable().map_err(Error::from)?; + if let Err(err) = conn.load_extension(&path, entry_point.as_deref()) { + let _ = conn.load_extension_disable(); + return Err(Error::from(err)); + } + conn.load_extension_disable().map_err(Error::from)?; + Ok(()) + }) + .map_err(|e| napi::Error::from(e)) + } + + /// Returns the maximum write replication index. + /// + /// # Returns + /// + /// The maximum write replication index. + #[napi] + pub fn max_write_replication_index(&self) -> Result { + let result = self.db.max_write_replication_index(); + Ok(result.unwrap_or(0) as f64) + } + + /// Executes a SQL statement. + /// + /// # Arguments + /// + /// * `env` - The environment. + /// * `sql` - The SQL statement to execute. + #[napi] + pub fn exec(&self, env: Env, sql: String) -> Result<()> { + let rt = runtime()?; + let conn = match &self.conn { + Some(conn) => conn.clone(), + None => return Err(throw_database_closed_error(&env).into()), + }; + rt.block_on(async move { + let conn = conn.lock().await; + conn.execute_batch(&sql).await + }) + .map_err(Error::from)?; + Ok(()) + } + + /// Syncs the database. + /// + /// # Returns + /// + /// A `SyncResult` instance. + #[napi] + pub async fn sync(&self) -> Result { + let result = self.db.sync().await.map_err(Error::from)?; + Ok(SyncResult { + frames_synced: result.frames_synced() as f64, + replication_index: result.frame_no().unwrap_or(0) as f64, + }) + } + + /// Interrupts any ongoing database operations. + /// + /// # Arguments + /// + /// * `env` - The environment. + #[napi] + pub fn interrupt(&self, env: Env) -> Result<()> { + let rt = runtime()?; + let conn = match &self.conn { + Some(conn) => conn.clone(), + None => return Err(throw_database_closed_error(&env).into()), + }; + rt.block_on(async move { + let conn = conn.lock().await; + conn.interrupt() + }) + .map_err(Error::from)?; + Ok(()) + } + + /// Closes the database connection. + #[napi] + pub fn close(&mut self) -> Result<()> { + self.conn = None; + Ok(()) + } + + /// Sets the default safe integers mode. + /// + /// # Arguments + /// + /// * `toggle` - Whether to use safe integers by default. + #[napi] + pub fn defaultSafeIntegers(&self, toggle: Option) -> Result<()> { + self.default_safe_integers + .store(toggle.unwrap_or(true), Ordering::SeqCst); + Ok(()) + } +} + +/// Result of a database sync operation. +#[napi(object)] +pub struct SyncResult { + /// The number of frames synced. + pub frames_synced: f64, + /// The replication index. + pub replication_index: f64, +} + +/// Prepares a statement in blocking mode. +#[napi] +pub fn database_prepare_sync(db: &Database, sql: String) -> Result { + let rt = runtime()?; + rt.block_on(async move { db.prepare(sql).await }) +} + +/// Syncs the database in blocking mode. +#[napi] +pub fn database_sync_sync(db: &Database) -> Result { + let rt = runtime()?; + rt.block_on(async move { db.sync().await }) +} + +fn is_remote_path(path: &str) -> bool { + path.starts_with("libsql://") || path.starts_with("http://") || path.starts_with("https://") +} + +fn throw_database_closed_error(env: &Env) -> napi::Error { + let msg = "The database connection is not open"; + let err = napi::Error::new(napi::Status::InvalidArg, msg.to_string()); + env.throw_type_error(msg, None).unwrap(); + err +} + +/// SQLite statement object. +#[napi] +pub struct Statement { + // The libSQL connection instance. + conn: Arc>, + // The libSQL statement instance. + stmt: Arc>, + // The column names. + column_names: Vec, + // The access mode. + mode: AccessMode, +} + +#[napi] +impl Statement { + /// Creates a new statement instance. + /// + /// # Arguments + /// + /// * `conn` - The connection instance. + /// * `stmt` - The libSQL statement instance. + /// * `mode` - The access mode. + pub(crate) fn new( + conn: Arc>, + stmt: libsql::Statement, + mode: AccessMode, + ) -> Self { + let column_names: Vec = stmt + .columns() + .iter() + .map(|c| std::ffi::CString::new(c.name().to_string()).unwrap()) + .collect(); + let stmt = Arc::new(tokio::sync::Mutex::new(stmt)); + Self { + conn, + stmt, + column_names, + mode, + } + } + + /// Executes a SQL statement. + /// + /// # Arguments + /// + /// * `params` - The parameters to bind to the statement. + #[napi] + pub fn run(&self, params: Option) -> Result { + let rt = runtime()?; + rt.block_on(async move { + let conn = self.conn.lock().await; + let total_changes_before = conn.total_changes(); + let start = std::time::Instant::now(); + + let mut stmt = self.stmt.lock().await; + stmt.reset(); + let params = map_params(&stmt, params)?; + stmt.query(params).await.map_err(Error::from)?; + let changes = if conn.total_changes() == total_changes_before { + 0 + } else { + conn.changes() + }; + let last_insert_row_id = conn.last_insert_rowid(); + let duration = start.elapsed().as_secs_f64(); + Ok(RunResult { + changes: changes as f64, + duration, + lastInsertRowid: last_insert_row_id, + }) + }) + } + + /// Executes a SQL statement and returns the first row. + /// + /// # Arguments + /// + /// * `env` - The environment. + /// * `params` - The parameters to bind to the statement. + #[napi] + pub fn get(&self, env: Env, params: Option) -> Result { + let rt = runtime()?; + + let safe_ints = self.mode.safe_ints.load(Ordering::SeqCst); + let raw = self.mode.raw.load(Ordering::SeqCst); + let pluck = self.mode.pluck.load(Ordering::SeqCst); + let timed = self.mode.timing.load(Ordering::SeqCst); + + let start = if timed { + Some(std::time::Instant::now()) + } else { + None + }; + rt.block_on(async move { + let mut stmt = self.stmt.lock().await; + let params = map_params(&stmt, params)?; + let mut rows = stmt.query(params).await.map_err(Error::from)?; + let row = rows.next().await.map_err(Error::from)?; + let duration: Option = start.map(|start| start.elapsed().as_secs_f64()); + let result = Self::get_internal( + &env, + &row, + &self.column_names, + safe_ints, + raw, + pluck, + duration, + ); + stmt.reset(); + result + }) + } + + fn get_internal( + env: &Env, + row: &Option, + column_names: &[std::ffi::CString], + safe_ints: bool, + raw: bool, + pluck: bool, + duration: Option, + ) -> Result { + match row { + Some(row) => { + if raw { + let js_array = map_row_raw(&env, &column_names, &row, safe_ints, pluck)?; + Ok(js_array.into_unknown()) + } else { + let mut js_object = + map_row_object(&env, &column_names, &row, safe_ints, pluck)? + .coerce_to_object()?; + if let Some(duration) = duration { + let mut metadata = env.create_object()?; + let js_duration = env.create_double(duration)?; + metadata.set_named_property("duration", js_duration)?; + js_object.set_named_property("_metadata", metadata)?; + } + Ok(js_object.into_unknown()) + } + } + None => { + let undefined = env.get_undefined()?; + Ok(undefined.into_unknown()) + } + } + } + + /// Create an iterator over the rows of a statement. + /// + /// # Arguments + /// + /// * `env` - The environment. + /// * `params` - The parameters to bind to the statement. + #[napi] + pub fn iterate(&self, env: Env, params: Option) -> Result { + let rt = runtime()?; + let safe_ints = self.mode.safe_ints.load(Ordering::SeqCst); + let raw = self.mode.raw.load(Ordering::SeqCst); + let pluck = self.mode.pluck.load(Ordering::SeqCst); + let stmt = self.stmt.clone(); + let params = { + let stmt = stmt.clone(); + rt.block_on(async move { + let mut stmt = stmt.lock().await; + stmt.reset(); + map_params(&stmt, params).unwrap() + }) + }; + let stmt = self.stmt.clone(); + let future = async move { + let rows = stmt.lock().await.query(params).await.map_err(Error::from)?; + Ok::<_, napi::Error>(rows) + }; + let column_names = self.column_names.clone(); + env.execute_tokio_future(future, move |&mut _env, result| { + Ok(RowsIterator::new( + Arc::new(tokio::sync::Mutex::new(result)), + column_names, + safe_ints, + raw, + pluck, + )) + }) + } -fn runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static Runtime> { + #[napi] + pub fn raw(&self, raw: Option) -> Result<&Self> { + let rt = runtime()?; + let returns_data = rt.block_on(async move { + let stmt = self.stmt.lock().await; + !stmt.columns().is_empty() + }); + if !returns_data { + return Err(napi::Error::from_reason( + "The raw() method is only for statements that return data", + )); + } + self.mode.raw.store(raw.unwrap_or(true), Ordering::SeqCst); + Ok(self) + } + + #[napi] + pub fn pluck(&self, pluck: Option) -> Result<&Self> { + self.mode + .pluck + .store(pluck.unwrap_or(true), Ordering::SeqCst); + Ok(self) + } + + #[napi] + pub fn timing(&self, timing: Option) -> Result<&Self> { + self.mode + .timing + .store(timing.unwrap_or(true), Ordering::SeqCst); + Ok(self) + } + + #[napi] + pub fn columns(&self, env: Env) -> Result { + let rt = runtime()?; + rt.block_on(async move { + let stmt = self.stmt.lock().await; + let columns = stmt.columns(); + let mut js_array = env.create_array(columns.len() as u32)?; + for (i, col) in columns.iter().enumerate() { + let mut js_obj = env.create_object()?; + js_obj.set_named_property("name", env.create_string(col.name())?)?; + // origin_name -> column + if let Some(origin_name) = col.origin_name() { + js_obj.set_named_property("column", env.create_string(origin_name)?)?; + } else { + js_obj.set_named_property("column", env.get_null()?)?; + } + // table_name -> table + if let Some(table_name) = col.table_name() { + js_obj.set_named_property("table", env.create_string(table_name)?)?; + } else { + js_obj.set_named_property("table", env.get_null()?)?; + } + // database_name -> database + if let Some(database_name) = col.database_name() { + js_obj.set_named_property("database", env.create_string(database_name)?)?; + } else { + js_obj.set_named_property("database", env.get_null()?)?; + } + // decl_type -> type + if let Some(decl_type) = col.decl_type() { + js_obj.set_named_property("type", env.create_string(decl_type)?)?; + } else { + js_obj.set_named_property("type", env.get_null()?)?; + } + js_array.set(i as u32, js_obj)?; + } + Ok(js_array) + }) + } + + #[napi] + pub fn safeIntegers(&self, toggle: Option) -> Result<&Self> { + self.mode + .safe_ints + .store(toggle.unwrap_or(true), Ordering::SeqCst); + Ok(self) + } + + #[napi] + pub fn interrupt(&self) -> Result<()> { + let rt = runtime()?; + rt.block_on(async move { + let mut stmt = self.stmt.lock().await; + stmt.interrupt() + }) + .map_err(Error::from)?; + Ok(()) + } +} + + +#[napi] +pub fn statement_iterate_sync(stmt: &Statement, _env: Env, params: Option) -> Result { + let rt = runtime()?; + let safe_ints = stmt.mode.safe_ints.load(Ordering::SeqCst); + let raw = stmt.mode.raw.load(Ordering::SeqCst); + let pluck = stmt.mode.pluck.load(Ordering::SeqCst); + let stmt = stmt.stmt.clone(); + let (rows, column_names) = rt.block_on(async move { + let mut stmt = stmt.lock().await; + stmt.reset(); + let params = map_params(&stmt, params)?; + let rows = stmt.query(params).await.map_err(Error::from)?; + let mut column_names = Vec::new(); + for i in 0..rows.column_count() { + column_names.push( + std::ffi::CString::new(rows.column_name(i).unwrap().to_string()).unwrap(), + ); + } + Ok::<_, napi::Error>((rows, column_names)) + })?; + Ok(RowsIterator::new( + Arc::new(tokio::sync::Mutex::new(rows)), + column_names, + safe_ints, + raw, + pluck, + )) +} + +/// SQLite `run()` result object +#[napi(object)] +pub struct RunResult { + pub changes: f64, + pub duration: f64, + pub lastInsertRowid: i64, +} + +fn map_params( + stmt: &libsql::Statement, + params: Option, +) -> Result { + if let Some(params) = params { + match params.get_type()? { + ValueType::Object => { + let object = params.coerce_to_object()?; + if object.is_array()? { + map_params_array(object) + } else { + map_params_object(stmt, object) + } + } + _ => map_params_single(params), + } + } else { + Ok(libsql::params::Params::None) + } +} + +fn map_params_single(param: napi::JsUnknown) -> Result { + Ok(libsql::params::Params::Positional(vec![map_value(param)?])) +} + +fn map_params_array(object: napi::JsObject) -> Result { + let mut params = vec![]; + let length = object.get_array_length()?; + for i in 0..length { + let element = object.get_element::(i)?; + let value = map_value(element)?; + params.push(value); + } + Ok(libsql::params::Params::Positional(params)) +} + +fn map_params_object( + stmt: &libsql::Statement, + object: napi::JsObject, +) -> Result { + let mut params = vec![]; + for idx in 0..stmt.parameter_count() { + let name = stmt.parameter_name((idx + 1) as i32).unwrap(); + let name = name.to_string(); + // Remove the leading ':' or '@' or '$' from parameter name + let key = &name[1..]; + if let Ok(value) = object.get_named_property::(key) { + let value = map_value(value)?; + params.push((name, value)); + } + } + Ok(libsql::params::Params::Named(params)) +} + +/// Maps a JavaScript value to libSQL value types. +fn map_value(value: JsUnknown) -> Result { + let value_type = value.get_type()?; + + match value_type { + ValueType::Null | ValueType::Undefined => Ok(libsql::Value::Null), + + ValueType::Boolean => { + let js_bool = value.coerce_to_bool()?; + let b = js_bool.get_value()?; + Ok(libsql::Value::Integer(if b { 1 } else { 0 })) + } + + ValueType::Number => { + let js_num = value.coerce_to_number()?; + let n = js_num.get_double()?; + Ok(libsql::Value::Real(n)) + } + + ValueType::BigInt => { + let js_bigint = napi::JsBigInt::from_unknown(value)?; + let (v, lossless) = js_bigint.get_i64()?; + if !lossless { + return Err(napi::Error::from_reason( + "BigInt value is out of range for SQLite INTEGER (i64)", + )); + } + Ok(libsql::Value::Integer(v)) + } + + ValueType::String => { + let js_str = value.coerce_to_string()?; + let utf8 = js_str.into_utf8()?; + // into_utf8 returns a Utf8 object that derefs to str + Ok(libsql::Value::Text(utf8.as_str()?.to_owned())) + } + + ValueType::Object => { + let obj = value.coerce_to_object()?; + + // Check if it's a buffer + if obj.is_buffer()? { + let buf = napi::JsBuffer::try_from(obj.into_unknown())?; + let data = buf.into_value()?.to_vec(); + return Ok(libsql::Value::Blob(data)); + } + + if obj.is_typedarray()? { + let js_typed = napi::JsTypedArray::try_from(obj.into_unknown())?; + let typed_array_value = js_typed.into_value()?; + + let buffer_data = typed_array_value.arraybuffer.into_value()?; + let start = typed_array_value.byte_offset; + let end = start + typed_array_value.length; + + if end > buffer_data.len() { + return Err(napi::Error::from_reason("TypedArray length out of bounds")); + } + + let slice = &buffer_data[start..end]; + return Ok(libsql::Value::Blob(slice.to_vec())); + } + Err(napi::Error::from_reason( + "SQLite3 can only bind numbers, strings, bigints, buffers, and null", + )) + } + + _ => Err(napi::Error::from_reason( + "SQLite3 can only bind numbers, strings, bigints, buffers, and null", + )), + } +} + +/// A raw iterator over rows. The JavaScript layer wraps this in a iterable. +#[napi] +pub struct RowsIterator { + rows: Arc>, + column_names: Vec, + safe_ints: bool, + raw: bool, + pluck: bool, +} + +#[napi] +impl RowsIterator { + pub fn new( + rows: Arc>, + column_names: Vec, + safe_ints: bool, + raw: bool, + pluck: bool, + ) -> Self { + Self { + rows, + column_names, + safe_ints, + raw, + pluck, + } + } + + #[napi] + pub async fn next(&self) -> Result { + let mut rows = self.rows.lock().await; + let row = rows.next().await.map_err(Error::from)?; + Ok(Record { + row, + column_names: self.column_names.clone(), + safe_ints: self.safe_ints, + raw: self.raw, + pluck: self.pluck, + }) + } +} + +/// Retrieve next row from an iterator synchronously. Needed for better-sqlite3 API compatibility. +#[napi] +pub fn iterator_next_sync(iter: &RowsIterator) -> Result { + let rt = runtime()?; + rt.block_on(async move { iter.next().await }) +} + +#[napi] +pub struct Record { + row: Option, + column_names: Vec, + safe_ints: bool, + raw: bool, + pluck: bool, +} + +#[napi] +impl Record { + #[napi(getter)] + pub fn value(&self, env: Env) -> napi::Result { + if let Some(row) = &self.row { + Ok(map_row( + &env, + &self.column_names, + &row, + self.safe_ints, + self.raw, + self.pluck, + )?) + } else { + Ok(env.get_null()?.into_unknown()) + } + } + + #[napi(getter)] + pub fn done(&self) -> bool { + self.row.is_none() + } +} + +fn runtime() -> Result<&'static Runtime> { static RUNTIME: OnceCell = OnceCell::new(); - RUNTIME - .get_or_try_init(Runtime::new) - .or_else(|err| cx.throw_error(&err.to_string())) -} - -#[neon::main] -fn main(mut cx: ModuleContext) -> NeonResult<()> { - let _ = tracing_subscriber::fmt::fmt() - .with_env_filter( - EnvFilter::builder() - .with_default_directive(LevelFilter::ERROR.into()) - .from_env_lossy(), - ) - .try_init(); - cx.export_function("databaseOpen", Database::js_open)?; - cx.export_function("databaseOpenWithSync", Database::js_open_with_sync)?; - cx.export_function("databaseInTransaction", Database::js_in_transaction)?; - cx.export_function("databaseInterrupt", Database::js_interrupt)?; - cx.export_function("databaseClose", Database::js_close)?; - cx.export_function("databaseSyncSync", Database::js_sync_sync)?; - cx.export_function("databaseSyncAsync", Database::js_sync_async)?; - cx.export_function("databaseSyncUntilSync", Database::js_sync_until_sync)?; - cx.export_function("databaseSyncUntilAsync", Database::js_sync_until_async)?; - cx.export_function("databaseExecSync", Database::js_exec_sync)?; - cx.export_function("databaseExecAsync", Database::js_exec_async)?; - cx.export_function("databasePrepareSync", Database::js_prepare_sync)?; - cx.export_function("databasePrepareAsync", Database::js_prepare_async)?; - cx.export_function( - "databaseDefaultSafeIntegers", - Database::js_default_safe_integers, - )?; - cx.export_function("databaseAuthorizer", Database::js_authorizer)?; - cx.export_function("databaseLoadExtension", Database::js_load_extension)?; - cx.export_function( - "databaseMaxWriteReplicationIndex", - Database::js_max_write_replication_index, - )?; - cx.export_function("statementRaw", Statement::js_raw)?; - cx.export_function("statementIsReader", Statement::js_is_reader)?; - cx.export_function("statementRun", Statement::js_run)?; - cx.export_function("statementInterrupt", Statement::js_interrupt)?; - cx.export_function("statementGet", Statement::js_get)?; - cx.export_function("statementRowsSync", Statement::js_rows_sync)?; - cx.export_function("statementRowsAsync", Statement::js_rows_async)?; - cx.export_function("statementColumns", Statement::js_columns)?; - cx.export_function("statementSafeIntegers", Statement::js_safe_integers)?; - cx.export_function("rowsNext", Rows::js_next)?; - Ok(()) + let rt = RUNTIME.get_or_try_init(Runtime::new).unwrap(); + Ok(rt) +} + +fn map_row( + env: &Env, + column_names: &[std::ffi::CString], + row: &libsql::Row, + safe_ints: bool, + raw: bool, + pluck: bool, +) -> Result { + let result = if raw { + map_row_raw(env, column_names, row, safe_ints, pluck)? + } else { + map_row_object(env, column_names, row, safe_ints, pluck)?.into_unknown() + }; + Ok(result) +} + +fn convert_value_to_js( + env: &Env, + value: &libsql::Value, + safe_ints: bool, +) -> Result { + match value { + libsql::Value::Null => Ok(env.get_null()?.into_unknown()), + libsql::Value::Integer(v) => { + if safe_ints { + Ok(env.create_bigint_from_i64(*v)?.into_unknown()?) + } else { + Ok(env.create_double(*v as f64)?.into_unknown()) + } + } + libsql::Value::Real(v) => Ok(env.create_double(*v)?.into_unknown()), + libsql::Value::Text(v) => Ok(env.create_string(v)?.into_unknown()), + libsql::Value::Blob(v) => Ok(env.create_buffer_with_data(v.clone())?.into_unknown()), + } +} + +fn map_row_object( + env: &Env, + column_names: &[std::ffi::CString], + row: &libsql::Row, + safe_ints: bool, + pluck: bool, +) -> Result { + let column_count = column_names.len(); + + let result = if pluck { + if column_count > 0 { + let value = match row.get_value(0) { + Ok(v) => v, + Err(e) => return Err(napi::Error::from_reason(e.to_string())), + }; + convert_value_to_js(env, &value, safe_ints)? + } else { + env.get_null()?.into_unknown() + } + } else { + let result = env.create_object()?; + let result = unsafe { napi::JsObject::to_napi_value(env.raw(), result)? }; + // If not plucking, get all columns + for idx in 0..column_count { + let value = match row.get_value(idx as i32) { + Ok(v) => v, + Err(e) => return Err(napi::Error::from_reason(e.to_string())), + }; + + let column_name = &column_names[idx]; + let js_value = convert_value_to_js(env, &value, safe_ints)?; + unsafe { + napi::sys::napi_set_named_property( + env.raw(), + result, + column_name.as_ptr(), + napi::JsUnknown::to_napi_value(env.raw(), js_value)?, + ); + } + } + let result: napi::JsObject = unsafe { napi::JsObject::from_napi_value(env.raw(), result)? }; + result.into_unknown() + }; + Ok(result) +} + +fn map_row_raw( + env: &Env, + column_names: &[std::ffi::CString], + row: &libsql::Row, + safe_ints: bool, + pluck: bool, +) -> Result { + if pluck { + let value = match row.get_value(0) { + Ok(v) => convert_value_to_js(env, &v, safe_ints)?, + Err(_) => env.get_null()?.into_unknown(), + }; + return Ok(value); + } + let column_count = column_names.len(); + let mut arr = env.create_array(column_count as u32)?; + for idx in 0..column_count { + let value = match row.get_value(idx as i32) { + Ok(v) => v, + Err(e) => return Err(napi::Error::from_reason(e.to_string())), + }; + let js_value = convert_value_to_js(env, &value, safe_ints)?; + arr.set(idx as u32, js_value)?; + } + Ok(arr.coerce_to_object()?.into_unknown()) +} + +static LOGGER_INIT: OnceCell<()> = OnceCell::new(); + +fn ensure_logger() { + LOGGER_INIT.get_or_init(|| { + let _ = tracing_subscriber::fmt::fmt() + .with_env_filter( + EnvFilter::builder() + .with_default_directive(LevelFilter::ERROR.into()) + .from_env_lossy(), + ) + .try_init(); + }); } diff --git a/src/statement.rs b/src/statement.rs deleted file mode 100644 index 10214ca..0000000 --- a/src/statement.rs +++ /dev/null @@ -1,462 +0,0 @@ -use neon::types::buffer::TypedArray; -use neon::types::JsPromise; -use neon::{prelude::*, types::JsBigInt}; -use std::cell::RefCell; -use std::sync::Arc; -use tokio::sync::Mutex; -use tokio::time::Instant; - -use crate::errors::throw_libsql_error; -use crate::runtime; - -pub(crate) struct Statement { - pub conn: Arc>, - pub stmt: Arc>, - pub raw: RefCell, - pub safe_ints: RefCell, -} - -impl Finalize for Statement {} - -fn js_value_to_value( - cx: &mut FunctionContext, - v: Handle<'_, JsValue>, -) -> NeonResult { - if v.is_a::(cx) || v.is_a::(cx) { - Ok(libsql::Value::Null) - } else if v.is_a::(cx) { - todo!("bool"); - } else if v.is_a::(cx) { - let v = v.downcast_or_throw::(cx)?; - let v = v.value(cx); - Ok(libsql::Value::Real(v)) - } else if v.is_a::(cx) { - let v = v.downcast_or_throw::(cx)?; - let v = v.value(cx); - Ok(libsql::Value::Text(v)) - } else if v.is_a::(cx) { - let v = v.downcast_or_throw::(cx)?; - let v = v.to_i64(cx).or_throw(cx)?; - Ok(libsql::Value::Integer(v)) - } else if v.is_a::(cx) { - let v = v.downcast_or_throw::(cx)?; - let v = v.as_slice(cx); - Ok(libsql::Value::Blob(v.to_vec())) - } else if v.is_a::(cx) { - let v = v.downcast_or_throw::(cx)?; - let v = v.buffer(cx); - let v = v.as_slice(cx); - Ok(libsql::Value::Blob(v.to_vec())) - } else { - cx.throw_error("SQLite3 can only bind numbers, strings, bigints, buffers, and null") - } -} - -impl Statement { - pub fn js_raw(mut cx: FunctionContext) -> JsResult { - let stmt: Handle<'_, JsBox> = cx.this()?; - let raw_stmt = stmt.stmt.blocking_lock(); - if raw_stmt.columns().is_empty() { - return cx.throw_error("The raw() method is only for statements that return data"); - } - let raw = cx.argument::(0)?; - let raw = raw.value(&mut cx); - stmt.set_raw(raw); - Ok(cx.null()) - } - - fn set_raw(&self, raw: bool) { - self.raw.replace(raw); - } - - pub fn js_is_reader(mut cx: FunctionContext) -> JsResult { - let stmt: Handle<'_, JsBox> = cx.this()?; - let raw_stmt = stmt.stmt.blocking_lock(); - Ok(cx.boolean(!raw_stmt.columns().is_empty())) - } - - pub fn js_run(mut cx: FunctionContext) -> JsResult { - let stmt: Handle<'_, JsBox> = cx.this()?; - let raw_conn = stmt.conn.clone(); - let total_changes_before = raw_conn.blocking_lock().total_changes(); - let params = cx.argument::(0)?; - let params = convert_params(&mut cx, &stmt, params)?; - let mut raw_stmt = stmt.stmt.blocking_lock(); - raw_stmt.reset(); - let fut = raw_stmt.run(params); - let rt = runtime(&mut cx)?; - - let initial = Instant::now(); - - rt.block_on(fut) - .or_else(|err| throw_libsql_error(&mut cx, err))?; - - let duration = Instant::now() - initial; - - let (changes, last_insert_rowid) = { - let raw_conn = stmt.conn.clone(); - let raw_conn = raw_conn.blocking_lock(); - let changes = if raw_conn.total_changes() == total_changes_before { - 0 - } else { - raw_conn.changes() - }; - let last_insert_rowid = raw_conn.last_insert_rowid(); - (changes, last_insert_rowid) - }; - - let info = cx.empty_object(); - - let changes = cx.number(changes as f64); - info.set(&mut cx, "changes", changes)?; - - let duration = cx.number(duration.as_secs_f64() as f64); - info.set(&mut cx, "duration", duration)?; - - let last_insert_row_id = cx.number(last_insert_rowid as f64); - info.set(&mut cx, "lastInsertRowid", last_insert_row_id)?; - - Ok(info.upcast()) - } - - pub fn js_interrupt(mut cx: FunctionContext) -> JsResult { - let stmt: Handle<'_, JsBox> = cx.this()?; - let mut raw_stmt = stmt.stmt.blocking_lock(); - raw_stmt - .interrupt() - .or_else(|err| throw_libsql_error(&mut cx, err))?; - Ok(cx.null()) - } - - pub fn js_get(mut cx: FunctionContext) -> JsResult { - let stmt: Handle<'_, JsBox> = cx.this()?; - let params = cx.argument::(0)?; - let params = convert_params(&mut cx, &stmt, params)?; - let safe_ints = *stmt.safe_ints.borrow(); - let mut raw_stmt = stmt.stmt.blocking_lock(); - let fut = raw_stmt.query(params); - let rt = runtime(&mut cx)?; - let result = rt.block_on(fut); - let mut rows = result.or_else(|err| throw_libsql_error(&mut cx, err))?; - - let initial = Instant::now(); - - let result = rt - .block_on(rows.next()) - .or_else(|err| throw_libsql_error(&mut cx, err))?; - - let duration = Instant::now() - initial; - - let result = match result { - Some(row) => { - if *stmt.raw.borrow() { - let mut result = cx.empty_array(); - convert_row_raw(&mut cx, safe_ints, &mut result, &rows, &row)?; - Ok(result.upcast()) - } else { - let mut result = cx.empty_object(); - convert_row(&mut cx, safe_ints, &mut result, &rows, &row)?; - - let metadata = cx.empty_object(); - result.set(&mut cx, "_metadata", metadata)?; - - let duration = cx.number(duration.as_secs_f64()); - metadata.set(&mut cx, "duration", duration)?; - - Ok(result.upcast()) - } - } - None => Ok(cx.undefined().upcast()), - }; - raw_stmt.reset(); - result - } - - pub fn js_rows_sync(mut cx: FunctionContext) -> JsResult { - let stmt: Handle<'_, JsBox> = cx.this()?; - let params = cx.argument::(0)?; - let params = convert_params(&mut cx, &stmt, params)?; - let rt = runtime(&mut cx)?; - let result = rt.block_on(async move { - let mut raw_stmt = stmt.stmt.lock().await; - raw_stmt.reset(); - raw_stmt.query(params).await - }); - let rows = result.or_else(|err| throw_libsql_error(&mut cx, err))?; - let rows = Rows { - rows: RefCell::new(rows), - raw: *stmt.raw.borrow(), - safe_ints: *stmt.safe_ints.borrow(), - }; - Ok(cx.boxed(rows).upcast()) - } - - pub fn js_rows_async(mut cx: FunctionContext) -> JsResult { - let stmt: Handle<'_, JsBox> = cx.this()?; - let params = cx.argument::(0)?; - let params = convert_params(&mut cx, &stmt, params)?; - { - let mut raw_stmt = stmt.stmt.blocking_lock(); - raw_stmt.reset(); - } - let (deferred, promise) = cx.promise(); - let channel = cx.channel(); - let rt = runtime(&mut cx)?; - let raw = *stmt.raw.borrow(); - let safe_ints = *stmt.safe_ints.borrow(); - let raw_stmt = stmt.stmt.clone(); - rt.spawn(async move { - let result = { - let mut raw_stmt = raw_stmt.lock().await; - raw_stmt.query(params).await - }; - match result { - Ok(rows) => { - deferred.settle_with(&channel, move |mut cx| { - let rows = Rows { - rows: RefCell::new(rows), - raw, - safe_ints, - }; - Ok(cx.boxed(rows)) - }); - } - Err(err) => { - deferred.settle_with(&channel, |mut cx| { - throw_libsql_error(&mut cx, err)?; - Ok(cx.undefined()) - }); - } - } - }); - Ok(promise) - } - - pub fn js_columns(mut cx: FunctionContext) -> JsResult { - let stmt: Handle<'_, JsBox> = cx.this()?; - let result = cx.empty_array(); - let raw_stmt = stmt.stmt.blocking_lock(); - for (i, col) in raw_stmt.columns().iter().enumerate() { - let column = cx.empty_object(); - let column_name = cx.string(col.name()); - column.set(&mut cx, "name", column_name)?; - let column_origin_name: Handle<'_, JsValue> = - if let Some(origin_name) = col.origin_name() { - cx.string(origin_name).upcast() - } else { - cx.null().upcast() - }; - column.set(&mut cx, "column", column_origin_name)?; - let column_table_name: Handle<'_, JsValue> = if let Some(table_name) = col.table_name() - { - cx.string(table_name).upcast() - } else { - cx.null().upcast() - }; - column.set(&mut cx, "table", column_table_name)?; - let column_database_name: Handle<'_, JsValue> = - if let Some(database_name) = col.database_name() { - cx.string(database_name).upcast() - } else { - cx.null().upcast() - }; - column.set(&mut cx, "database", column_database_name)?; - let column_decl_type: Handle<'_, JsValue> = if let Some(decl_type) = col.decl_type() { - cx.string(decl_type).upcast() - } else { - cx.null().upcast() - }; - column.set(&mut cx, "type", column_decl_type)?; - result.set(&mut cx, i as u32, column)?; - } - Ok(result.upcast()) - } - - pub fn js_safe_integers(mut cx: FunctionContext) -> JsResult { - let stmt: Handle<'_, JsBox> = cx.this()?; - let toggle = cx.argument::(0)?; - let toggle = toggle.value(&mut cx); - stmt.set_safe_integers(toggle); - Ok(cx.null()) - } - - fn set_safe_integers(&self, toggle: bool) { - self.safe_ints.replace(toggle); - } -} - -pub(crate) struct Rows { - rows: RefCell, - raw: bool, - safe_ints: bool, -} - -impl Finalize for Rows {} - -impl Rows { - pub fn js_next(mut cx: FunctionContext) -> JsResult { - let result_arr = cx.argument::(0)?; - let rows: Handle<'_, JsBox> = cx.this()?; - let raw = rows.raw; - let safe_ints = rows.safe_ints; - let mut rows = rows.rows.borrow_mut(); - let rt = runtime(&mut cx)?; - let count = result_arr.len(&mut cx); - let res = cx.null(); - rt.block_on(async move { - let mut keys = Vec::>::with_capacity(rows.column_count() as usize); - for idx in 0..rows.column_count() { - let column_name = rows.column_name(idx).unwrap(); - keys.push(cx.string(column_name)); - } - for idx in 0..count { - match rows - .next() - .await - .or_else(|err| throw_libsql_error(&mut cx, err))? - { - Some(row) => { - if raw { - let mut result = cx.empty_array(); - convert_row_raw(&mut cx, safe_ints, &mut result, &rows, &row)?; - result_arr.set(&mut cx, idx, result)?; - } else { - let result = cx.empty_object(); - for idx in 0..rows.column_count() { - let v = row - .get_value(idx) - .or_else(|err| throw_libsql_error(&mut cx, err))?; - let v: Handle<'_, JsValue> = match v { - libsql::Value::Null => cx.null().upcast(), - libsql::Value::Integer(v) => { - if safe_ints { - neon::types::JsBigInt::from_i64(&mut cx, v).upcast() - } else { - cx.number(v as f64).upcast() - } - } - libsql::Value::Real(v) => cx.number(v).upcast(), - libsql::Value::Text(v) => cx.string(v).upcast(), - libsql::Value::Blob(v) => { - JsArrayBuffer::from_slice(&mut cx, &v)?.upcast() - } - }; - result.set(&mut cx, keys[idx as usize], v)?; - } - result_arr.set(&mut cx, idx, result)?; - } - } - None => { - break; - } - }; - } - Ok(()) - })?; - Ok(res) - } -} - -fn convert_params( - cx: &mut FunctionContext, - stmt: &Statement, - v: Handle<'_, JsValue>, -) -> NeonResult { - if v.is_a::(cx) { - let v = v.downcast_or_throw::(cx)?; - convert_params_array(cx, v) - } else { - let v = v.downcast_or_throw::(cx)?; - convert_params_object(cx, stmt, v) - } -} - -fn convert_params_array( - cx: &mut FunctionContext, - v: Handle<'_, JsArray>, -) -> NeonResult { - let mut params = vec![]; - for i in 0..v.len(cx) { - let v = v.get(cx, i)?; - let v = js_value_to_value(cx, v)?; - params.push(v); - } - Ok(libsql::params::Params::Positional(params)) -} - -fn convert_params_object( - cx: &mut FunctionContext, - stmt: &Statement, - v: Handle<'_, JsObject>, -) -> NeonResult { - let mut params = vec![]; - let stmt = &stmt.stmt; - let raw_stmt = stmt.blocking_lock(); - for idx in 0..raw_stmt.parameter_count() { - let name = raw_stmt.parameter_name((idx + 1) as i32).unwrap(); - let name = name.to_string(); - let v = v.get(cx, &name[1..])?; - let v = js_value_to_value(cx, v)?; - params.push((name, v)); - } - Ok(libsql::params::Params::Named(params)) -} - -fn convert_row( - cx: &mut FunctionContext, - safe_ints: bool, - result: &mut JsObject, - rows: &libsql::Rows, - row: &libsql::Row, -) -> NeonResult<()> { - for idx in 0..rows.column_count() { - let v = row - .get_value(idx) - .or_else(|err| throw_libsql_error(cx, err))?; - let column_name = rows.column_name(idx).unwrap(); - let v: Handle<'_, JsValue> = match v { - libsql::Value::Null => cx.null().upcast(), - libsql::Value::Integer(v) => { - if safe_ints { - neon::types::JsBigInt::from_i64(cx, v).upcast() - } else { - cx.number(v as f64).upcast() - } - } - libsql::Value::Real(v) => cx.number(v).upcast(), - libsql::Value::Text(v) => cx.string(v).upcast(), - libsql::Value::Blob(v) => JsBuffer::from_slice(cx, &v)?.upcast(), - }; - result.set(cx, column_name, v)?; - } - Ok(()) -} - -fn convert_row_raw( - cx: &mut FunctionContext, - safe_ints: bool, - result: &mut JsArray, - rows: &libsql::Rows, - row: &libsql::Row, -) -> NeonResult<()> { - for idx in 0..rows.column_count() { - let v = row - .get_value(idx) - .or_else(|err| throw_libsql_error(cx, err))?; - let v: Handle<'_, JsValue> = match v { - libsql::Value::Null => cx.null().upcast(), - libsql::Value::Integer(v) => { - if safe_ints { - neon::types::JsBigInt::from_i64(cx, v).upcast() - } else { - cx.number(v as f64).upcast() - } - } - libsql::Value::Real(v) => cx.number(v).upcast(), - libsql::Value::Text(v) => cx.string(v).upcast(), - libsql::Value::Blob(v) => JsBuffer::from_slice(cx, &v)?.upcast(), - }; - result.set(cx, idx as u32, v)?; - } - Ok(()) -} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 56e7a44..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "include": ["./promise.js"], - "compilerOptions": { - // Tells TypeScript to read JS files, as - // normally they are ignored as source files - "allowJs": true, - // Generate d.ts files - "declaration": true, - // This compiler run should - // only output d.ts files - "emitDeclarationOnly": true, - // Types should go into this directory. - // Removing this would place the .d.ts files - // next to the .js files - "outDir": "types", - // go to js file when using IDE functions like - // "Go to Definition" in VSCode - "declarationMap": true - } -} diff --git a/types/auth.d.ts b/types/auth.d.ts deleted file mode 100644 index 8b6d7bb..0000000 --- a/types/auth.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export = Authorization; -/** - * * - */ -type Authorization = number; -declare namespace Authorization { - let ALLOW: number; - let DENY: number; -} -//# sourceMappingURL=auth.d.ts.map \ No newline at end of file diff --git a/types/auth.d.ts.map b/types/auth.d.ts.map deleted file mode 100644 index eb37725..0000000 --- a/types/auth.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../auth.js"],"names":[],"mappings":";;;;qBAIU,MAAM;;eAOJ,MAAM;cAMN,MAAM"} \ No newline at end of file diff --git a/types/index.d.ts b/types/index.d.ts deleted file mode 100644 index dc23956..0000000 --- a/types/index.d.ts +++ /dev/null @@ -1,162 +0,0 @@ -// Type definitions for better-sqlite3 7.6 -// Project: https://github.com/JoshuaWise/better-sqlite3 -// Definitions by: Ben Davies -// Mathew Rumsey -// Santiago Aguilar -// Alessandro Vergani -// Andrew Kaiser -// Mark Stewart -// Florian Stamer -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 3.8 - -/// - -// FIXME: Is this `any` really necessary? -type VariableArgFunction = (...params: any[]) => unknown; -type ArgumentTypes = F extends (...args: infer A) => unknown ? A : never; -type ElementOf = T extends Array ? E : T; - -declare namespace Libsql { - interface Statement { - database: Database; - source: string; - reader: boolean; - readonly: boolean; - busy: boolean; - - run(...params: BindParameters): Database.RunResult; - get(...params: BindParameters): unknown; - all(...params: BindParameters): unknown[]; - iterate(...params: BindParameters): IterableIterator; - pluck(toggleState?: boolean): this; - expand(toggleState?: boolean): this; - raw(toggleState?: boolean): this; - bind(...params: BindParameters): this; - columns(): ColumnDefinition[]; - safeIntegers(toggleState?: boolean): this; - } - - interface ColumnDefinition { - name: string; - column: string | null; - table: string | null; - database: string | null; - type: string | null; - } - - interface Transaction { - (...params: ArgumentTypes): ReturnType; - default(...params: ArgumentTypes): ReturnType; - deferred(...params: ArgumentTypes): ReturnType; - immediate(...params: ArgumentTypes): ReturnType; - exclusive(...params: ArgumentTypes): ReturnType; - } - - interface VirtualTableOptions { - rows: () => Generator; - columns: string[]; - parameters?: string[] | undefined; - safeIntegers?: boolean | undefined; - directOnly?: boolean | undefined; - } - - interface Database { - memory: boolean; - readonly: boolean; - name: string; - open: boolean; - inTransaction: boolean; - - prepare( - source: string, - ): BindParameters extends unknown[] ? Statement : Statement<[BindParameters]>; - transaction(fn: F): Transaction; - sync(): any; - exec(source: string): this; - pragma(source: string, options?: Database.PragmaOptions): unknown; - function(name: string, cb: (...params: unknown[]) => unknown): this; - function(name: string, options: Database.RegistrationOptions, cb: (...params: unknown[]) => unknown): this; - aggregate(name: string, options: Database.RegistrationOptions & { - start?: T | (() => T); - step: (total: T, next: ElementOf) => T | void; - inverse?: ((total: T, dropped: T) => T) | undefined; - result?: ((total: T) => unknown) | undefined; - }): this; - loadExtension(path: string): this; - close(): this; - defaultSafeIntegers(toggleState?: boolean): this; - backup(destinationFile: string, options?: Database.BackupOptions): Promise; - table(name: string, options: VirtualTableOptions): this; - unsafeMode(unsafe?: boolean): this; - serialize(options?: Database.SerializeOptions): Buffer; - } - - interface DatabaseConstructor { - new (filename: string | Buffer, options?: Database.Options): Database; - (filename: string, options?: Database.Options): Database; - prototype: Database; - - SqliteError: typeof SqliteError; - } -} - -declare class SqliteError extends Error { - name: string; - message: string; - code: string; - rawCode?: number; - constructor(message: string, code: string, rawCode?: number); -} - -declare namespace Database { - interface RunResult { - changes: number; - lastInsertRowid: number | bigint; - } - - interface Options { - readonly?: boolean | undefined; - fileMustExist?: boolean | undefined; - timeout?: number | undefined; - verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined; - nativeBinding?: string | undefined; - syncUrl?: string | undefined; - } - - interface SerializeOptions { - attached?: string; - } - - interface PragmaOptions { - simple?: boolean | undefined; - } - - interface RegistrationOptions { - varargs?: boolean | undefined; - deterministic?: boolean | undefined; - safeIntegers?: boolean | undefined; - directOnly?: boolean | undefined; - } - - type AggregateOptions = Parameters[1]; - - interface BackupMetadata { - totalPages: number; - remainingPages: number; - } - interface BackupOptions { - progress: (info: BackupMetadata) => number; - } - - type SqliteError = typeof SqliteError; - type Statement = BindParameters extends unknown[] - ? Libsql.Statement - : Libsql.Statement<[BindParameters]>; - type ColumnDefinition = Libsql.ColumnDefinition; - type Transaction = Libsql.Transaction; - type Database = Libsql.Database; -} - -declare const Database: Libsql.DatabaseConstructor; -export = Database; diff --git a/types/promise.d.ts b/types/promise.d.ts deleted file mode 100644 index c510606..0000000 --- a/types/promise.d.ts +++ /dev/null @@ -1,66 +0,0 @@ -export = Database; -/** - * Database represents a connection that can prepare and execute SQL statements. - */ -declare class Database { - /** - * Creates a new database connection. If the database file pointed to by `path` does not exists, it will be created. - * - * @constructor - * @param {string} path - Path to the database file. - */ - constructor(path: string, opts: any); - db: any; - memory: boolean; - readonly: boolean; - name: string; - open: boolean; - sync(): any; - syncUntil(replicationIndex: any): any; - /** - * Prepares a SQL statement for execution. - * - * @param {string} sql - The SQL statement string to prepare. - */ - prepare(sql: string): any; - /** - * Returns a function that executes the given function in a transaction. - * - * @param {function} fn - The function to wrap in a transaction. - */ - transaction(fn: Function): (...bindParameters: any[]) => Promise; - pragma(source: any, options: any): any; - backup(filename: any, options: any): void; - serialize(options: any): void; - function(name: any, options: any, fn: any): void; - aggregate(name: any, options: any): void; - table(name: any, factory: any): void; - authorizer(rules: any): void; - loadExtension(...args: any[]): void; - maxWriteReplicationIndex(): any; - /** - * Executes a SQL statement. - * - * @param {string} sql - The SQL statement string to execute. - */ - exec(sql: string): any; - /** - * Interrupts the database connection. - */ - interrupt(): void; - /** - * Closes the database connection. - */ - close(): void; - /** - * Toggle 64-bit integer support. - */ - defaultSafeIntegers(toggle: any): this; - unsafeMode(...args: any[]): void; -} -declare namespace Database { - export { Authorization, SqliteError }; -} -import Authorization = require("./auth"); -import SqliteError = require("./sqlite-error"); -//# sourceMappingURL=promise.d.ts.map \ No newline at end of file diff --git a/types/promise.d.ts.map b/types/promise.d.ts.map deleted file mode 100644 index 370ac51..0000000 --- a/types/promise.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"promise.d.ts","sourceRoot":"","sources":["../promise.js"],"names":[],"mappings":";AAoEA;;GAEG;AACH;IACE;;;;;OAKG;IACH,kBAFW,MAAM,aAoChB;IArBG,QAAmH;IAQrH,gBAAiC;IACjC,kBAAqB;IACrB,aAAc;IACd,cAAgB;IAYlB,YAEC;IAED,sCAEC;IAED;;;;OAIG;IACH,aAFW,MAAM,OAQhB;IAED;;;;OAIG;IACH,sEA8BC;IAED,uCAQC;IAED,0CAEC;IAED,8BAEC;IAED,iDAqBC;IAED,yCAYC;IAED,qCAUC;IAED,6BAEC;IAED,oCAEC;IAED,gCAEC;IAED;;;;OAIG;IACH,UAFW,MAAM,OAMhB;IAED;;OAEG;IACH,kBAEC;IAED;;OAEG;IACH,cAEC;IAED;;OAEG;IACH,uCAGC;IAED,iCAEC;CACF"} \ No newline at end of file diff --git a/types/sqlite-error.d.ts b/types/sqlite-error.d.ts deleted file mode 100644 index 8d7bda0..0000000 --- a/types/sqlite-error.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export = SqliteError; -declare function SqliteError(message: any, code: any, rawCode: any): SqliteError; -declare class SqliteError { - constructor(message: any, code: any, rawCode: any); - code: string; - rawCode: any; - name: string; -} -//# sourceMappingURL=sqlite-error.d.ts.map \ No newline at end of file diff --git a/types/sqlite-error.d.ts.map b/types/sqlite-error.d.ts.map deleted file mode 100644 index becc5e6..0000000 --- a/types/sqlite-error.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"sqlite-error.d.ts","sourceRoot":"","sources":["../sqlite-error.js"],"names":[],"mappings":";AAGA,iFAaC;;IAbD,mDAaC;IAFO,aAAgB;IAChB,aAAsB"} \ No newline at end of file diff --git a/wrapper.js b/wrapper.js new file mode 100644 index 0000000..732b19f --- /dev/null +++ b/wrapper.js @@ -0,0 +1,351 @@ +"use strict"; + +const { Database: NativeDb, databasePrepareSync, databaseSyncSync, statementIterateSync, iteratorNextSync } = require("./index.js"); +const SqliteError = require("./sqlite-error.js"); +const Authorization = require("./auth"); + +function convertError(err) { + // Handle errors from Rust with JSON-encoded message + if (typeof err.message === 'string') { + try { + const data = JSON.parse(err.message); + if (data && data.libsqlError) { + if (data.code === "SQLITE_AUTH") { + // For SQLITE_AUTH, preserve the JSON string for the test + return new SqliteError(err.message, data.code, data.rawCode); + } else if (data.code === "SQLITE_NOTOPEN") { + // Convert SQLITE_NOTOPEN to TypeError with expected message + return new TypeError("The database connection is not open"); + } else { + // For all other errors, use the plain message string + return new SqliteError(data.message, data.code, data.rawCode); + } + } + } catch (_) { + // Not JSON, ignore + } + } + return err; +} + +/** + * Database represents a connection that can prepare and execute SQL statements. + */ +class Database { + /** + * Creates a new database connection. If the database file pointed to by `path` does not exists, it will be created. + * + * @constructor + * @param {string} path - Path to the database file. + */ + constructor(path, opts) { + this.db = new NativeDb(path, opts); + this.memory = this.db.memory + const db = this.db; + Object.defineProperties(this, { + inTransaction: { + get() { + return db.inTransaction(); + } + }, + }); + } + + sync() { + try { + const result = databaseSyncSync(this.db); + return { + frames_synced: result.frames_synced, + replication_index: result.replication_index + }; + } catch (err) { + throw convertError(err); + } + } + + syncUntil(replicationIndex) { + throw new Error("not implemented"); + } + + /** + * Prepares a SQL statement for execution. + * + * @param {string} sql - The SQL statement string to prepare. + */ + prepare(sql) { + try { + const stmt = databasePrepareSync(this.db, sql); + return new Statement(stmt); + } catch (err) { + throw convertError(err); + } + } + + /** + * Returns a function that executes the given function in a transaction. + * + * @param {function} fn - The function to wrap in a transaction. + */ + transaction(fn) { + if (typeof fn !== "function") + throw new TypeError("Expected first argument to be a function"); + + const db = this; + const wrapTxn = (mode) => { + return (...bindParameters) => { + db.exec("BEGIN " + mode); + try { + const result = fn(...bindParameters); + db.exec("COMMIT"); + return result; + } catch (err) { + db.exec("ROLLBACK"); + throw err; + } + }; + }; + const properties = { + default: { value: wrapTxn("") }, + deferred: { value: wrapTxn("DEFERRED") }, + immediate: { value: wrapTxn("IMMEDIATE") }, + exclusive: { value: wrapTxn("EXCLUSIVE") }, + database: { value: this, enumerable: true }, + }; + Object.defineProperties(properties.default.value, properties); + Object.defineProperties(properties.deferred.value, properties); + Object.defineProperties(properties.immediate.value, properties); + Object.defineProperties(properties.exclusive.value, properties); + return properties.default.value; + } + + pragma(source, options) { + if (options == null) options = {}; + if (typeof source !== 'string') throw new TypeError('Expected first argument to be a string'); + if (typeof options !== 'object') throw new TypeError('Expected second argument to be an options object'); + const simple = options['simple']; + const stmt = this.prepare(`PRAGMA ${source}`, this, true); + return simple ? stmt.pluck().get() : stmt.all(); + } + + backup(filename, options) { + throw new Error("not implemented"); + } + + serialize(options) { + throw new Error("not implemented"); + } + + function(name, options, fn) { + throw new Error("not implemented"); + } + + aggregate(name, options) { + throw new Error("not implemented"); + } + + table(name, factory) { + throw new Error("not implemented"); + } + + authorizer(rules) { + try { + this.db.authorizer(rules); + } catch (err) { + throw convertError(err); + } + } + + loadExtension(...args) { + try { + this.db.loadExtension(...args); + } catch (err) { + throw convertError(err); + } + } + + maxWriteReplicationIndex() { + try { + return this.db.maxWriteReplicationIndex(); + } catch (err) { + throw convertError(err); + } + } + + /** + * Executes a SQL statement. + * + * @param {string} sql - The SQL statement string to execute. + */ + exec(sql) { + try { + this.db.exec(sql); + } catch (err) { + throw convertError(err); + } + } + + /** + * Interrupts the database connection. + */ + interrupt() { + this.db.interrupt(); + } + + /** + * Closes the database connection. + */ + close() { + this.db.close(); + } + + authorizer(hook) { + this.db.authorizer(hook); + return this; + } + + /** + * Toggle 64-bit integer support. + */ + defaultSafeIntegers(toggle) { + this.db.defaultSafeIntegers(toggle); + return this; + } + + unsafeMode(...args) { + throw new Error("not implemented"); + } +} + +/** + * Statement represents a prepared SQL statement that can be executed. + */ +class Statement { + constructor(stmt) { + this.stmt = stmt; + } + + /** + * Toggle raw mode. + * + * @param raw Enable or disable raw mode. If you don't pass the parameter, raw mode is enabled. + */ + raw(raw) { + this.stmt.raw(raw); + return this; + } + + /** + * Toggle pluck mode. + * + * @param pluckMode Enable or disable pluck mode. If you don't pass the parameter, pluck mode is enabled. + */ + pluck(pluckMode) { + this.stmt.pluck(pluckMode); + return this; + } + + /** + * Toggle query timing. + * + * @param timing Enable or disable query timing. If you don't pass the parameter, query timing is enabled. + */ + timing(timingMode) { + this.stmt.timing(timingMode); + return this; + } + + get reader() { + throw new Error("not implemented"); + } + + /** + * Executes the SQL statement and returns an info object. + */ + run(...bindParameters) { + try { + return this.stmt.run(...bindParameters); + } catch (err) { + throw convertError(err); + } + } + + /** + * Executes the SQL statement and returns the first row. + * + * @param bindParameters - The bind parameters for executing the statement. + */ + get(...bindParameters) { + try { + return this.stmt.get(...bindParameters); + } catch (err) { + throw convertError(err); + } + } + + /** + * Executes the SQL statement and returns an iterator to the resulting rows. + * + * @param bindParameters - The bind parameters for executing the statement. + */ + iterate(...bindParameters) { + try { + const it = statementIterateSync(this.stmt, ...bindParameters); + return { + next: () => iteratorNextSync(it), + [Symbol.iterator]() { + return { + next: () => iteratorNextSync(it), + } + }, + }; + } catch (err) { + throw convertError(err); + } + } + + /** + * Executes the SQL statement and returns an array of the resulting rows. + * + * @param bindParameters - The bind parameters for executing the statement. + */ + all(...bindParameters) { + try { + const result = []; + const iterator = this.iterate(...bindParameters); + let next; + while (!(next = iterator.next()).done) { + result.push(next.value); + } + return result; + } catch (err) { + throw convertError(err); + } + } + + /** + * Interrupts the statement. + */ + interrupt() { + this.stmt.interrupt(); + return this; + } + + /** + * Returns the columns in the result set returned by this prepared statement. + */ + columns() { + return this.stmt.columns(); + } + + /** + * Toggle 64-bit integer support. + */ + safeIntegers(toggle) { + this.stmt.safeIntegers(toggle); + return this; + } +} + +module.exports = Database; +module.exports.SqliteError = SqliteError; +module.exports.Authorization = Authorization; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..b8cd7df --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1128 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@isaacs/fs-minipass@^4.0.0": + version "4.0.1" + resolved "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz" + integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== + dependencies: + minipass "^7.0.4" + +"@mapbox/node-pre-gyp@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz" + integrity sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg== + dependencies: + consola "^3.2.3" + detect-libc "^2.0.0" + https-proxy-agent "^7.0.5" + node-fetch "^2.6.7" + nopt "^8.0.0" + semver "^7.5.3" + tar "^7.4.0" + +"@napi-rs/cli@^2.18.4": + version "2.18.4" + resolved "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.18.4.tgz" + integrity sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@rollup/pluginutils@^5.1.3": + version "5.1.4" + resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz" + integrity sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^4.0.2" + +"@sindresorhus/merge-streams@^2.1.0": + version "2.3.0" + resolved "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz" + integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== + +"@types/estree@^1.0.0": + version "1.0.7" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz" + integrity sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ== + +"@vercel/nft@^0.29.2": + version "0.29.4" + resolved "https://registry.npmjs.org/@vercel/nft/-/nft-0.29.4.tgz" + integrity sha512-6lLqMNX3TuycBPABycx7A9F1bHQR7kiQln6abjFbPrf5C/05qHM9M5E4PeTE59c7z8g6vHnx1Ioihb2AQl7BTA== + dependencies: + "@mapbox/node-pre-gyp" "^2.0.0" + "@rollup/pluginutils" "^5.1.3" + acorn "^8.6.0" + acorn-import-attributes "^1.9.5" + async-sema "^3.1.1" + bindings "^1.4.0" + estree-walker "2.0.2" + glob "^10.4.5" + graceful-fs "^4.2.9" + node-gyp-build "^4.2.2" + picomatch "^4.0.2" + resolve-from "^5.0.0" + +abbrev@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz" + integrity sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg== + +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + +acorn-walk@^8.3.4: + version "8.3.4" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + +acorn@^8, acorn@^8.11.0, acorn@^8.14.1, acorn@^8.6.0: + version "8.14.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz" + integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== + +agent-base@^7.1.2: + version "7.1.3" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz" + integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.0.0, ansi-styles@^6.1.0, ansi-styles@^6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz" + integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== + +arrgv@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz" + integrity sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw== + +arrify@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz" + integrity sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw== + +async-sema@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz" + integrity sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg== + +ava@^6.0.1: + version "6.3.0" + resolved "https://registry.npmjs.org/ava/-/ava-6.3.0.tgz" + integrity sha512-64K+xNmlgMo1D94evJlkBWmJ6CGrO6oEctGEjA3PIl5GrwZyMXM5OEycZWnKGduE1YdqMvYDl29SgnNk7kyx+A== + dependencies: + "@vercel/nft" "^0.29.2" + acorn "^8.14.1" + acorn-walk "^8.3.4" + ansi-styles "^6.2.1" + arrgv "^1.0.2" + arrify "^3.0.0" + callsites "^4.2.0" + cbor "^10.0.3" + chalk "^5.4.1" + chunkd "^2.0.1" + ci-info "^4.2.0" + ci-parallel-vars "^1.0.1" + cli-truncate "^4.0.0" + code-excerpt "^4.0.0" + common-path-prefix "^3.0.0" + concordance "^5.0.4" + currently-unhandled "^0.4.1" + debug "^4.4.0" + emittery "^1.1.0" + figures "^6.1.0" + globby "^14.1.0" + ignore-by-default "^2.1.0" + indent-string "^5.0.0" + is-plain-object "^5.0.0" + is-promise "^4.0.0" + matcher "^5.0.0" + memoize "^10.1.0" + ms "^2.1.3" + p-map "^7.0.3" + package-config "^5.0.0" + picomatch "^4.0.2" + plur "^5.1.0" + pretty-ms "^9.2.0" + resolve-cwd "^3.0.0" + stack-utils "^2.0.6" + strip-ansi "^7.1.0" + supertap "^3.0.1" + temp-dir "^3.0.0" + write-file-atomic "^6.0.0" + yargs "^17.7.2" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +bindings@^1.4.0: + 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" + +blueimp-md5@^2.10.0: + version "2.19.0" + resolved "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz" + integrity sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w== + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +callsites@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-4.2.0.tgz" + integrity sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ== + +cbor@^10.0.3: + version "10.0.3" + resolved "https://registry.npmjs.org/cbor/-/cbor-10.0.3.tgz" + integrity sha512-72Jnj81xMsqepqdcSdf2+fflz/UDsThOHy5hj2MW5F5xzHL8Oa0KQ6I6V9CwVUPxg5pf+W9xp6W2KilaRXWWtw== + dependencies: + nofilter "^3.0.2" + +chalk@^5.4.1: + version "5.4.1" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz" + integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== + +chownr@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz" + integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== + +chunkd@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz" + integrity sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ== + +ci-info@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz" + integrity sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg== + +ci-parallel-vars@^1.0.1: + 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== + +cli-truncate@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz" + integrity sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA== + dependencies: + slice-ansi "^5.0.0" + string-width "^7.0.0" + +cliui@^8.0.1: + 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== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +code-excerpt@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz" + integrity sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA== + dependencies: + convert-to-spaces "^2.0.1" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +common-path-prefix@^3.0.0: + 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== + +concordance@^5.0.4: + version "5.0.4" + resolved "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz" + integrity sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw== + 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" + +consola@^3.2.3: + version "3.4.2" + resolved "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz" + integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA== + +convert-to-spaces@^2.0.1: + 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== + +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz" + integrity sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng== + dependencies: + array-find-index "^1.0.1" + +date-time@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz" + integrity sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg== + dependencies: + time-zone "^1.0.0" + +debug@^4.4.0, debug@4: + version "4.4.1" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + +detect-libc@^2.0.0: + version "2.0.4" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz" + integrity sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +emittery@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/emittery/-/emittery-1.1.0.tgz" + integrity sha512-rsX7ktqARv/6UQDgMaLfIqUWAEzzbCQiVh7V9rhDXp6c37yoJcks12NVD+XPkgl4AEavmNhVfrhGoqYwIsMYYA== + +emoji-regex@^10.3.0: + version "10.4.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz" + integrity sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +escalade@^3.1.1: + version "3.2.0" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^5.0.0: + 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== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estree-walker@^2.0.2, estree-walker@2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +esutils@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +fast-diff@^1.2.0: + version "1.3.0" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^3.3.3: + version "3.3.3" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + 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.8" + +fastq@^1.6.0: + version "1.19.1" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz" + integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + dependencies: + reusify "^1.0.4" + +figures@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz" + integrity sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg== + dependencies: + is-unicode-supported "^2.0.0" + +file-uri-to-path@1.0.0: + 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== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up-simple@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz" + integrity sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ== + +foreground-child@^3.1.0: + version "3.3.1" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-east-asian-width@^1.0.0: + version "1.3.0" + resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz" + integrity sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^10.4.5: + version "10.4.5" + resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +globby@^14.1.0: + version "14.1.0" + resolved "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz" + integrity sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA== + dependencies: + "@sindresorhus/merge-streams" "^2.1.0" + fast-glob "^3.3.3" + ignore "^7.0.3" + path-type "^6.0.0" + slash "^5.1.0" + unicorn-magic "^0.3.0" + +graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +https-proxy-agent@^7.0.5: + version "7.0.6" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== + dependencies: + agent-base "^7.1.2" + debug "4" + +ignore-by-default@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz" + integrity sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw== + +ignore@^7.0.3: + version "7.0.5" + resolved "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz" + integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz" + integrity sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg== + +irregular-plurals@^3.3.0: + 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== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + 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== + +is-fullwidth-code-point@^4.0.0: + 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== + +is-glob@^4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + +is-unicode-supported@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz" + integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +js-string-escape@^1.0.1: + 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== + +js-yaml@^3.14.1: + version "3.14.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +load-json-file@^7.0.1: + 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== + +lodash@^4.17.15: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +matcher@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz" + integrity sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw== + dependencies: + escape-string-regexp "^5.0.0" + +md5-hex@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz" + integrity sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw== + dependencies: + blueimp-md5 "^2.10.0" + +memoize@^10.1.0: + version "10.1.0" + resolved "https://registry.npmjs.org/memoize/-/memoize-10.1.0.tgz" + integrity sha512-MMbFhJzh4Jlg/poq1si90XRlTZRDHVqdlz2mPyGJ6kqMpyHUyVpDd5gpFAvVehW64+RA1eKE9Yt8aSLY7w2Kgg== + dependencies: + mimic-function "^5.0.1" + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mimic-function@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4, minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +minizlib@^3.0.1: + version "3.0.2" + resolved "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz" + integrity sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA== + dependencies: + minipass "^7.1.2" + +mkdirp@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-gyp-build@^4.2.2: + version "4.8.4" + resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz" + integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== + +nofilter@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== + +nopt@^8.0.0: + version "8.1.0" + resolved "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz" + integrity sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A== + dependencies: + abbrev "^3.0.0" + +p-map@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz" + integrity sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA== + +package-config@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/package-config/-/package-config-5.0.0.tgz" + integrity sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg== + dependencies: + find-up-simple "^1.0.0" + load-json-file "^7.0.1" + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +parse-ms@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz" + integrity sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-type@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz" + integrity sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ== + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +picomatch@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== + +plur@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz" + integrity sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg== + dependencies: + irregular-plurals "^3.3.0" + +pretty-ms@^9.2.0: + version "9.2.0" + resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz" + integrity sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg== + dependencies: + parse-ms "^4.0.0" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +require-directory@^2.1.1: + 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== + +resolve-cwd@^3.0.0: + 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== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +reusify@^1.0.4: + version "1.1.0" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz" + integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +semver@^7.3.2, semver@^7.5.3: + version "7.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + +serialize-error@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz" + integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== + dependencies: + type-fest "^0.13.1" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +slash@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz" + integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== + +slice-ansi@^5.0.0: + 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== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stack-utils@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1: + version "5.1.2" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string-width@^7.0.0: + version "7.2.0" + resolved "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1, strip-ansi@^7.1.0: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +supertap@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz" + integrity sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw== + dependencies: + indent-string "^5.0.0" + js-yaml "^3.14.1" + serialize-error "^7.0.1" + strip-ansi "^7.0.1" + +tar@^7.4.0: + version "7.4.3" + resolved "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz" + integrity sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw== + dependencies: + "@isaacs/fs-minipass" "^4.0.0" + chownr "^3.0.0" + minipass "^7.1.2" + minizlib "^3.0.1" + mkdirp "^3.0.1" + yallist "^5.0.0" + +temp-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz" + integrity sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw== + +time-zone@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz" + integrity sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA== + +to-regex-range@^5.0.1: + 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== + dependencies: + is-number "^7.0.0" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +type-fest@^0.13.1: + version "0.13.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + +unicorn-magic@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz" + integrity sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +well-known-symbols@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz" + integrity sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +write-file-atomic@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz" + integrity sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^4.0.1" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz" + integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + 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" From 68e7d411b8c94e260c31a21b559864e4aecc7f94 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Thu, 12 Jun 2025 12:54:42 +0300 Subject: [PATCH 2/3] github: Run tests on Node 20 and 22 Node 18 is EOL. --- .github/workflows/CI.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 54808af..b296f87 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -141,8 +141,8 @@ jobs: - host: windows-latest target: x86_64-pc-windows-msvc node: - - '18' - '20' + - '22' runs-on: ${{ matrix.settings.host }} steps: - uses: actions/checkout@v4 @@ -172,8 +172,8 @@ jobs: fail-fast: false matrix: node: - - '18' - '20' + - '22' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -202,8 +202,8 @@ jobs: fail-fast: false matrix: node: - - '18' - '20' + - '22' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -234,8 +234,8 @@ jobs: fail-fast: false matrix: node: - - '18' - '20' + - '22' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From e2cb98cc3cbcfd3cba119a12f846076aaa0500ae Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Thu, 12 Jun 2025 13:08:11 +0300 Subject: [PATCH 3/3] github: Run tests with Node 22 only on Windows and macOS The tests fail randomly with Node 20 on Windows. --- .github/workflows/CI.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index b296f87..6f0d6e9 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -141,7 +141,6 @@ jobs: - host: windows-latest target: x86_64-pc-windows-msvc node: - - '20' - '22' runs-on: ${{ matrix.settings.host }} steps: