mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-18 00:54:19 +01:00
Remove wasm binding
With napi v3 we can compile our javascript binding to wasm, which can reduce a lot of maintenance overhead and complexity
This commit is contained in:
64
.github/labeler.yml
vendored
64
.github/labeler.yml
vendored
@@ -1,108 +1,106 @@
|
|||||||
simulator:
|
simulator:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'simulator/**/*'
|
- any-glob-to-any-file: "simulator/**/*"
|
||||||
docs:
|
docs:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'docs/**/*'
|
- any-glob-to-any-file: "docs/**/*"
|
||||||
|
|
||||||
extensionlib:
|
extensionlib:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: ['extensions/core/**/*', 'macros/src/ext/*', 'core/ext/*']
|
- any-glob-to-any-file:
|
||||||
|
["extensions/core/**/*", "macros/src/ext/*", "core/ext/*"]
|
||||||
|
|
||||||
macros:
|
macros:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: ['macros/**/*']
|
- any-glob-to-any-file: ["macros/**/*"]
|
||||||
|
|
||||||
extensions-other:
|
extensions-other:
|
||||||
- all:
|
- all:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'extensions/**/*'
|
- any-glob-to-any-file: "extensions/**/*"
|
||||||
- all-globs-to-all-files: '!extensions/core/**/*'
|
- all-globs-to-all-files: "!extensions/core/**/*"
|
||||||
|
|
||||||
fuzzing:
|
fuzzing:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'fuzz/**/*'
|
- any-glob-to-any-file: "fuzz/**/*"
|
||||||
|
|
||||||
perf/benchmarks:
|
perf/benchmarks:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: ['perf/**/*', 'core/benches/*']
|
- any-glob-to-any-file: ["perf/**/*", "core/benches/*"]
|
||||||
|
|
||||||
go-bindings:
|
go-bindings:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'bindings/go/**/*'
|
- any-glob-to-any-file: "bindings/go/**/*"
|
||||||
|
|
||||||
wasm-bindings:
|
|
||||||
- changed-files:
|
|
||||||
- any-glob-to-any-file: 'bindings/wasm/**/*'
|
|
||||||
|
|
||||||
python-bindings:
|
python-bindings:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'bindings/python/**/*'
|
- any-glob-to-any-file: "bindings/python/**/*"
|
||||||
|
|
||||||
js-bindings:
|
js-bindings:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'bindings/javascript/**/*'
|
- any-glob-to-any-file: "bindings/javascript/**/*"
|
||||||
|
|
||||||
rust-bindings:
|
rust-bindings:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'bindings/rust/**/*'
|
- any-glob-to-any-file: "bindings/rust/**/*"
|
||||||
|
|
||||||
java-bindings:
|
java-bindings:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'bindings/java/**/*'
|
- any-glob-to-any-file: "bindings/java/**/*"
|
||||||
|
|
||||||
parser:
|
parser:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: "vendored/sqlite3-parser/*"
|
- any-glob-to-any-file: "vendored/sqlite3-parser/*"
|
||||||
|
|
||||||
cli:
|
cli:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: "cli/**/*"
|
- any-glob-to-any-file: "cli/**/*"
|
||||||
|
|
||||||
sqlite3:
|
sqlite3:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: "sqlite3/**/*"
|
- any-glob-to-any-file: "sqlite3/**/*"
|
||||||
|
|
||||||
core:
|
core:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'core/**/*'
|
- any-glob-to-any-file: "core/**/*"
|
||||||
|
|
||||||
optimizer:
|
optimizer:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'core/translate/optimizer/*'
|
- any-glob-to-any-file: "core/translate/optimizer/*"
|
||||||
|
|
||||||
translation/planning:
|
translation/planning:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'core/translate/*.rs'
|
- any-glob-to-any-file: "core/translate/*.rs"
|
||||||
|
|
||||||
io:
|
io:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'core/io/*'
|
- any-glob-to-any-file: "core/io/*"
|
||||||
|
|
||||||
mvcc:
|
mvcc:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'core/mvcc/**/*'
|
- any-glob-to-any-file: "core/mvcc/**/*"
|
||||||
|
|
||||||
vdbe:
|
vdbe:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'core/vdbe/*'
|
- any-glob-to-any-file: "core/vdbe/*"
|
||||||
|
|
||||||
json:
|
json:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'core/json/*'
|
- any-glob-to-any-file: "core/json/*"
|
||||||
|
|
||||||
storage:
|
storage:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'core/storage/*'
|
- any-glob-to-any-file: "core/storage/*"
|
||||||
|
|
||||||
vector:
|
vector:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: 'core/vector/*'
|
- any-glob-to-any-file: "core/vector/*"
|
||||||
|
|
||||||
antithesis:
|
antithesis:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: ['antithesis-tests/**/*', 'scripts/antithesis/*', 'stress/**/*']
|
- any-glob-to-any-file:
|
||||||
|
["antithesis-tests/**/*", "scripts/antithesis/*", "stress/**/*"]
|
||||||
|
|
||||||
ci-actions:
|
ci-actions:
|
||||||
- changed-files:
|
- changed-files:
|
||||||
- any-glob-to-any-file: '.github/workflows/*.yml'
|
- any-glob-to-any-file: ".github/workflows/*.yml"
|
||||||
|
|||||||
71
.github/workflows/rust.yml
vendored
71
.github/workflows/rust.yml
vendored
@@ -2,9 +2,9 @@ name: Rust
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: ["main"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: ["main"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
@@ -13,12 +13,12 @@ jobs:
|
|||||||
cargo-fmt-check:
|
cargo-fmt-check:
|
||||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: cargo fmt --check
|
run: cargo fmt --check
|
||||||
- name: Check formatting (fuzz)
|
- name: Check formatting (fuzz)
|
||||||
run: cd fuzz && cargo fmt --check
|
run: cd fuzz && cargo fmt --check
|
||||||
|
|
||||||
build-native:
|
build-native:
|
||||||
strategy:
|
strategy:
|
||||||
@@ -28,21 +28,21 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: useblacksmith/rust-cache@v3
|
- uses: useblacksmith/rust-cache@v3
|
||||||
with:
|
with:
|
||||||
prefix-key: "v1-rust" # can be updated if we need to reset caches due to non-trivial change in the dependencies (for example, custom env var were set for single workspace project)
|
prefix-key: "v1-rust" # can be updated if we need to reset caches due to non-trivial change in the dependencies (for example, custom env var were set for single workspace project)
|
||||||
- name: Set up Python 3.10
|
- name: Set up Python 3.10
|
||||||
uses: useblacksmith/setup-python@v6
|
uses: useblacksmith/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --verbose
|
run: cargo build --verbose
|
||||||
- name: Test
|
- name: Test
|
||||||
env:
|
env:
|
||||||
RUST_LOG: ${{ runner.debug && 'turso_core::storage=trace' || '' }}
|
RUST_LOG: ${{ runner.debug && 'turso_core::storage=trace' || '' }}
|
||||||
run: cargo test --verbose
|
run: cargo test --verbose
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
@@ -50,21 +50,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: |
|
run: |
|
||||||
cargo clippy --workspace --all-features --all-targets --exclude limbo-wasm -- --deny=warnings
|
cargo clippy --workspace --all-features --all-targets -- --deny=warnings
|
||||||
- name: Clippy `limbo-wasm` crate `nodejs` feature
|
|
||||||
run: |
|
|
||||||
cargo clippy --package limbo-wasm --features nodejs --all-targets --no-deps -- -A clippy::all -W clippy::correctness -W clippy::perf -W clippy::suspicious --deny=warnings
|
|
||||||
- name: Clippy `limbo-wasm` crate `web` feature
|
|
||||||
run: |
|
|
||||||
cargo clippy --package limbo-wasm --no-default-features --features web --all-targets --no-deps -- -A clippy::all -W clippy::correctness -W clippy::perf -W clippy::suspicious --deny=warnings
|
|
||||||
|
|
||||||
build-wasm:
|
|
||||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install
|
|
||||||
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
|
|
||||||
- run: wasm-pack build --target nodejs bindings/wasm
|
|
||||||
|
|
||||||
simulator:
|
simulator:
|
||||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
@@ -95,10 +81,10 @@ jobs:
|
|||||||
- name: Test
|
- name: Test
|
||||||
run: make test
|
run: make test
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
# - uses: "./.github/shared/install_sqlite"
|
# - uses: "./.github/shared/install_sqlite"
|
||||||
# - name: Test with index enabled
|
# - name: Test with index enabled
|
||||||
# run: SQLITE_EXEC="scripts/limbo-sqlite3-index-experimental" make test
|
# run: SQLITE_EXEC="scripts/limbo-sqlite3-index-experimental" make test
|
||||||
# timeout-minutes: 20
|
# timeout-minutes: 20
|
||||||
test-sqlite:
|
test-sqlite:
|
||||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||||
steps:
|
steps:
|
||||||
@@ -106,4 +92,3 @@ jobs:
|
|||||||
- uses: "./.github/shared/install_sqlite"
|
- uses: "./.github/shared/install_sqlite"
|
||||||
- name: Test
|
- name: Test
|
||||||
run: SQLITE_EXEC="sqlite3" make test-compat
|
run: SQLITE_EXEC="sqlite3" make test-compat
|
||||||
|
|
||||||
|
|||||||
1057
Cargo.lock
generated
1057
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
47
Cargo.toml
47
Cargo.toml
@@ -3,30 +3,29 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"bindings/dart/rust",
|
"bindings/dart/rust",
|
||||||
"bindings/go",
|
"bindings/go",
|
||||||
"bindings/java",
|
"bindings/java",
|
||||||
"bindings/javascript",
|
"bindings/javascript",
|
||||||
"bindings/python",
|
"bindings/python",
|
||||||
"bindings/rust",
|
"bindings/rust",
|
||||||
"bindings/wasm",
|
"cli",
|
||||||
"cli",
|
"core",
|
||||||
"core",
|
"extensions/completion",
|
||||||
"extensions/completion",
|
"extensions/core",
|
||||||
"extensions/core",
|
"extensions/crypto",
|
||||||
"extensions/crypto",
|
"extensions/csv",
|
||||||
"extensions/csv",
|
"extensions/ipaddr",
|
||||||
"extensions/ipaddr",
|
"extensions/percentile",
|
||||||
"extensions/percentile",
|
"extensions/regexp",
|
||||||
"extensions/regexp",
|
"extensions/tests",
|
||||||
"extensions/tests",
|
"macros",
|
||||||
"macros",
|
"simulator",
|
||||||
"simulator",
|
"sqlite3",
|
||||||
"sqlite3",
|
"stress",
|
||||||
"stress",
|
"testing/sqlite_test_ext",
|
||||||
"testing/sqlite_test_ext",
|
"tests",
|
||||||
"tests",
|
"vendored/sqlite3-parser/sqlparser_bench",
|
||||||
"vendored/sqlite3-parser/sqlparser_bench",
|
|
||||||
]
|
]
|
||||||
exclude = ["perf/latency/limbo"]
|
exclude = ["perf/latency/limbo"]
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ COPY ./bindings/java ./bindings/java/
|
|||||||
COPY ./bindings/javascript ./bindings/javascript/
|
COPY ./bindings/javascript ./bindings/javascript/
|
||||||
COPY ./bindings/python ./bindings/python/
|
COPY ./bindings/python ./bindings/python/
|
||||||
COPY ./bindings/rust ./bindings/rust/
|
COPY ./bindings/rust ./bindings/rust/
|
||||||
COPY ./bindings/wasm ./bindings/wasm/
|
|
||||||
COPY ./cli ./cli/
|
COPY ./cli ./cli/
|
||||||
COPY ./core ./core/
|
COPY ./core ./core/
|
||||||
COPY ./extensions ./extensions/
|
COPY ./extensions ./extensions/
|
||||||
@@ -35,7 +34,7 @@ RUN cargo chef prepare --bin turso_stress --recipe-path recipe.json
|
|||||||
# Build the project.
|
# Build the project.
|
||||||
#
|
#
|
||||||
|
|
||||||
FROM chef AS builder
|
FROM chef AS builder
|
||||||
|
|
||||||
ARG antithesis=true
|
ARG antithesis=true
|
||||||
|
|
||||||
@@ -62,7 +61,6 @@ COPY --from=planner /app/bindings/go ./bindings/go/
|
|||||||
COPY --from=planner /app/bindings/javascript ./bindings/javascript/
|
COPY --from=planner /app/bindings/javascript ./bindings/javascript/
|
||||||
COPY --from=planner /app/bindings/java ./bindings/java/
|
COPY --from=planner /app/bindings/java ./bindings/java/
|
||||||
COPY --from=planner /app/bindings/python ./bindings/python/
|
COPY --from=planner /app/bindings/python ./bindings/python/
|
||||||
COPY --from=planner /app/bindings/wasm ./bindings/wasm/
|
|
||||||
COPY --from=planner /app/core ./core/
|
COPY --from=planner /app/core ./core/
|
||||||
COPY --from=planner /app/extensions ./extensions/
|
COPY --from=planner /app/extensions ./extensions/
|
||||||
COPY --from=planner /app/macros ./macros/
|
COPY --from=planner /app/macros ./macros/
|
||||||
@@ -97,7 +95,7 @@ COPY --from=builder /app/target/antithesis/turso_stress /symbols
|
|||||||
COPY stress/docker-entrypoint.sh /bin
|
COPY stress/docker-entrypoint.sh /bin
|
||||||
RUN chmod +x /bin/docker-entrypoint.sh
|
RUN chmod +x /bin/docker-entrypoint.sh
|
||||||
|
|
||||||
COPY --from=builder /app/target/wheels/* /tmp
|
COPY --from=builder /app/target/wheels/* /tmp
|
||||||
RUN pip install /tmp/*.whl
|
RUN pip install /tmp/*.whl
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
15
Makefile
15
Makefile
@@ -9,7 +9,7 @@ MINIMUM_TCL_VERSION := 8.6
|
|||||||
SQLITE_EXEC ?= scripts/limbo-sqlite3
|
SQLITE_EXEC ?= scripts/limbo-sqlite3
|
||||||
RUST_LOG := off
|
RUST_LOG := off
|
||||||
|
|
||||||
all: check-rust-version check-wasm-target limbo limbo-wasm
|
all: check-rust-version limbo
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
|
||||||
check-rust-version:
|
check-rust-version:
|
||||||
@@ -39,14 +39,6 @@ check-tcl-version:
|
|||||||
| tclsh
|
| tclsh
|
||||||
.PHONY: check-tcl-version
|
.PHONY: check-tcl-version
|
||||||
|
|
||||||
check-wasm-target:
|
|
||||||
@echo "Checking wasm32-wasip1 target..."
|
|
||||||
@if ! rustup target list | grep -q "wasm32-wasip1 (installed)"; then \
|
|
||||||
echo "Installing wasm32-wasip1 target..."; \
|
|
||||||
rustup target add wasm32-wasip1; \
|
|
||||||
fi
|
|
||||||
.PHONY: check-wasm-target
|
|
||||||
|
|
||||||
limbo:
|
limbo:
|
||||||
cargo build
|
cargo build
|
||||||
.PHONY: limbo
|
.PHONY: limbo
|
||||||
@@ -55,11 +47,6 @@ limbo-c:
|
|||||||
cargo cbuild
|
cargo cbuild
|
||||||
.PHONY: limbo-c
|
.PHONY: limbo-c
|
||||||
|
|
||||||
limbo-wasm:
|
|
||||||
rustup target add wasm32-wasip1
|
|
||||||
cargo build --package limbo-wasm --target wasm32-wasip1
|
|
||||||
.PHONY: limbo-wasm
|
|
||||||
|
|
||||||
uv-sync:
|
uv-sync:
|
||||||
uv sync --all-packages
|
uv sync --all-packages
|
||||||
.PHONE: uv-sync
|
.PHONE: uv-sync
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ Turso Database is a _work-in-progress_, in-process OLTP database engine library
|
|||||||
* [Java](bindings/java)
|
* [Java](bindings/java)
|
||||||
* [Python](bindings/python)
|
* [Python](bindings/python)
|
||||||
* [Rust](bindings/rust)
|
* [Rust](bindings/rust)
|
||||||
* [WebAssembly](bindings/wasm)
|
* [WebAssembly](bindings/javascript)
|
||||||
* **Asynchronous I/O** support on Linux with `io_uring`
|
* **Asynchronous I/O** support on Linux with `io_uring`
|
||||||
* **OS support** for Linux, macOS, and Windows
|
* **OS support** for Linux, macOS, and Windows
|
||||||
|
|
||||||
|
|||||||
@@ -2,3 +2,7 @@ nodeLinker: node-modules
|
|||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-4.9.2.cjs
|
yarnPath: .yarn/releases/yarn-4.9.2.cjs
|
||||||
enableHardenedMode: false
|
enableHardenedMode: false
|
||||||
|
supportedArchitectures:
|
||||||
|
cpu:
|
||||||
|
- current
|
||||||
|
- wasm32
|
||||||
|
|||||||
1
bindings/javascript/browser.js
Normal file
1
bindings/javascript/browser.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from '@tursodatabase/turso-wasm32-wasi'
|
||||||
1915
bindings/javascript/package-lock.json
generated
1915
bindings/javascript/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,19 +6,24 @@
|
|||||||
"url": "https://github.com/tursodatabase/turso"
|
"url": "https://github.com/tursodatabase/turso"
|
||||||
},
|
},
|
||||||
"description": "The Turso database library",
|
"description": "The Turso database library",
|
||||||
"main": "wrapper.js",
|
"files": [
|
||||||
|
"wrapper.js",
|
||||||
|
"browser.js"
|
||||||
|
],
|
||||||
"types": "index.d.ts",
|
"types": "index.d.ts",
|
||||||
"napi": {
|
"napi": {
|
||||||
"binaryName": "turso",
|
"binaryName": "turso",
|
||||||
"targets": [
|
"targets": [
|
||||||
"x86_64-unknown-linux-gnu",
|
"x86_64-unknown-linux-gnu",
|
||||||
"x86_64-pc-windows-msvc",
|
"x86_64-pc-windows-msvc",
|
||||||
"universal-apple-darwin"
|
"universal-apple-darwin",
|
||||||
|
"wasm32-wasip1-threads"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@napi-rs/cli": "^3.0.4",
|
"@napi-rs/cli": "^3.0.4",
|
||||||
|
"@napi-rs/wasm-runtime": "^1.0.1",
|
||||||
"ava": "^6.0.1",
|
"ava": "^6.0.1",
|
||||||
"better-sqlite3": "^11.9.1"
|
"better-sqlite3": "^11.9.1"
|
||||||
},
|
},
|
||||||
|
|||||||
60
bindings/javascript/turso.wasi-browser.js
Normal file
60
bindings/javascript/turso.wasi-browser.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import {
|
||||||
|
createOnMessage as __wasmCreateOnMessageForFsProxy,
|
||||||
|
getDefaultContext as __emnapiGetDefaultContext,
|
||||||
|
instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync,
|
||||||
|
WASI as __WASI,
|
||||||
|
} from '@napi-rs/wasm-runtime'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const __wasi = new __WASI({
|
||||||
|
version: 'preview1',
|
||||||
|
})
|
||||||
|
|
||||||
|
const __wasmUrl = new URL('./turso.wasm32-wasi.wasm', import.meta.url).href
|
||||||
|
const __emnapiContext = __emnapiGetDefaultContext()
|
||||||
|
|
||||||
|
|
||||||
|
const __sharedMemory = new WebAssembly.Memory({
|
||||||
|
initial: 4000,
|
||||||
|
maximum: 65536,
|
||||||
|
shared: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const __wasmFile = await fetch(__wasmUrl).then((res) => res.arrayBuffer())
|
||||||
|
|
||||||
|
const {
|
||||||
|
instance: __napiInstance,
|
||||||
|
module: __wasiModule,
|
||||||
|
napiModule: __napiModule,
|
||||||
|
} = __emnapiInstantiateNapiModuleSync(__wasmFile, {
|
||||||
|
context: __emnapiContext,
|
||||||
|
asyncWorkPoolSize: 4,
|
||||||
|
wasi: __wasi,
|
||||||
|
onCreateWorker() {
|
||||||
|
const worker = new Worker(new URL('./wasi-worker-browser.mjs', import.meta.url), {
|
||||||
|
type: 'module',
|
||||||
|
})
|
||||||
|
|
||||||
|
return worker
|
||||||
|
},
|
||||||
|
overwriteImports(importObject) {
|
||||||
|
importObject.env = {
|
||||||
|
...importObject.env,
|
||||||
|
...importObject.napi,
|
||||||
|
...importObject.emnapi,
|
||||||
|
memory: __sharedMemory,
|
||||||
|
}
|
||||||
|
return importObject
|
||||||
|
},
|
||||||
|
beforeInit({ instance }) {
|
||||||
|
for (const name of Object.keys(instance.exports)) {
|
||||||
|
if (name.startsWith('__napi_register__')) {
|
||||||
|
instance.exports[name]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
export default __napiModule.exports
|
||||||
|
export const Database = __napiModule.exports.Database
|
||||||
|
export const Statement = __napiModule.exports.Statement
|
||||||
112
bindings/javascript/turso.wasi.cjs
Normal file
112
bindings/javascript/turso.wasi.cjs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
|
||||||
|
/* auto-generated by NAPI-RS */
|
||||||
|
|
||||||
|
const __nodeFs = require('node:fs')
|
||||||
|
const __nodePath = require('node:path')
|
||||||
|
const { WASI: __nodeWASI } = require('node:wasi')
|
||||||
|
const { Worker } = require('node:worker_threads')
|
||||||
|
|
||||||
|
const {
|
||||||
|
createOnMessage: __wasmCreateOnMessageForFsProxy,
|
||||||
|
getDefaultContext: __emnapiGetDefaultContext,
|
||||||
|
instantiateNapiModuleSync: __emnapiInstantiateNapiModuleSync,
|
||||||
|
} = require('@napi-rs/wasm-runtime')
|
||||||
|
|
||||||
|
const __rootDir = __nodePath.parse(process.cwd()).root
|
||||||
|
|
||||||
|
const __wasi = new __nodeWASI({
|
||||||
|
version: 'preview1',
|
||||||
|
env: process.env,
|
||||||
|
preopens: {
|
||||||
|
[__rootDir]: __rootDir,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const __emnapiContext = __emnapiGetDefaultContext()
|
||||||
|
|
||||||
|
const __sharedMemory = new WebAssembly.Memory({
|
||||||
|
initial: 4000,
|
||||||
|
maximum: 65536,
|
||||||
|
shared: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
let __wasmFilePath = __nodePath.join(__dirname, 'turso.wasm32-wasi.wasm')
|
||||||
|
const __wasmDebugFilePath = __nodePath.join(__dirname, 'turso.wasm32-wasi.debug.wasm')
|
||||||
|
|
||||||
|
if (__nodeFs.existsSync(__wasmDebugFilePath)) {
|
||||||
|
__wasmFilePath = __wasmDebugFilePath
|
||||||
|
} else if (!__nodeFs.existsSync(__wasmFilePath)) {
|
||||||
|
try {
|
||||||
|
__wasmFilePath = __nodePath.resolve('@tursodatabase/turso-wasm32-wasi')
|
||||||
|
} catch {
|
||||||
|
throw new Error('Cannot find turso.wasm32-wasi.wasm file, and @tursodatabase/turso-wasm32-wasi package is not installed.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { instance: __napiInstance, module: __wasiModule, napiModule: __napiModule } = __emnapiInstantiateNapiModuleSync(__nodeFs.readFileSync(__wasmFilePath), {
|
||||||
|
context: __emnapiContext,
|
||||||
|
asyncWorkPoolSize: (function() {
|
||||||
|
const threadsSizeFromEnv = Number(process.env.NAPI_RS_ASYNC_WORK_POOL_SIZE ?? process.env.UV_THREADPOOL_SIZE)
|
||||||
|
// NaN > 0 is false
|
||||||
|
if (threadsSizeFromEnv > 0) {
|
||||||
|
return threadsSizeFromEnv
|
||||||
|
} else {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
reuseWorker: true,
|
||||||
|
wasi: __wasi,
|
||||||
|
onCreateWorker() {
|
||||||
|
const worker = new Worker(__nodePath.join(__dirname, 'wasi-worker.mjs'), {
|
||||||
|
env: process.env,
|
||||||
|
})
|
||||||
|
worker.onmessage = ({ data }) => {
|
||||||
|
__wasmCreateOnMessageForFsProxy(__nodeFs)(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The main thread of Node.js waits for all the active handles before exiting.
|
||||||
|
// But Rust threads are never waited without `thread::join`.
|
||||||
|
// So here we hack the code of Node.js to prevent the workers from being referenced (active).
|
||||||
|
// According to https://github.com/nodejs/node/blob/19e0d472728c79d418b74bddff588bea70a403d0/lib/internal/worker.js#L415,
|
||||||
|
// a worker is consist of two handles: kPublicPort and kHandle.
|
||||||
|
{
|
||||||
|
const kPublicPort = Object.getOwnPropertySymbols(worker).find(s =>
|
||||||
|
s.toString().includes("kPublicPort")
|
||||||
|
);
|
||||||
|
if (kPublicPort) {
|
||||||
|
worker[kPublicPort].ref = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const kHandle = Object.getOwnPropertySymbols(worker).find(s =>
|
||||||
|
s.toString().includes("kHandle")
|
||||||
|
);
|
||||||
|
if (kHandle) {
|
||||||
|
worker[kHandle].ref = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.unref();
|
||||||
|
}
|
||||||
|
return worker
|
||||||
|
},
|
||||||
|
overwriteImports(importObject) {
|
||||||
|
importObject.env = {
|
||||||
|
...importObject.env,
|
||||||
|
...importObject.napi,
|
||||||
|
...importObject.emnapi,
|
||||||
|
memory: __sharedMemory,
|
||||||
|
}
|
||||||
|
return importObject
|
||||||
|
},
|
||||||
|
beforeInit({ instance }) {
|
||||||
|
for (const name of Object.keys(instance.exports)) {
|
||||||
|
if (name.startsWith('__napi_register__')) {
|
||||||
|
instance.exports[name]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
module.exports = __napiModule.exports
|
||||||
|
module.exports.Database = __napiModule.exports.Database
|
||||||
|
module.exports.Statement = __napiModule.exports.Statement
|
||||||
32
bindings/javascript/wasi-worker-browser.mjs
Normal file
32
bindings/javascript/wasi-worker-browser.mjs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { instantiateNapiModuleSync, MessageHandler, WASI } from '@napi-rs/wasm-runtime'
|
||||||
|
|
||||||
|
const handler = new MessageHandler({
|
||||||
|
onLoad({ wasmModule, wasmMemory }) {
|
||||||
|
const wasi = new WASI({
|
||||||
|
print: function () {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log.apply(console, arguments)
|
||||||
|
},
|
||||||
|
printErr: function() {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error.apply(console, arguments)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return instantiateNapiModuleSync(wasmModule, {
|
||||||
|
childThread: true,
|
||||||
|
wasi,
|
||||||
|
overwriteImports(importObject) {
|
||||||
|
importObject.env = {
|
||||||
|
...importObject.env,
|
||||||
|
...importObject.napi,
|
||||||
|
...importObject.emnapi,
|
||||||
|
memory: wasmMemory,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
globalThis.onmessage = function (e) {
|
||||||
|
handler.handle(e)
|
||||||
|
}
|
||||||
63
bindings/javascript/wasi-worker.mjs
Normal file
63
bindings/javascript/wasi-worker.mjs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import fs from "node:fs";
|
||||||
|
import { createRequire } from "node:module";
|
||||||
|
import { parse } from "node:path";
|
||||||
|
import { WASI } from "node:wasi";
|
||||||
|
import { parentPort, Worker } from "node:worker_threads";
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
|
const { instantiateNapiModuleSync, MessageHandler, getDefaultContext } = require("@napi-rs/wasm-runtime");
|
||||||
|
|
||||||
|
if (parentPort) {
|
||||||
|
parentPort.on("message", (data) => {
|
||||||
|
globalThis.onmessage({ data });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(globalThis, {
|
||||||
|
self: globalThis,
|
||||||
|
require,
|
||||||
|
Worker,
|
||||||
|
importScripts: function (f) {
|
||||||
|
;(0, eval)(fs.readFileSync(f, "utf8") + "//# sourceURL=" + f);
|
||||||
|
},
|
||||||
|
postMessage: function (msg) {
|
||||||
|
if (parentPort) {
|
||||||
|
parentPort.postMessage(msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emnapiContext = getDefaultContext();
|
||||||
|
|
||||||
|
const __rootDir = parse(process.cwd()).root;
|
||||||
|
|
||||||
|
const handler = new MessageHandler({
|
||||||
|
onLoad({ wasmModule, wasmMemory }) {
|
||||||
|
const wasi = new WASI({
|
||||||
|
version: 'preview1',
|
||||||
|
env: process.env,
|
||||||
|
preopens: {
|
||||||
|
[__rootDir]: __rootDir,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return instantiateNapiModuleSync(wasmModule, {
|
||||||
|
childThread: true,
|
||||||
|
wasi,
|
||||||
|
context: emnapiContext,
|
||||||
|
overwriteImports(importObject) {
|
||||||
|
importObject.env = {
|
||||||
|
...importObject.env,
|
||||||
|
...importObject.napi,
|
||||||
|
...importObject.emnapi,
|
||||||
|
memory: wasmMemory
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
globalThis.onmessage = function (e) {
|
||||||
|
handler.handle(e);
|
||||||
|
};
|
||||||
@@ -1073,6 +1073,7 @@ __metadata:
|
|||||||
resolution: "@tursodatabase/turso@workspace:."
|
resolution: "@tursodatabase/turso@workspace:."
|
||||||
dependencies:
|
dependencies:
|
||||||
"@napi-rs/cli": "npm:^3.0.4"
|
"@napi-rs/cli": "npm:^3.0.4"
|
||||||
|
"@napi-rs/wasm-runtime": "npm:^1.0.1"
|
||||||
ava: "npm:^6.0.1"
|
ava: "npm:^6.0.1"
|
||||||
better-sqlite3: "npm:^11.9.1"
|
better-sqlite3: "npm:^11.9.1"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
|
|||||||
5
bindings/wasm/.gitignore
vendored
5
bindings/wasm/.gitignore
vendored
@@ -1,5 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
*.wasm
|
|
||||||
**/dist/
|
|
||||||
limbo-wasm*tgz
|
|
||||||
claude.md
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "limbo-wasm"
|
|
||||||
version.workspace = true
|
|
||||||
authors.workspace = true
|
|
||||||
edition.workspace = true
|
|
||||||
license.workspace = true
|
|
||||||
repository.workspace = true
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["cdylib"]
|
|
||||||
path = "lib.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
console_error_panic_hook = "0.1.7"
|
|
||||||
js-sys = "0.3.72"
|
|
||||||
turso_core = { path = "../../core", default-features = false }
|
|
||||||
wasm-bindgen = "0.2"
|
|
||||||
wasm-bindgen-futures = "0.4"
|
|
||||||
web-sys = "0.3"
|
|
||||||
getrandom = { version = "0.2.15", features = ["js"] }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
web = []
|
|
||||||
nodejs = []
|
|
||||||
default = ["nodejs"]
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Limbo Wasm bindings
|
|
||||||
|
|
||||||
This source tree contains Limbo Wasm bindings.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
For nodejs
|
|
||||||
```
|
|
||||||
./scripts/build
|
|
||||||
```
|
|
||||||
For web
|
|
||||||
|
|
||||||
```
|
|
||||||
./scripts/build web
|
|
||||||
```
|
|
||||||
|
|
||||||
# Browser Support
|
|
||||||
|
|
||||||
Adding experimental support for limbo in the browser. This is done by adding support for OPFS as a VFS.
|
|
||||||
|
|
||||||
To see a basic example of this `npm run dev` and navigate to `http://localhost:5173/limbo-opfs-test.html` and open the console.
|
|
||||||
|
|
||||||
## Design
|
|
||||||
|
|
||||||
This design mirrors sqlite's approach for OPFS support. It has a sync api in `opfs.js` which communicates with `opfs-sync-proxy.js` via `SharedArrayBuffer` and `Atomics.wait`. This allows us to live the VFS api in `lib.rs` unchanged.
|
|
||||||
|
|
||||||
You can see `limbo-opfs-test.html` for basic usage.
|
|
||||||
|
|
||||||
## UTs
|
|
||||||
|
|
||||||
There are OPFS specific unit tests and then some basic limbo unit tests. These are run via `npm test` or `npx vitest`.
|
|
||||||
|
|
||||||
For more info and log output you can run `npx vitest:ui` but you can get some parallel execution of test cases which cause issues.
|
|
||||||
|
|
||||||
|
|
||||||
## TODO
|
|
||||||
|
|
||||||
-[] Add a wrapper js that provides a clean interface to the `limbo-worker.js`
|
|
||||||
-[] Add more tests for opfs.js operations
|
|
||||||
-[] Add error return handling
|
|
||||||
-[] Make sure posix flags for open are handled instead of just being ignored (this requires creating a mapping of behaviors from posix to opfs as far as makes sense)
|
|
||||||
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
# class Database
|
|
||||||
|
|
||||||
The `Database` class represents a connection that can prepare and execute SQL statements.
|
|
||||||
|
|
||||||
## Methods
|
|
||||||
|
|
||||||
### new Database(path) ⇒ Database
|
|
||||||
|
|
||||||
Creates a new database connection.
|
|
||||||
|
|
||||||
| Param | Type | Description |
|
|
||||||
| ------- | ------------------- | ------------------------- |
|
|
||||||
| path | <code>string</code> | Path to the database file |
|
|
||||||
|
|
||||||
### prepare(sql) ⇒ Statement
|
|
||||||
|
|
||||||
Prepares a SQL statement for execution.
|
|
||||||
|
|
||||||
| Param | Type | Description |
|
|
||||||
| ------ | ------------------- | ------------------------------------ |
|
|
||||||
| sql | <code>string</code> | The SQL statement string to prepare. |
|
|
||||||
|
|
||||||
The function returns a `Statement` object.
|
|
||||||
|
|
||||||
### transaction(function) ⇒ function
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### pragma(string, [options]) ⇒ results
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### backup(destination, [options]) ⇒ promise
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### serialize([options]) ⇒ Buffer
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### function(name, [options], function) ⇒ this
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### aggregate(name, options) ⇒ this
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### table(name, definition) ⇒ this
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### loadExtension(path, [entryPoint]) ⇒ this
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### exec(sql) ⇒ this
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### close() ⇒ this
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
# class Statement
|
|
||||||
|
|
||||||
## Methods
|
|
||||||
|
|
||||||
### run([...bindParameters]) ⇒ object
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### get([...bindParameters]) ⇒ row
|
|
||||||
|
|
||||||
Executes the SQL statement and returns the first row.
|
|
||||||
|
|
||||||
| Param | Type | Description |
|
|
||||||
| -------------- | ----------------------------- | ------------------------------------------------ |
|
|
||||||
| bindParameters | <code>array of objects</code> | The bind parameters for executing the statement. |
|
|
||||||
|
|
||||||
### all([...bindParameters]) ⇒ array of rows
|
|
||||||
|
|
||||||
Executes the SQL statement and returns an array of the resulting rows.
|
|
||||||
|
|
||||||
| Param | Type | Description |
|
|
||||||
| -------------- | ----------------------------- | ------------------------------------------------ |
|
|
||||||
| bindParameters | <code>array of objects</code> | The bind parameters for executing the statement. |
|
|
||||||
|
|
||||||
### iterate([...bindParameters]) ⇒ iterator
|
|
||||||
|
|
||||||
Executes the SQL statement and returns an iterator to the resulting rows.
|
|
||||||
|
|
||||||
| Param | Type | Description |
|
|
||||||
| -------------- | ----------------------------- | ------------------------------------------------ |
|
|
||||||
| bindParameters | <code>array of objects</code> | The bind parameters for executing the statement. |
|
|
||||||
|
|
||||||
### pluck([toggleState]) ⇒ this
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### expand([toggleState]) ⇒ this
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### raw([rawMode]) ⇒ this
|
|
||||||
|
|
||||||
Toggle raw mode.
|
|
||||||
|
|
||||||
| Param | Type | Description |
|
|
||||||
| ------- | -------------------- | --------------------------------------------------------------------------------- |
|
|
||||||
| rawMode | <code>boolean</code> | Enable or disable raw mode. If you don't pass the parameter, raw mode is enabled. |
|
|
||||||
|
|
||||||
This function enables or disables raw mode. Prepared statements return objects by default, but if raw mode is enabled, the functions return arrays instead.
|
|
||||||
|
|
||||||
### columns() ⇒ array of objects
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
|
|
||||||
### bind([...bindParameters]) ⇒ this
|
|
||||||
|
|
||||||
This function is currently not supported.
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { drizzle } from 'drizzle-orm/better-sqlite3';
|
|
||||||
import * as s from 'drizzle-orm/sqlite-core';
|
|
||||||
import { Database } from 'limbo-wasm';
|
|
||||||
|
|
||||||
const sqlite = new Database('sqlite.db');
|
|
||||||
const db = drizzle({ client: sqlite });
|
|
||||||
const users = s.sqliteTable("users", {
|
|
||||||
id: s.integer(),
|
|
||||||
name: s.text(),
|
|
||||||
})
|
|
||||||
const result = db.select().from(users).all();
|
|
||||||
console.log(result);
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { Database } from 'limbo-wasm';
|
|
||||||
|
|
||||||
const db = new Database('hello.db');
|
|
||||||
|
|
||||||
db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
|
|
||||||
|
|
||||||
db.exec('INSERT INTO users (name) VALUES (\'Alice\')');
|
|
||||||
|
|
||||||
const stmt = db.prepare('SELECT * FROM users');
|
|
||||||
|
|
||||||
const users = stmt.all();
|
|
||||||
|
|
||||||
console.log(users);
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "limbo-example",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"type": "module",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"better-sqlite3": "^11.5.0",
|
|
||||||
"drizzle-orm": "^0.36.3",
|
|
||||||
"limbo-wasm": ".."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
3707
bindings/wasm/integration-tests/package-lock.json
generated
3707
bindings/wasm/integration-tests/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "limbo-wasm-integration-tests",
|
|
||||||
"type": "module",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"test": "PROVIDER=better-sqlite3 ava tests/test.js && PROVIDER=limbo-wasm ava tests/test.js && rm *.db *.db-wal"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"ava": "^6.2.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"better-sqlite3": "^11.7.0",
|
|
||||||
"limbo-wasm": ".."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
import test from "ava";
|
|
||||||
import { unlinkSync, existsSync } from "node:fs"
|
|
||||||
|
|
||||||
test.beforeEach(async (t) => {
|
|
||||||
const [db, errorType, provider] = await connect();
|
|
||||||
// DROP TABLE IF EXISTS users; <- is not supported in lib.rs yet
|
|
||||||
db.exec(`
|
|
||||||
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)
|
|
||||||
`);
|
|
||||||
db.exec(
|
|
||||||
"INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.org')"
|
|
||||||
);
|
|
||||||
db.exec(
|
|
||||||
"INSERT INTO users (id, name, email) VALUES (2, 'Bob', 'bob@example.com')"
|
|
||||||
);
|
|
||||||
t.context = {
|
|
||||||
db,
|
|
||||||
errorType,
|
|
||||||
provider
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
test.serial("Statement.raw().all()", async (t) => {
|
|
||||||
const db = t.context.db;
|
|
||||||
|
|
||||||
const stmt = db.prepare("SELECT * FROM users");
|
|
||||||
const expected = [
|
|
||||||
[1, "Alice", "alice@example.org"],
|
|
||||||
[2, "Bob", "bob@example.com"],
|
|
||||||
];
|
|
||||||
t.deepEqual(stmt.raw().all(), expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
test.serial("Statement.raw().get()", async (t) => {
|
|
||||||
const db = t.context.db;
|
|
||||||
|
|
||||||
const stmt = db.prepare("SELECT * FROM users");
|
|
||||||
const expected = [
|
|
||||||
1, "Alice", "alice@example.org"
|
|
||||||
];
|
|
||||||
t.deepEqual(stmt.raw().get(), expected);
|
|
||||||
|
|
||||||
const emptyStmt = db.prepare("SELECT * FROM users WHERE id = -1");
|
|
||||||
t.is(emptyStmt.raw().get(), undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
test.serial("Statement.raw().iterate()", async (t) => {
|
|
||||||
const db = t.context.db;
|
|
||||||
|
|
||||||
const stmt = db.prepare("SELECT * FROM users");
|
|
||||||
const expected = [
|
|
||||||
{ done: false, value: [1, "Alice", "alice@example.org"] },
|
|
||||||
{ done: false, value: [2, "Bob", "bob@example.com"] },
|
|
||||||
{ done: true, value: undefined },
|
|
||||||
];
|
|
||||||
|
|
||||||
let iter = stmt.raw().iterate();
|
|
||||||
t.is(typeof iter[Symbol.iterator], 'function');
|
|
||||||
t.deepEqual(iter.next(), expected[0])
|
|
||||||
t.deepEqual(iter.next(), expected[1])
|
|
||||||
t.deepEqual(iter.next(), expected[2])
|
|
||||||
|
|
||||||
const emptyStmt = db.prepare("SELECT * FROM users WHERE id = -1");
|
|
||||||
t.is(typeof emptyStmt[Symbol.iterator], 'undefined');
|
|
||||||
t.throws(() => emptyStmt.next(), { instanceOf: TypeError });
|
|
||||||
});
|
|
||||||
|
|
||||||
const connect = async (path_opt) => {
|
|
||||||
// delete hello.db if it exists
|
|
||||||
if (existsSync("hello.db")) {
|
|
||||||
unlinkSync("hello.db");
|
|
||||||
}
|
|
||||||
|
|
||||||
const path = path_opt ?? "hello.db";
|
|
||||||
const provider = process.env.PROVIDER;
|
|
||||||
if (provider === "limbo-wasm") {
|
|
||||||
const database = process.env.LIBSQL_DATABASE ?? path;
|
|
||||||
const x = await import("limbo-wasm");
|
|
||||||
const options = {};
|
|
||||||
const db = new x.Database(database, options);
|
|
||||||
return [db, x.SqliteError, provider];
|
|
||||||
}
|
|
||||||
if (provider == "better-sqlite3") {
|
|
||||||
const x = await import("better-sqlite3");
|
|
||||||
const options = {};
|
|
||||||
const db = x.default(path, options);
|
|
||||||
return [db, x.SqliteError, provider];
|
|
||||||
}
|
|
||||||
throw new Error("Unknown provider: " + provider);
|
|
||||||
};
|
|
||||||
@@ -1,439 +0,0 @@
|
|||||||
#[cfg(all(feature = "web", feature = "nodejs"))]
|
|
||||||
compile_error!("Features 'web' and 'nodejs' cannot be enabled at the same time");
|
|
||||||
|
|
||||||
use js_sys::{Array, Object};
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use turso_core::{Clock, Instant, OpenFlags, Result};
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub struct Database {
|
|
||||||
db: Arc<turso_core::Database>,
|
|
||||||
conn: Arc<turso_core::Connection>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::arc_with_non_send_sync)]
|
|
||||||
#[wasm_bindgen]
|
|
||||||
impl Database {
|
|
||||||
#[wasm_bindgen(constructor)]
|
|
||||||
pub fn new(path: &str) -> Database {
|
|
||||||
let io: Arc<dyn turso_core::IO> = Arc::new(PlatformIO { vfs: VFS::new() });
|
|
||||||
let file = io.open_file(path, OpenFlags::Create, false).unwrap();
|
|
||||||
let db_file = Arc::new(DatabaseFile::new(file));
|
|
||||||
let db = turso_core::Database::open(io, path, db_file, false, false).unwrap();
|
|
||||||
let conn = db.connect().unwrap();
|
|
||||||
Database { db, conn }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn exec(&self, _sql: &str) {
|
|
||||||
self.conn.execute(_sql).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn prepare(&self, _sql: &str) -> Statement {
|
|
||||||
let stmt = self.conn.prepare(_sql).unwrap();
|
|
||||||
Statement::new(RefCell::new(stmt), false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub struct RowIterator {
|
|
||||||
inner: RefCell<turso_core::Statement>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
impl RowIterator {
|
|
||||||
fn new(inner: RefCell<turso_core::Statement>) -> Self {
|
|
||||||
Self { inner }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
#[allow(clippy::should_implement_trait)]
|
|
||||||
pub fn next(&mut self) -> JsValue {
|
|
||||||
let mut stmt = self.inner.borrow_mut();
|
|
||||||
match stmt.step() {
|
|
||||||
Ok(turso_core::StepResult::Row) => {
|
|
||||||
let row = stmt.row().unwrap();
|
|
||||||
let row_array = Array::new();
|
|
||||||
for value in row.get_values() {
|
|
||||||
let value = to_js_value(value);
|
|
||||||
row_array.push(&value);
|
|
||||||
}
|
|
||||||
JsValue::from(row_array)
|
|
||||||
}
|
|
||||||
Ok(turso_core::StepResult::IO) => JsValue::UNDEFINED,
|
|
||||||
Ok(turso_core::StepResult::Done) | Ok(turso_core::StepResult::Interrupt) => {
|
|
||||||
JsValue::UNDEFINED
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(turso_core::StepResult::Busy) => JsValue::UNDEFINED,
|
|
||||||
Err(e) => panic!("Error: {e:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub struct Statement {
|
|
||||||
inner: RefCell<turso_core::Statement>,
|
|
||||||
raw: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
impl Statement {
|
|
||||||
fn new(inner: RefCell<turso_core::Statement>, raw: bool) -> Self {
|
|
||||||
Self { inner, raw }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn raw(mut self, toggle: Option<bool>) -> Self {
|
|
||||||
self.raw = toggle.unwrap_or(true);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self) -> JsValue {
|
|
||||||
let mut stmt = self.inner.borrow_mut();
|
|
||||||
match stmt.step() {
|
|
||||||
Ok(turso_core::StepResult::Row) => {
|
|
||||||
let row = stmt.row().unwrap();
|
|
||||||
let row_array = js_sys::Array::new();
|
|
||||||
for value in row.get_values() {
|
|
||||||
let value = to_js_value(value);
|
|
||||||
row_array.push(&value);
|
|
||||||
}
|
|
||||||
JsValue::from(row_array)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(turso_core::StepResult::IO)
|
|
||||||
| Ok(turso_core::StepResult::Done)
|
|
||||||
| Ok(turso_core::StepResult::Interrupt)
|
|
||||||
| Ok(turso_core::StepResult::Busy) => JsValue::UNDEFINED,
|
|
||||||
Err(e) => panic!("Error: {e:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn all(&self) -> js_sys::Array {
|
|
||||||
let array = js_sys::Array::new();
|
|
||||||
loop {
|
|
||||||
let mut stmt = self.inner.borrow_mut();
|
|
||||||
match stmt.step() {
|
|
||||||
Ok(turso_core::StepResult::Row) => {
|
|
||||||
let row = stmt.row().unwrap();
|
|
||||||
let row_array = js_sys::Array::new();
|
|
||||||
for value in row.get_values() {
|
|
||||||
let value = to_js_value(value);
|
|
||||||
row_array.push(&value);
|
|
||||||
}
|
|
||||||
array.push(&row_array);
|
|
||||||
}
|
|
||||||
Ok(turso_core::StepResult::IO) => {}
|
|
||||||
Ok(turso_core::StepResult::Interrupt) => break,
|
|
||||||
Ok(turso_core::StepResult::Done) => break,
|
|
||||||
Ok(turso_core::StepResult::Busy) => break,
|
|
||||||
Err(e) => panic!("Error: {e:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
array
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn iterate(self) -> JsValue {
|
|
||||||
let iterator = RowIterator::new(self.inner);
|
|
||||||
let iterator_obj = Object::new();
|
|
||||||
|
|
||||||
// Define the next method that will be called by JavaScript
|
|
||||||
let next_fn = js_sys::Function::new_with_args(
|
|
||||||
"",
|
|
||||||
"const value = this.iterator.next();
|
|
||||||
const done = value === undefined;
|
|
||||||
return {
|
|
||||||
value,
|
|
||||||
done
|
|
||||||
};",
|
|
||||||
);
|
|
||||||
|
|
||||||
js_sys::Reflect::set(&iterator_obj, &JsValue::from_str("next"), &next_fn).unwrap();
|
|
||||||
|
|
||||||
js_sys::Reflect::set(
|
|
||||||
&iterator_obj,
|
|
||||||
&JsValue::from_str("iterator"),
|
|
||||||
&JsValue::from(iterator),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let symbol_iterator = js_sys::Function::new_no_args("return this;");
|
|
||||||
js_sys::Reflect::set(&iterator_obj, &js_sys::Symbol::iterator(), &symbol_iterator).unwrap();
|
|
||||||
|
|
||||||
JsValue::from(iterator_obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_js_value(value: &turso_core::Value) -> JsValue {
|
|
||||||
match value {
|
|
||||||
turso_core::Value::Null => JsValue::null(),
|
|
||||||
turso_core::Value::Integer(i) => {
|
|
||||||
let i = *i;
|
|
||||||
if i >= i32::MIN as i64 && i <= i32::MAX as i64 {
|
|
||||||
JsValue::from(i as i32)
|
|
||||||
} else {
|
|
||||||
JsValue::from(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
turso_core::Value::Float(f) => JsValue::from(*f),
|
|
||||||
turso_core::Value::Text(t) => JsValue::from_str(t.as_str()),
|
|
||||||
turso_core::Value::Blob(b) => js_sys::Uint8Array::from(b.as_slice()).into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct File {
|
|
||||||
vfs: VFS,
|
|
||||||
fd: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for File {}
|
|
||||||
unsafe impl Sync for File {}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl File {
|
|
||||||
fn new(vfs: VFS, fd: i32) -> Self {
|
|
||||||
Self { vfs, fd }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl turso_core::File for File {
|
|
||||||
fn lock_file(&self, _exclusive: bool) -> Result<()> {
|
|
||||||
// TODO
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unlock_file(&self) -> Result<()> {
|
|
||||||
// TODO
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pread(
|
|
||||||
&self,
|
|
||||||
pos: usize,
|
|
||||||
c: Arc<turso_core::Completion>,
|
|
||||||
) -> Result<Arc<turso_core::Completion>> {
|
|
||||||
let r = match c.completion_type {
|
|
||||||
turso_core::CompletionType::Read(ref r) => r,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let nr = {
|
|
||||||
let mut buf = r.buf_mut();
|
|
||||||
let buf: &mut [u8] = buf.as_mut_slice();
|
|
||||||
self.vfs.pread(self.fd, buf, pos)
|
|
||||||
};
|
|
||||||
r.complete(nr);
|
|
||||||
#[allow(clippy::arc_with_non_send_sync)]
|
|
||||||
Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pwrite(
|
|
||||||
&self,
|
|
||||||
pos: usize,
|
|
||||||
buffer: Arc<std::cell::RefCell<turso_core::Buffer>>,
|
|
||||||
c: Arc<turso_core::Completion>,
|
|
||||||
) -> Result<Arc<turso_core::Completion>> {
|
|
||||||
let w = match c.completion_type {
|
|
||||||
turso_core::CompletionType::Write(ref w) => w,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let buf = buffer.borrow();
|
|
||||||
let buf: &[u8] = buf.as_slice();
|
|
||||||
self.vfs.pwrite(self.fd, buf, pos);
|
|
||||||
w.complete(buf.len() as i32);
|
|
||||||
#[allow(clippy::arc_with_non_send_sync)]
|
|
||||||
Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sync(&self, c: Arc<turso_core::Completion>) -> Result<Arc<turso_core::Completion>> {
|
|
||||||
self.vfs.sync(self.fd);
|
|
||||||
c.complete(0);
|
|
||||||
#[allow(clippy::arc_with_non_send_sync)]
|
|
||||||
Ok(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> Result<u64> {
|
|
||||||
Ok(self.vfs.size(self.fd))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct PlatformIO {
|
|
||||||
vfs: VFS,
|
|
||||||
}
|
|
||||||
unsafe impl Send for PlatformIO {}
|
|
||||||
unsafe impl Sync for PlatformIO {}
|
|
||||||
|
|
||||||
impl Clock for PlatformIO {
|
|
||||||
fn now(&self) -> Instant {
|
|
||||||
let date = Date::new();
|
|
||||||
let ms_since_epoch = date.getTime();
|
|
||||||
|
|
||||||
Instant {
|
|
||||||
secs: (ms_since_epoch / 1000.0) as i64,
|
|
||||||
micros: ((ms_since_epoch % 1000.0) * 1000.0) as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl turso_core::IO for PlatformIO {
|
|
||||||
fn open_file(
|
|
||||||
&self,
|
|
||||||
path: &str,
|
|
||||||
_flags: OpenFlags,
|
|
||||||
_direct: bool,
|
|
||||||
) -> Result<Arc<dyn turso_core::File>> {
|
|
||||||
let fd = self.vfs.open(path, "a+");
|
|
||||||
Ok(Arc::new(File {
|
|
||||||
vfs: VFS::new(),
|
|
||||||
fd,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wait_for_completion(&self, c: Arc<turso_core::Completion>) -> Result<()> {
|
|
||||||
while !c.is_completed() {
|
|
||||||
self.run_once()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_once(&self) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_random_number(&self) -> i64 {
|
|
||||||
let mut buf = [0u8; 8];
|
|
||||||
getrandom::getrandom(&mut buf).unwrap();
|
|
||||||
i64::from_ne_bytes(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_memory_io(&self) -> Arc<turso_core::MemoryIO> {
|
|
||||||
Arc::new(turso_core::MemoryIO::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
extern "C" {
|
|
||||||
type Date;
|
|
||||||
|
|
||||||
#[wasm_bindgen(constructor)]
|
|
||||||
fn new() -> Date;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method, getter)]
|
|
||||||
fn toISOString(this: &Date) -> String;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn getTime(this: &Date) -> f64;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DatabaseFile {
|
|
||||||
file: Arc<dyn turso_core::File>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for DatabaseFile {}
|
|
||||||
unsafe impl Sync for DatabaseFile {}
|
|
||||||
|
|
||||||
impl DatabaseFile {
|
|
||||||
pub fn new(file: Arc<dyn turso_core::File>) -> Self {
|
|
||||||
Self { file }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl turso_core::DatabaseStorage for DatabaseFile {
|
|
||||||
fn read_page(&self, page_idx: usize, c: turso_core::Completion) -> Result<()> {
|
|
||||||
let r = match c.completion_type {
|
|
||||||
turso_core::CompletionType::Read(ref r) => r,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let size = r.buf().len();
|
|
||||||
assert!(page_idx > 0);
|
|
||||||
if !(512..=65536).contains(&size) || size & (size - 1) != 0 {
|
|
||||||
return Err(turso_core::LimboError::NotADB);
|
|
||||||
}
|
|
||||||
let pos = (page_idx - 1) * size;
|
|
||||||
self.file.pread(pos, c.into())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_page(
|
|
||||||
&self,
|
|
||||||
page_idx: usize,
|
|
||||||
buffer: Arc<std::cell::RefCell<turso_core::Buffer>>,
|
|
||||||
c: turso_core::Completion,
|
|
||||||
) -> Result<()> {
|
|
||||||
let size = buffer.borrow().len();
|
|
||||||
let pos = (page_idx - 1) * size;
|
|
||||||
self.file.pwrite(pos, buffer, c.into())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sync(&self, c: turso_core::Completion) -> Result<()> {
|
|
||||||
let _ = self.file.sync(c.into())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> Result<u64> {
|
|
||||||
self.file.size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "web", not(feature = "nodejs")))]
|
|
||||||
#[wasm_bindgen(module = "/web/src/web-vfs.js")]
|
|
||||||
extern "C" {
|
|
||||||
type VFS;
|
|
||||||
#[wasm_bindgen(constructor)]
|
|
||||||
fn new() -> VFS;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn open(this: &VFS, path: &str, flags: &str) -> i32;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn close(this: &VFS, fd: i32) -> bool;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn pwrite(this: &VFS, fd: i32, buffer: &[u8], offset: usize) -> i32;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn pread(this: &VFS, fd: i32, buffer: &mut [u8], offset: usize) -> i32;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn size(this: &VFS, fd: i32) -> u64;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn sync(this: &VFS, fd: i32);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "nodejs", not(feature = "web")))]
|
|
||||||
#[wasm_bindgen(module = "/node/src/vfs.cjs")]
|
|
||||||
extern "C" {
|
|
||||||
type VFS;
|
|
||||||
#[wasm_bindgen(constructor)]
|
|
||||||
fn new() -> VFS;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn open(this: &VFS, path: &str, flags: &str) -> i32;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn close(this: &VFS, fd: i32) -> bool;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn pwrite(this: &VFS, fd: i32, buffer: &[u8], offset: usize) -> i32;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn pread(this: &VFS, fd: i32, buffer: &mut [u8], offset: usize) -> i32;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn size(this: &VFS, fd: i32) -> u64;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method)]
|
|
||||||
fn sync(this: &VFS, fd: i32);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen(start)]
|
|
||||||
pub fn init() {
|
|
||||||
console_error_panic_hook::set_once();
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
const fs = require('node:fs');
|
|
||||||
|
|
||||||
class VFS {
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
|
|
||||||
open(path, flags) {
|
|
||||||
return fs.openSync(path, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fd) {
|
|
||||||
fs.closeSync(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
pread(fd, buffer, offset) {
|
|
||||||
return fs.readSync(fd, buffer, 0, buffer.length, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
pwrite(fd, buffer, offset) {
|
|
||||||
return fs.writeSync(fd, buffer, 0, buffer.length, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
size(fd) {
|
|
||||||
let stats = fs.fstatSync(fd);
|
|
||||||
return BigInt(stats.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
sync(fd) {
|
|
||||||
fs.fsyncSync(fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { VFS };
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "limbo-perf",
|
|
||||||
"type": "module",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"limbo-wasm": "../pkg",
|
|
||||||
"better-sqlite3": "^9.5.0",
|
|
||||||
"libsql": "..",
|
|
||||||
"mitata": "^0.1.11"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import { run, bench, group, baseline } from 'mitata';
|
|
||||||
|
|
||||||
import Database from 'better-sqlite3';
|
|
||||||
|
|
||||||
const db = new Database('better-sqlite3.db');
|
|
||||||
|
|
||||||
db.exec('CREATE TABLE t (x)');
|
|
||||||
db.exec('INSERT INTO t VALUES (1)');
|
|
||||||
db.exec('INSERT INTO t VALUES (2)');
|
|
||||||
db.exec('INSERT INTO t VALUES (3)');
|
|
||||||
|
|
||||||
group('SQL queries [all()]', () => {
|
|
||||||
const stmt1 = db.prepare("SELECT 1");
|
|
||||||
bench('SELECT 1', () => {
|
|
||||||
stmt1.all();
|
|
||||||
});
|
|
||||||
const stmt2 = db.prepare("SELECT * FROM t");
|
|
||||||
bench('SELECT * FROM t', () => {
|
|
||||||
stmt2.all();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('SQL queries [iterate()]', () => {
|
|
||||||
const stmt1 = db.prepare("SELECT 1");
|
|
||||||
const it1 = stmt1.iterate();
|
|
||||||
bench('SELECT 1', () => {
|
|
||||||
it1.next();
|
|
||||||
});
|
|
||||||
const stmt2 = db.prepare("SELECT * FROM t");
|
|
||||||
const it2 = stmt2.iterate();
|
|
||||||
bench('SELECT * FROM t', () => {
|
|
||||||
it2.next();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await run({
|
|
||||||
units: false,
|
|
||||||
silent: false,
|
|
||||||
avg: true,
|
|
||||||
json: false,
|
|
||||||
colors: true,
|
|
||||||
min_max: true,
|
|
||||||
percentiles: true,
|
|
||||||
});
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import { run, bench, group, baseline } from 'mitata';
|
|
||||||
|
|
||||||
import { Database } from 'limbo-wasm';
|
|
||||||
|
|
||||||
const db = new Database('limbo.db');
|
|
||||||
|
|
||||||
db.exec('CREATE TABLE t (x)');
|
|
||||||
db.exec('INSERT INTO t VALUES (1)');
|
|
||||||
db.exec('INSERT INTO t VALUES (2)');
|
|
||||||
db.exec('INSERT INTO t VALUES (3)');
|
|
||||||
|
|
||||||
group('SQL queries [all()]', () => {
|
|
||||||
const stmt1 = db.prepare("SELECT 1");
|
|
||||||
bench('SELECT 1', () => {
|
|
||||||
stmt1.all();
|
|
||||||
});
|
|
||||||
const stmt2 = db.prepare("SELECT * FROM t");
|
|
||||||
bench('SELECT * FROM t', () => {
|
|
||||||
stmt2.all();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('SQL queries [iterate()]', () => {
|
|
||||||
const stmt1 = db.prepare("SELECT 1");
|
|
||||||
const it1 = stmt1.iterate();
|
|
||||||
bench('SELECT 1', () => {
|
|
||||||
it1.next();
|
|
||||||
});
|
|
||||||
const stmt2 = db.prepare("SELECT * FROM t");
|
|
||||||
const it2 = stmt2.iterate();
|
|
||||||
bench('SELECT * FROM t', () => {
|
|
||||||
it2.next();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
await run({
|
|
||||||
units: false,
|
|
||||||
silent: false,
|
|
||||||
avg: true,
|
|
||||||
json: false,
|
|
||||||
colors: true,
|
|
||||||
min_max: true,
|
|
||||||
percentiles: true,
|
|
||||||
});
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Define final output directories
|
|
||||||
NODE_DIR="node"
|
|
||||||
WEB_DIR="web"
|
|
||||||
|
|
||||||
rm -rf $NODE_DIR/dist $WEB_DIR/dist pkg
|
|
||||||
mkdir -p $NODE_DIR/dist $WEB_DIR/dist pkg
|
|
||||||
|
|
||||||
# Build Node.js target
|
|
||||||
npx wasm-pack build \
|
|
||||||
--target nodejs \
|
|
||||||
--out-name index \
|
|
||||||
--no-default-features \
|
|
||||||
--features nodejs
|
|
||||||
|
|
||||||
rm -rf pkg/package.json # don't want generated package.json
|
|
||||||
mv pkg/* $NODE_DIR/dist/
|
|
||||||
cp -r $NODE_DIR/src/* $NODE_DIR/dist/
|
|
||||||
cd node/dist/
|
|
||||||
for f in *.js; do
|
|
||||||
cp "$f" "${f%.js}.cjs"
|
|
||||||
done
|
|
||||||
rm *.js
|
|
||||||
cd ../../
|
|
||||||
rm -r pkg
|
|
||||||
|
|
||||||
# Build web target
|
|
||||||
npx wasm-pack build \
|
|
||||||
--target web \
|
|
||||||
--out-name index \
|
|
||||||
--no-default-features \
|
|
||||||
--features web
|
|
||||||
|
|
||||||
rm -rf pkg/package.json # don't want generated package.json
|
|
||||||
mv pkg/* $WEB_DIR/dist/
|
|
||||||
cp -r $WEB_DIR/src/* $WEB_DIR/dist/
|
|
||||||
|
|
||||||
|
|
||||||
# mv $WEB_DIR/index.js $WEB_DIR/index.mjs
|
|
||||||
rm -rf pkg
|
|
||||||
1
bindings/wasm/test-limbo-pkg/.gitignore
vendored
1
bindings/wasm/test-limbo-pkg/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
test.db
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
// import { Database } from "limbo-wasm/node";
|
|
||||||
|
|
||||||
const { Database } = require("limbo-wasm");
|
|
||||||
// Rest of your code...
|
|
||||||
|
|
||||||
const db = new Database("test.db");
|
|
||||||
db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
|
|
||||||
db.exec("INSERT INTO users (name) VALUES ('test')");
|
|
||||||
|
|
||||||
const stmt = db.prepare("SELECT * FROM users");
|
|
||||||
console.log(stmt.all());
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Limbo Test</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script type="module">
|
|
||||||
function waitForMessage(worker, type, op) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const handler = (e) => {
|
|
||||||
if (e.data.type === type && (!op || e.data.op === op)) {
|
|
||||||
worker.removeEventListener('message', handler);
|
|
||||||
resolve(e.data);
|
|
||||||
} else if (e.data.type === 'error') {
|
|
||||||
worker.removeEventListener('message', handler);
|
|
||||||
reject(e.data.error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
worker.addEventListener('message', handler);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runTests() {
|
|
||||||
// const worker = new Worker('./src/limbo-worker.js', { type: 'module' });
|
|
||||||
// const worker = new Worker('limbo-wasm/src/limbo-worker.js', { type: 'module' });
|
|
||||||
// const worker = new Worker('./node_modules/limbo-wasm/src/limbo-worker.js', { type: 'module' });
|
|
||||||
const worker = new Worker(new URL('limbo-wasm/limbo-worker.js', import.meta.url), { type: 'module' });
|
|
||||||
|
|
||||||
// Wait for ready then send createDb
|
|
||||||
await waitForMessage(worker, 'ready');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'createDb',
|
|
||||||
path: 'test.db'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for createDb success then send exec
|
|
||||||
await waitForMessage(worker, 'success', 'createDb');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'exec',
|
|
||||||
sql: `
|
|
||||||
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT);
|
|
||||||
`
|
|
||||||
});
|
|
||||||
console.log("made it here");
|
|
||||||
|
|
||||||
// Wait for exec success then send prepare
|
|
||||||
await waitForMessage(worker, 'success', 'exec');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'exec',
|
|
||||||
sql: `
|
|
||||||
INSERT INTO users VALUES (1, 'Alice', 'alice@example.org');
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitForMessage(worker, 'success', 'exec');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'exec',
|
|
||||||
sql: `
|
|
||||||
INSERT INTO users VALUES (2, 'Bob', 'bob@example.org');
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitForMessage(worker, 'success', 'exec');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'exec',
|
|
||||||
sql: `
|
|
||||||
INSERT INTO users VALUES (3, 'bill', 'bill@example.com');
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for exec success then send prepare
|
|
||||||
await waitForMessage(worker, 'success', 'exec');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'prepare',
|
|
||||||
sql: 'SELECT * FROM users;'
|
|
||||||
});
|
|
||||||
|
|
||||||
const results = await waitForMessage(worker, 'result');
|
|
||||||
console.log('Query results:', results);
|
|
||||||
}
|
|
||||||
|
|
||||||
runTests().catch(console.error);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
962
bindings/wasm/test-limbo-pkg/package-lock.json
generated
962
bindings/wasm/test-limbo-pkg/package-lock.json
generated
@@ -1,962 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "test-limbo",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "test-limbo",
|
|
||||||
"dependencies": {
|
|
||||||
"limbo-wasm": ".."
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"vite": "^6.2.6",
|
|
||||||
"vite-plugin-wasm": "^3.4.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"..": {},
|
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"aix"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-arm": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-arm64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-x64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-arm": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==",
|
|
||||||
"cpu": [
|
|
||||||
"loong64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==",
|
|
||||||
"cpu": [
|
|
||||||
"mips64el"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==",
|
|
||||||
"cpu": [
|
|
||||||
"riscv64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==",
|
|
||||||
"cpu": [
|
|
||||||
"s390x"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-x64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/netbsd-arm64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"netbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"netbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openbsd-arm64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"sunos"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-x64": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ==",
|
|
||||||
"cpu": [
|
|
||||||
"loong64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw==",
|
|
||||||
"cpu": [
|
|
||||||
"riscv64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA==",
|
|
||||||
"cpu": [
|
|
||||||
"s390x"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"node_modules/@types/estree": {
|
|
||||||
"version": "1.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
|
||||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/esbuild": {
|
|
||||||
"version": "0.25.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz",
|
|
||||||
"integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"esbuild": "bin/esbuild"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@esbuild/aix-ppc64": "0.25.2",
|
|
||||||
"@esbuild/android-arm": "0.25.2",
|
|
||||||
"@esbuild/android-arm64": "0.25.2",
|
|
||||||
"@esbuild/android-x64": "0.25.2",
|
|
||||||
"@esbuild/darwin-arm64": "0.25.2",
|
|
||||||
"@esbuild/darwin-x64": "0.25.2",
|
|
||||||
"@esbuild/freebsd-arm64": "0.25.2",
|
|
||||||
"@esbuild/freebsd-x64": "0.25.2",
|
|
||||||
"@esbuild/linux-arm": "0.25.2",
|
|
||||||
"@esbuild/linux-arm64": "0.25.2",
|
|
||||||
"@esbuild/linux-ia32": "0.25.2",
|
|
||||||
"@esbuild/linux-loong64": "0.25.2",
|
|
||||||
"@esbuild/linux-mips64el": "0.25.2",
|
|
||||||
"@esbuild/linux-ppc64": "0.25.2",
|
|
||||||
"@esbuild/linux-riscv64": "0.25.2",
|
|
||||||
"@esbuild/linux-s390x": "0.25.2",
|
|
||||||
"@esbuild/linux-x64": "0.25.2",
|
|
||||||
"@esbuild/netbsd-arm64": "0.25.2",
|
|
||||||
"@esbuild/netbsd-x64": "0.25.2",
|
|
||||||
"@esbuild/openbsd-arm64": "0.25.2",
|
|
||||||
"@esbuild/openbsd-x64": "0.25.2",
|
|
||||||
"@esbuild/sunos-x64": "0.25.2",
|
|
||||||
"@esbuild/win32-arm64": "0.25.2",
|
|
||||||
"@esbuild/win32-ia32": "0.25.2",
|
|
||||||
"@esbuild/win32-x64": "0.25.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/fsevents": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/limbo-wasm": {
|
|
||||||
"resolved": "..",
|
|
||||||
"link": true
|
|
||||||
},
|
|
||||||
"node_modules/nanoid": {
|
|
||||||
"version": "3.3.11",
|
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
|
||||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ai"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"nanoid": "bin/nanoid.cjs"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/picocolors": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/postcss": {
|
|
||||||
"version": "8.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
|
|
||||||
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/postcss/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "tidelift",
|
|
||||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ai"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"nanoid": "^3.3.8",
|
|
||||||
"picocolors": "^1.1.1",
|
|
||||||
"source-map-js": "^1.2.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12 || >=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rollup": {
|
|
||||||
"version": "4.30.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.30.1.tgz",
|
|
||||||
"integrity": "sha512-mlJ4glW020fPuLi7DkM/lN97mYEZGWeqBnrljzN0gs7GLctqX3lNWxKQ7Gl712UAX+6fog/L3jh4gb7R6aVi3w==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/estree": "1.0.6"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"rollup": "dist/bin/rollup"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0",
|
|
||||||
"npm": ">=8.0.0"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@rollup/rollup-android-arm-eabi": "4.30.1",
|
|
||||||
"@rollup/rollup-android-arm64": "4.30.1",
|
|
||||||
"@rollup/rollup-darwin-arm64": "4.30.1",
|
|
||||||
"@rollup/rollup-darwin-x64": "4.30.1",
|
|
||||||
"@rollup/rollup-freebsd-arm64": "4.30.1",
|
|
||||||
"@rollup/rollup-freebsd-x64": "4.30.1",
|
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.30.1",
|
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.30.1",
|
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.30.1",
|
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.30.1",
|
|
||||||
"@rollup/rollup-linux-loongarch64-gnu": "4.30.1",
|
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.30.1",
|
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.30.1",
|
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.30.1",
|
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.30.1",
|
|
||||||
"@rollup/rollup-linux-x64-musl": "4.30.1",
|
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.30.1",
|
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.30.1",
|
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.30.1",
|
|
||||||
"fsevents": "~2.3.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/source-map-js": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
|
||||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vite": {
|
|
||||||
"version": "6.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz",
|
|
||||||
"integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"esbuild": "^0.25.0",
|
|
||||||
"postcss": "^8.5.3",
|
|
||||||
"rollup": "^4.30.1"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"vite": "bin/vite.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"fsevents": "~2.3.3"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
|
|
||||||
"jiti": ">=1.21.0",
|
|
||||||
"less": "*",
|
|
||||||
"lightningcss": "^1.21.0",
|
|
||||||
"sass": "*",
|
|
||||||
"sass-embedded": "*",
|
|
||||||
"stylus": "*",
|
|
||||||
"sugarss": "*",
|
|
||||||
"terser": "^5.16.0",
|
|
||||||
"tsx": "^4.8.1",
|
|
||||||
"yaml": "^2.4.2"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@types/node": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"jiti": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"less": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"lightningcss": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"sass": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"sass-embedded": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"stylus": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"sugarss": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"terser": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"tsx": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"yaml": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vite-plugin-wasm": {
|
|
||||||
"version": "3.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/vite-plugin-wasm/-/vite-plugin-wasm-3.4.1.tgz",
|
|
||||||
"integrity": "sha512-ja3nSo2UCkVeitltJGkS3pfQHAanHv/DqGatdI39ja6McgABlpsZ5hVgl6wuR8Qx5etY3T5qgDQhOWzc5RReZA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"vite": "^2 || ^3 || ^4 || ^5 || ^6"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "test-limbo",
|
|
||||||
"private": true,
|
|
||||||
"type": "module",
|
|
||||||
"dependencies": {
|
|
||||||
"limbo-wasm": ".."
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"vite": "^6.2.6",
|
|
||||||
"vite-plugin-wasm": "^3.4.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import { defineConfig } from "vite";
|
|
||||||
import wasm from "vite-plugin-wasm";
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [wasm()],
|
|
||||||
server: {
|
|
||||||
headers: {
|
|
||||||
"Cross-Origin-Embedder-Policy": "require-corp",
|
|
||||||
"Cross-Origin-Opener-Policy": "same-origin",
|
|
||||||
"Cross-Origin-Resource-Policy": "cross-origin",
|
|
||||||
},
|
|
||||||
fs: {
|
|
||||||
allow: ["../web/dist"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
worker: {
|
|
||||||
format: "es",
|
|
||||||
rollupOptions: {
|
|
||||||
output: {
|
|
||||||
format: "es",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<body>
|
|
||||||
<script type="module">
|
|
||||||
import { VFSInterface } from './src/opfs-interface.js';
|
|
||||||
window.VFSInterface = VFSInterface;
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Limbo Test</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script type="module">
|
|
||||||
function waitForMessage(worker, type, op) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const handler = (e) => {
|
|
||||||
if (e.data.type === type && (!op || e.data.op === op)) {
|
|
||||||
worker.removeEventListener('message', handler);
|
|
||||||
resolve(e.data);
|
|
||||||
} else if (e.data.type === 'error') {
|
|
||||||
worker.removeEventListener('message', handler);
|
|
||||||
reject(e.data.error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
worker.addEventListener('message', handler);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runTests() {
|
|
||||||
const worker = new Worker('./src/limbo-worker.js', { type: 'module' });
|
|
||||||
|
|
||||||
// Wait for ready then send createDb
|
|
||||||
await waitForMessage(worker, 'ready');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'createDb',
|
|
||||||
path: 'test.db'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for createDb success then send exec
|
|
||||||
await waitForMessage(worker, 'success', 'createDb');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'exec',
|
|
||||||
sql: `
|
|
||||||
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT);
|
|
||||||
`
|
|
||||||
});
|
|
||||||
console.log("made it here");
|
|
||||||
|
|
||||||
// Wait for exec success then send prepare
|
|
||||||
await waitForMessage(worker, 'success', 'exec');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'exec',
|
|
||||||
sql: `
|
|
||||||
INSERT INTO users VALUES (1, 'Alice', 'alice@example.org');
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitForMessage(worker, 'success', 'exec');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'exec',
|
|
||||||
sql: `
|
|
||||||
INSERT INTO users VALUES (2, 'Bob', 'bob@example.org');
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
await waitForMessage(worker, 'success', 'exec');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'exec',
|
|
||||||
sql: `
|
|
||||||
INSERT INTO users VALUES (3, 'bill', 'bill@example.com');
|
|
||||||
`
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait for exec success then send prepare
|
|
||||||
await waitForMessage(worker, 'success', 'exec');
|
|
||||||
worker.postMessage({
|
|
||||||
op: 'prepare',
|
|
||||||
sql: 'SELECT * FROM users;'
|
|
||||||
});
|
|
||||||
|
|
||||||
const results = await waitForMessage(worker, 'result');
|
|
||||||
console.log('Query results:', results);
|
|
||||||
}
|
|
||||||
|
|
||||||
runTests().catch(console.error);
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Limbo Test</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script type="module">
|
|
||||||
window.Worker = Worker;
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
// Using Playwright (recommended)
|
|
||||||
import { expect, test } from "@playwright/test";
|
|
||||||
|
|
||||||
// playwright.config.js
|
|
||||||
export default {
|
|
||||||
use: {
|
|
||||||
headless: true,
|
|
||||||
// Required for SharedArrayBuffer
|
|
||||||
launchOptions: {
|
|
||||||
args: ["--cross-origin-isolated"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
import { VFS } from "./opfs.js";
|
|
||||||
import init, { Database } from "../dist/index.js";
|
|
||||||
|
|
||||||
let db = null;
|
|
||||||
let currentStmt = null;
|
|
||||||
|
|
||||||
async function initVFS() {
|
|
||||||
const vfs = new VFS();
|
|
||||||
await vfs.ready;
|
|
||||||
self.vfs = vfs;
|
|
||||||
return vfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initAll() {
|
|
||||||
await initVFS();
|
|
||||||
await init();
|
|
||||||
}
|
|
||||||
|
|
||||||
initAll().then(() => {
|
|
||||||
self.postMessage({ type: "ready" });
|
|
||||||
|
|
||||||
self.onmessage = (e) => {
|
|
||||||
try {
|
|
||||||
switch (e.data.op) {
|
|
||||||
case "createDb": {
|
|
||||||
db = new Database(e.data.path);
|
|
||||||
self.postMessage({ type: "success", op: "createDb" });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "exec": {
|
|
||||||
log(e.data.sql);
|
|
||||||
db.exec(e.data.sql);
|
|
||||||
self.postMessage({ type: "success", op: "exec" });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "prepare": {
|
|
||||||
currentStmt = db.prepare(e.data.sql);
|
|
||||||
const results = currentStmt.raw().all();
|
|
||||||
self.postMessage({ type: "result", result: results });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "get": {
|
|
||||||
const row = currentStmt?.raw().get();
|
|
||||||
self.postMessage({ type: "result", result: row });
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
self.postMessage({ type: "error", error: err.toString() });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}).catch((error) => {
|
|
||||||
self.postMessage({ type: "error", error: error.toString() });
|
|
||||||
});
|
|
||||||
|
|
||||||
// logLevel:
|
|
||||||
//
|
|
||||||
// 0 = no logging output
|
|
||||||
// 1 = only errors
|
|
||||||
// 2 = warnings and errors
|
|
||||||
// 3 = debug, warnings, and errors
|
|
||||||
const logLevel = 1;
|
|
||||||
|
|
||||||
const loggers = {
|
|
||||||
0: console.error.bind(console),
|
|
||||||
1: console.warn.bind(console),
|
|
||||||
2: console.log.bind(console),
|
|
||||||
};
|
|
||||||
const logImpl = (level, ...args) => {
|
|
||||||
if (logLevel > level) loggers[level]("OPFS asyncer:", ...args);
|
|
||||||
};
|
|
||||||
const log = (...args) => logImpl(2, ...args);
|
|
||||||
const warn = (...args) => logImpl(1, ...args);
|
|
||||||
const error = (...args) => logImpl(0, ...args);
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
export class VFSInterface {
|
|
||||||
constructor(workerUrl) {
|
|
||||||
this.worker = new Worker(workerUrl, { type: "module" });
|
|
||||||
this.nextMessageId = 1;
|
|
||||||
this.pendingRequests = new Map();
|
|
||||||
|
|
||||||
this.worker.onmessage = (event) => {
|
|
||||||
console.log("interface onmessage: ", event.data);
|
|
||||||
let { id, result, error } = event.data;
|
|
||||||
const resolver = this.pendingRequests.get(id);
|
|
||||||
if (event.data?.buffer && event.data?.size) {
|
|
||||||
result = { size: event.data.size, buffer: event.data.buffer };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resolver) {
|
|
||||||
this.pendingRequests.delete(id);
|
|
||||||
if (error) {
|
|
||||||
resolver.reject(new Error(error));
|
|
||||||
} else {
|
|
||||||
resolver.resolve(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_sendMessage(method, args) {
|
|
||||||
const id = this.nextMessageId++;
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
this.pendingRequests.set(id, { resolve, reject });
|
|
||||||
this.worker.postMessage({ id, method, args });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async open(path, flags) {
|
|
||||||
return await this._sendMessage("open", { path, flags });
|
|
||||||
}
|
|
||||||
|
|
||||||
async close(fd) {
|
|
||||||
return await this._sendMessage("close", { fd });
|
|
||||||
}
|
|
||||||
|
|
||||||
async pwrite(fd, buffer, offset) {
|
|
||||||
return await this._sendMessage("pwrite", { fd, buffer, offset }, [
|
|
||||||
buffer.buffer,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async pread(fd, buffer, offset) {
|
|
||||||
console.log("interface in buffer: ", [...buffer]);
|
|
||||||
const result = await this._sendMessage("pread", {
|
|
||||||
fd,
|
|
||||||
buffer: buffer,
|
|
||||||
offset,
|
|
||||||
}, []);
|
|
||||||
console.log("interface out buffer: ", [...buffer]);
|
|
||||||
buffer.set(new Uint8Array(result.buffer));
|
|
||||||
return buffer.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
async size(fd) {
|
|
||||||
return await this._sendMessage("size", { fd });
|
|
||||||
}
|
|
||||||
|
|
||||||
async sync(fd) {
|
|
||||||
return await this._sendMessage("sync", { fd });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
// opfs-sync-proxy.js
|
|
||||||
let transferBuffer, statusBuffer, statusArray, statusView;
|
|
||||||
let transferArray;
|
|
||||||
let rootDir = null;
|
|
||||||
const handles = new Map();
|
|
||||||
let nextFd = 1;
|
|
||||||
|
|
||||||
self.postMessage("ready");
|
|
||||||
|
|
||||||
onmessage = async (e) => {
|
|
||||||
log("handle message: ", e.data);
|
|
||||||
if (e.data.cmd === "init") {
|
|
||||||
log("init");
|
|
||||||
transferBuffer = e.data.transferBuffer;
|
|
||||||
statusBuffer = e.data.statusBuffer;
|
|
||||||
|
|
||||||
transferArray = new Uint8Array(transferBuffer);
|
|
||||||
statusArray = new Int32Array(statusBuffer);
|
|
||||||
statusView = new DataView(statusBuffer);
|
|
||||||
|
|
||||||
self.postMessage("done");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await handleCommand(e.data);
|
|
||||||
sendResult(result);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.onerror = (error) => {
|
|
||||||
console.error("opfssync error: ", error);
|
|
||||||
// Don't close, keep running
|
|
||||||
return true; // Prevents default error handling
|
|
||||||
};
|
|
||||||
|
|
||||||
function handleCommand(msg) {
|
|
||||||
log(`handle message: ${msg.cmd}`);
|
|
||||||
switch (msg.cmd) {
|
|
||||||
case "open":
|
|
||||||
return handleOpen(msg.path);
|
|
||||||
case "close":
|
|
||||||
return handleClose(msg.fd);
|
|
||||||
case "read":
|
|
||||||
return handleRead(msg.fd, msg.offset, msg.size);
|
|
||||||
case "write":
|
|
||||||
return handleWrite(msg.fd, msg.buffer, msg.offset);
|
|
||||||
case "size":
|
|
||||||
return handleSize(msg.fd);
|
|
||||||
case "sync":
|
|
||||||
return handleSync(msg.fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleOpen(path) {
|
|
||||||
if (!rootDir) {
|
|
||||||
rootDir = await navigator.storage.getDirectory();
|
|
||||||
}
|
|
||||||
const fd = nextFd++;
|
|
||||||
|
|
||||||
const handle = await rootDir.getFileHandle(path, { create: true });
|
|
||||||
const syncHandle = await handle.createSyncAccessHandle();
|
|
||||||
|
|
||||||
handles.set(fd, syncHandle);
|
|
||||||
return { fd };
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleClose(fd) {
|
|
||||||
const handle = handles.get(fd);
|
|
||||||
handle.close();
|
|
||||||
handles.delete(fd);
|
|
||||||
return { success: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleRead(fd, offset, size) {
|
|
||||||
const handle = handles.get(fd);
|
|
||||||
const readBuffer = new ArrayBuffer(size);
|
|
||||||
const readSize = handle.read(readBuffer, { at: offset });
|
|
||||||
log("opfssync read: size: ", readBuffer.byteLength);
|
|
||||||
|
|
||||||
const tmp = new Uint8Array(readBuffer);
|
|
||||||
log("opfssync read buffer: ", [...tmp]);
|
|
||||||
|
|
||||||
transferArray.set(tmp);
|
|
||||||
|
|
||||||
return { success: true, length: readSize };
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleWrite(fd, buffer, offset) {
|
|
||||||
log("opfssync buffer size:", buffer.byteLength);
|
|
||||||
log("opfssync write buffer: ", [...buffer]);
|
|
||||||
const handle = handles.get(fd);
|
|
||||||
const size = handle.write(buffer, { at: offset });
|
|
||||||
return { success: true, length: size };
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSize(fd) {
|
|
||||||
const handle = handles.get(fd);
|
|
||||||
return { success: true, length: handle.getSize() };
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSync(fd) {
|
|
||||||
const handle = handles.get(fd);
|
|
||||||
handle.flush();
|
|
||||||
return { success: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendResult(result) {
|
|
||||||
if (result?.fd) {
|
|
||||||
statusView.setInt32(4, result.fd, true);
|
|
||||||
} else {
|
|
||||||
log("opfs-sync-proxy: result.length: ", result.length);
|
|
||||||
statusView.setInt32(4, result?.length || 0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Atomics.store(statusArray, 0, 1);
|
|
||||||
Atomics.notify(statusArray, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// logLevel:
|
|
||||||
//
|
|
||||||
// 0 = no logging output
|
|
||||||
// 1 = only errors
|
|
||||||
// 2 = warnings and errors
|
|
||||||
// 3 = debug, warnings, and errors
|
|
||||||
const logLevel = 1;
|
|
||||||
|
|
||||||
const loggers = {
|
|
||||||
0: console.error.bind(console),
|
|
||||||
1: console.warn.bind(console),
|
|
||||||
2: console.log.bind(console),
|
|
||||||
};
|
|
||||||
const logImpl = (level, ...args) => {
|
|
||||||
if (logLevel > level) loggers[level]("OPFS asyncer:", ...args);
|
|
||||||
};
|
|
||||||
const log = (...args) => logImpl(2, ...args);
|
|
||||||
const warn = (...args) => logImpl(1, ...args);
|
|
||||||
const error = (...args) => logImpl(0, ...args);
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import { VFS } from "./opfs.js";
|
|
||||||
|
|
||||||
const vfs = new VFS();
|
|
||||||
|
|
||||||
onmessage = async function (e) {
|
|
||||||
if (!vfs.isReady) {
|
|
||||||
console.log("opfs ready: ", vfs.isReady);
|
|
||||||
await vfs.ready;
|
|
||||||
console.log("opfs ready: ", vfs.isReady);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { id, method, args } = e.data;
|
|
||||||
|
|
||||||
console.log(`interface onmessage method: ${method}`);
|
|
||||||
try {
|
|
||||||
let result;
|
|
||||||
switch (method) {
|
|
||||||
case "open":
|
|
||||||
result = vfs.open(args.path, args.flags);
|
|
||||||
break;
|
|
||||||
case "close":
|
|
||||||
result = vfs.close(args.fd);
|
|
||||||
break;
|
|
||||||
case "pread": {
|
|
||||||
const buffer = new Uint8Array(args.buffer);
|
|
||||||
result = vfs.pread(args.fd, buffer, args.offset);
|
|
||||||
self.postMessage(
|
|
||||||
{ id, size: result, error: null, buffer },
|
|
||||||
);
|
|
||||||
console.log("read size: ", result);
|
|
||||||
console.log("read buffer: ", [...buffer]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case "pwrite": {
|
|
||||||
result = vfs.pwrite(args.fd, args.buffer, args.offset);
|
|
||||||
console.log("write size: ", result);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "size":
|
|
||||||
result = vfs.size(args.fd);
|
|
||||||
break;
|
|
||||||
case "sync":
|
|
||||||
result = vfs.sync(args.fd);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`Unknown method: ${method}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.postMessage(
|
|
||||||
{ id, result, error: null },
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
self.postMessage({ id, result: null, error: error.message });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log("opfs-worker.js");
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
// First file: VFS class
|
|
||||||
class VFS {
|
|
||||||
constructor() {
|
|
||||||
this.transferBuffer = new SharedArrayBuffer(1024 * 1024); // 1mb
|
|
||||||
this.statusBuffer = new SharedArrayBuffer(8); // Room for status + size
|
|
||||||
|
|
||||||
this.statusArray = new Int32Array(this.statusBuffer);
|
|
||||||
this.statusView = new DataView(this.statusBuffer);
|
|
||||||
|
|
||||||
this.worker = new Worker(
|
|
||||||
new URL("./opfs-sync-proxy.js", import.meta.url),
|
|
||||||
{ type: "module" },
|
|
||||||
);
|
|
||||||
|
|
||||||
this.isReady = false;
|
|
||||||
this.ready = new Promise((resolve, reject) => {
|
|
||||||
this.worker.addEventListener("message", async (e) => {
|
|
||||||
if (e.data === "ready") {
|
|
||||||
await this.initWorker();
|
|
||||||
this.isReady = true;
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}, { once: true });
|
|
||||||
this.worker.addEventListener("error", reject, { once: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
this.worker.onerror = (e) => {
|
|
||||||
console.error("Sync proxy worker error:", e.message);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
initWorker() {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
this.worker.addEventListener("message", (e) => {
|
|
||||||
log("eventListener: ", e.data);
|
|
||||||
resolve();
|
|
||||||
}, { once: true });
|
|
||||||
|
|
||||||
this.worker.postMessage({
|
|
||||||
cmd: "init",
|
|
||||||
transferBuffer: this.transferBuffer,
|
|
||||||
statusBuffer: this.statusBuffer,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
open(path) {
|
|
||||||
Atomics.store(this.statusArray, 0, 0);
|
|
||||||
this.worker.postMessage({ cmd: "open", path });
|
|
||||||
Atomics.wait(this.statusArray, 0, 0);
|
|
||||||
|
|
||||||
const result = this.statusView.getInt32(4, true);
|
|
||||||
log("opfs.js open result: ", result);
|
|
||||||
log("opfs.js open result type: ", typeof result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fd) {
|
|
||||||
Atomics.store(this.statusArray, 0, 0);
|
|
||||||
this.worker.postMessage({ cmd: "close", fd });
|
|
||||||
Atomics.wait(this.statusArray, 0, 0);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pread(fd, buffer, offset) {
|
|
||||||
let bytesRead = 0;
|
|
||||||
|
|
||||||
while (bytesRead < buffer.byteLength) {
|
|
||||||
const chunkSize = Math.min(
|
|
||||||
this.transferBuffer.byteLength,
|
|
||||||
buffer.byteLength - bytesRead,
|
|
||||||
);
|
|
||||||
|
|
||||||
Atomics.store(this.statusArray, 0, 0);
|
|
||||||
this.worker.postMessage({
|
|
||||||
cmd: "read",
|
|
||||||
fd,
|
|
||||||
offset: offset + bytesRead,
|
|
||||||
size: chunkSize,
|
|
||||||
});
|
|
||||||
|
|
||||||
Atomics.wait(this.statusArray, 0, 0);
|
|
||||||
const readSize = this.statusView.getInt32(4, true);
|
|
||||||
buffer.set(
|
|
||||||
new Uint8Array(this.transferBuffer, 0, readSize),
|
|
||||||
bytesRead,
|
|
||||||
);
|
|
||||||
log("opfs pread buffer: ", [...buffer]);
|
|
||||||
|
|
||||||
bytesRead += readSize;
|
|
||||||
if (readSize < chunkSize) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytesRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
pwrite(fd, buffer, offset) {
|
|
||||||
log("write buffer size: ", buffer.byteLength);
|
|
||||||
Atomics.store(this.statusArray, 0, 0);
|
|
||||||
this.worker.postMessage({
|
|
||||||
cmd: "write",
|
|
||||||
fd,
|
|
||||||
buffer: buffer,
|
|
||||||
offset: offset,
|
|
||||||
});
|
|
||||||
|
|
||||||
Atomics.wait(this.statusArray, 0, 0);
|
|
||||||
log(
|
|
||||||
"opfs pwrite length statusview: ",
|
|
||||||
this.statusView.getInt32(4, true),
|
|
||||||
);
|
|
||||||
return this.statusView.getInt32(4, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
size(fd) {
|
|
||||||
Atomics.store(this.statusArray, 0, 0);
|
|
||||||
this.worker.postMessage({ cmd: "size", fd });
|
|
||||||
Atomics.wait(this.statusArray, 0, 0);
|
|
||||||
|
|
||||||
const result = this.statusView.getInt32(4, true);
|
|
||||||
log("opfs.js size result: ", result);
|
|
||||||
log("opfs.js size result type: ", typeof result);
|
|
||||||
return BigInt(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
sync(fd) {
|
|
||||||
Atomics.store(this.statusArray, 0, 0);
|
|
||||||
this.worker.postMessage({ cmd: "sync", fd });
|
|
||||||
Atomics.wait(this.statusArray, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// logLevel:
|
|
||||||
//
|
|
||||||
// 0 = no logging output
|
|
||||||
// 1 = only errors
|
|
||||||
// 2 = warnings and errors
|
|
||||||
// 3 = debug, warnings, and errors
|
|
||||||
const logLevel = 1;
|
|
||||||
|
|
||||||
const loggers = {
|
|
||||||
0: console.error.bind(console),
|
|
||||||
1: console.warn.bind(console),
|
|
||||||
2: console.log.bind(console),
|
|
||||||
};
|
|
||||||
const logImpl = (level, ...args) => {
|
|
||||||
if (logLevel > level) loggers[level]("OPFS asyncer:", ...args);
|
|
||||||
};
|
|
||||||
const log = (...args) => logImpl(2, ...args);
|
|
||||||
const warn = (...args) => logImpl(1, ...args);
|
|
||||||
const error = (...args) => logImpl(0, ...args);
|
|
||||||
|
|
||||||
export { VFS };
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
export class VFS {
|
|
||||||
constructor() {
|
|
||||||
return self.vfs;
|
|
||||||
}
|
|
||||||
|
|
||||||
open(path, flags) {
|
|
||||||
return self.vfs.open(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fd) {
|
|
||||||
return self.vfs.close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
pread(fd, buffer, offset) {
|
|
||||||
return self.vfs.pread(fd, buffer, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
pwrite(fd, buffer, offset) {
|
|
||||||
return self.vfs.pwrite(fd, buffer, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
size(fd) {
|
|
||||||
return self.vfs.size(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
sync(fd) {
|
|
||||||
return self.vfs.sync(fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { createServer } from "vite";
|
|
||||||
import { chromium } from "playwright";
|
|
||||||
|
|
||||||
export async function setupTestEnvironment(port) {
|
|
||||||
const server = await createServer({
|
|
||||||
configFile: "./vite.config.js",
|
|
||||||
root: ".",
|
|
||||||
server: { port },
|
|
||||||
});
|
|
||||||
await server.listen();
|
|
||||||
const browser = await chromium.launch();
|
|
||||||
const context = await browser.newContext();
|
|
||||||
const page = await context.newPage();
|
|
||||||
globalThis.__page__ = page;
|
|
||||||
|
|
||||||
return { server, browser, context, page };
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function teardownTestEnvironment({ server, browser, context }) {
|
|
||||||
await context.close();
|
|
||||||
await browser.close();
|
|
||||||
await server.close();
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import { afterAll, beforeAll, beforeEach, expect, test } from "vitest";
|
|
||||||
import { setupTestEnvironment, teardownTestEnvironment } from "./helpers.js";
|
|
||||||
|
|
||||||
let testEnv;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
testEnv = await setupTestEnvironment(5174);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
const { page } = testEnv;
|
|
||||||
await page.goto("http://localhost:5174/limbo-test.html");
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
await teardownTestEnvironment(testEnv);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("basic database operations", async () => {
|
|
||||||
const { page } = testEnv;
|
|
||||||
const result = await page.evaluate(async () => {
|
|
||||||
const worker = new Worker("./src/limbo-worker.js", { type: "module" });
|
|
||||||
|
|
||||||
const waitForMessage = (type, op) =>
|
|
||||||
new Promise((resolve, reject) => {
|
|
||||||
const handler = (e) => {
|
|
||||||
if (e.data.type === type && (!op || e.data.op === op)) {
|
|
||||||
worker.removeEventListener("message", handler);
|
|
||||||
resolve(e.data);
|
|
||||||
} else if (e.data.type === "error") {
|
|
||||||
worker.removeEventListener("message", handler);
|
|
||||||
reject(e.data.error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
worker.addEventListener("message", handler);
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await waitForMessage("ready");
|
|
||||||
worker.postMessage({ op: "createDb", path: "test.db" });
|
|
||||||
await waitForMessage("success", "createDb");
|
|
||||||
|
|
||||||
worker.postMessage({
|
|
||||||
op: "exec",
|
|
||||||
sql:
|
|
||||||
"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT);",
|
|
||||||
});
|
|
||||||
await waitForMessage("success", "exec");
|
|
||||||
|
|
||||||
worker.postMessage({
|
|
||||||
op: "exec",
|
|
||||||
sql: "INSERT INTO users VALUES (1, 'Alice', 'alice@example.org');",
|
|
||||||
});
|
|
||||||
await waitForMessage("success", "exec");
|
|
||||||
|
|
||||||
worker.postMessage({
|
|
||||||
op: "prepare",
|
|
||||||
sql: "SELECT * FROM users;",
|
|
||||||
});
|
|
||||||
|
|
||||||
const results = await waitForMessage("result");
|
|
||||||
return results;
|
|
||||||
} catch (error) {
|
|
||||||
return { error: error.message };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.error) throw new Error(`Test failed: ${result.error}`);
|
|
||||||
expect(result.result).toHaveLength(1);
|
|
||||||
expect(result.result[0]).toEqual([1, "Alice", "alice@example.org"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
// test/opfs.test.js
|
|
||||||
import { afterAll, beforeAll, beforeEach, expect, test } from "vitest";
|
|
||||||
import { setupTestEnvironment, teardownTestEnvironment } from "./helpers.js";
|
|
||||||
|
|
||||||
let testEnv;
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
testEnv = await setupTestEnvironment(5173);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
const { page } = testEnv;
|
|
||||||
await page.goto("http://localhost:5173/index.html");
|
|
||||||
await page.waitForFunction(() => window.VFSInterface !== undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
await teardownTestEnvironment(testEnv);
|
|
||||||
});
|
|
||||||
|
|
||||||
const opfsImport = "./src/opfs-worker.js";
|
|
||||||
|
|
||||||
test("basic read/write functionality", async () => {
|
|
||||||
const { page } = testEnv;
|
|
||||||
const result = await page.evaluate(async () => {
|
|
||||||
const vfs = new window.VFSInterface("./src/opfs-worker.js");
|
|
||||||
let fd;
|
|
||||||
try {
|
|
||||||
fd = await vfs.open("test.txt", {});
|
|
||||||
const writeData = new Uint8Array([1, 2, 3, 4]);
|
|
||||||
const bytesWritten = await vfs.pwrite(fd, writeData, 0);
|
|
||||||
const readData = new Uint8Array(4);
|
|
||||||
const bytesRead = await vfs.pread(fd, readData, 0);
|
|
||||||
await vfs.close(fd);
|
|
||||||
return { fd, bytesWritten, bytesRead, readData: Array.from(readData) };
|
|
||||||
} catch (error) {
|
|
||||||
if (fd !== undefined) await vfs.close(fd);
|
|
||||||
return { error: error.message };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.error) throw new Error(`Test failed: ${result.error}`);
|
|
||||||
expect(result.fd).toBe(1);
|
|
||||||
expect(result.bytesWritten).toBe(4);
|
|
||||||
expect(result.bytesRead).toBe(4);
|
|
||||||
expect(result.readData).toEqual([1, 2, 3, 4]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("larger data read/write", async () => {
|
|
||||||
const { page } = testEnv;
|
|
||||||
const result = await page.evaluate(async () => {
|
|
||||||
const vfs = new window.VFSInterface("./src/opfs-worker.js");
|
|
||||||
let fd;
|
|
||||||
try {
|
|
||||||
fd = await vfs.open("large.txt", {});
|
|
||||||
const writeData = new Uint8Array(1024).map((_, i) => i % 256);
|
|
||||||
const bytesWritten = await vfs.pwrite(fd, writeData, 0);
|
|
||||||
const readData = new Uint8Array(1024);
|
|
||||||
const bytesRead = await vfs.pread(fd, readData, 0);
|
|
||||||
await vfs.close(fd);
|
|
||||||
return { bytesWritten, bytesRead, readData: Array.from(readData) };
|
|
||||||
} catch (error) {
|
|
||||||
if (fd !== undefined) await vfs.close(fd);
|
|
||||||
return { error: error.message };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.error) throw new Error(`Test failed: ${result.error}`);
|
|
||||||
expect(result.bytesWritten).toBe(1024);
|
|
||||||
expect(result.bytesRead).toBe(1024);
|
|
||||||
expect(result.readData).toEqual(
|
|
||||||
Array.from({ length: 1024 }, (_, i) => i % 256),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("partial reads and writes", async () => {
|
|
||||||
const { page } = testEnv;
|
|
||||||
const result = await page.evaluate(async () => {
|
|
||||||
const vfs = new window.VFSInterface("./src/opfs-worker.js");
|
|
||||||
let fd;
|
|
||||||
try {
|
|
||||||
fd = await vfs.open("partial.txt", {});
|
|
||||||
|
|
||||||
const writeData1 = new Uint8Array([1, 2, 3, 4]);
|
|
||||||
const writeData2 = new Uint8Array([5, 6, 7, 8]);
|
|
||||||
await vfs.pwrite(fd, writeData1, 0);
|
|
||||||
await vfs.pwrite(fd, writeData2, 4);
|
|
||||||
|
|
||||||
const readData1 = new Uint8Array(2);
|
|
||||||
const readData2 = new Uint8Array(4);
|
|
||||||
const readData3 = new Uint8Array(2);
|
|
||||||
|
|
||||||
await vfs.pread(fd, readData1, 0);
|
|
||||||
await vfs.pread(fd, readData2, 2);
|
|
||||||
await vfs.pread(fd, readData3, 6);
|
|
||||||
|
|
||||||
await vfs.close(fd);
|
|
||||||
return {
|
|
||||||
readData1: Array.from(readData1),
|
|
||||||
readData2: Array.from(readData2),
|
|
||||||
readData3: Array.from(readData3),
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
if (fd !== undefined) await vfs.close(fd);
|
|
||||||
return { error: error.message };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.error) throw new Error(`Test failed: ${result.error}`);
|
|
||||||
expect(result.readData1).toEqual([1, 2]);
|
|
||||||
expect(result.readData2).toEqual([3, 4, 5, 6]);
|
|
||||||
expect(result.readData3).toEqual([7, 8]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("file size operations", async () => {
|
|
||||||
const { page } = testEnv;
|
|
||||||
const result = await page.evaluate(async () => {
|
|
||||||
const vfs = new window.VFSInterface("./src/opfs-worker.js");
|
|
||||||
let fd;
|
|
||||||
try {
|
|
||||||
fd = await vfs.open("size.txt", {});
|
|
||||||
const writeData1 = new Uint8Array([1, 2, 3, 4]);
|
|
||||||
await vfs.pwrite(fd, writeData1, 0);
|
|
||||||
const size1 = await vfs.size(fd);
|
|
||||||
|
|
||||||
const writeData2 = new Uint8Array([5, 6, 7, 8]);
|
|
||||||
await vfs.pwrite(fd, writeData2, 4);
|
|
||||||
const size2 = await vfs.size(fd);
|
|
||||||
|
|
||||||
await vfs.close(fd);
|
|
||||||
return { size1, size2 };
|
|
||||||
} catch (error) {
|
|
||||||
if (fd !== undefined) await vfs.close(fd);
|
|
||||||
return { error: error.message };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.error) throw new Error(`Test failed: ${result.error}`);
|
|
||||||
expect(Number(result.size1)).toBe(4);
|
|
||||||
expect(Number(result.size2)).toBe(8);
|
|
||||||
});
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { defineConfig } from "vite";
|
|
||||||
import wasm from "vite-plugin-wasm";
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
publicDir: "./html",
|
|
||||||
root: "./",
|
|
||||||
plugins: [wasm()],
|
|
||||||
test: {
|
|
||||||
globals: true,
|
|
||||||
setupFiles: ["./test/setup.js"],
|
|
||||||
include: ["test/*.test.js"],
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
headers: {
|
|
||||||
"Cross-Origin-Embedder-Policy": "require-corp",
|
|
||||||
"Cross-Origin-Opener-Policy": "same-origin",
|
|
||||||
"Cross-Origin-Resource-Policy": "cross-origin",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
worker: {
|
|
||||||
format: "es",
|
|
||||||
rollupOptions: {
|
|
||||||
output: {
|
|
||||||
format: "es",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -20,7 +20,7 @@ NPM_PACKAGES = [
|
|||||||
"bindings/javascript/npm/darwin-universal",
|
"bindings/javascript/npm/darwin-universal",
|
||||||
"bindings/javascript/npm/linux-x64-gnu",
|
"bindings/javascript/npm/linux-x64-gnu",
|
||||||
"bindings/javascript/npm/win32-x64-msvc",
|
"bindings/javascript/npm/win32-x64-msvc",
|
||||||
"bindings/wasm",
|
"bindings/javascript/npm/wasm32-wasip1-threads",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user