mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-18 17:14:20 +01:00
restructure js bindings
This commit is contained in:
2
bindings/javascript/.gitignore
vendored
2
bindings/javascript/.gitignore
vendored
@@ -197,4 +197,4 @@ Cargo.lock
|
|||||||
*.node
|
*.node
|
||||||
*.wasm
|
*.wasm
|
||||||
|
|
||||||
package.native.json
|
npm
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ turso_core = { workspace = true }
|
|||||||
napi = { version = "3.1.3", default-features = false, features = ["napi6"] }
|
napi = { version = "3.1.3", default-features = false, features = ["napi6"] }
|
||||||
napi-derive = { version = "3.1.1", default-features = true }
|
napi-derive = { version = "3.1.1", default-features = true }
|
||||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||||
|
tracing.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
encryption = ["turso_core/encryption"]
|
encryption = ["turso_core/encryption"]
|
||||||
|
browser = []
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
napi-build = "2.2.3"
|
napi-build = "2.2.3"
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export * from '@tursodatabase/database-wasm32-wasi'
|
|
||||||
@@ -1,398 +0,0 @@
|
|||||||
// prettier-ignore
|
|
||||||
/* eslint-disable */
|
|
||||||
// @ts-nocheck
|
|
||||||
/* auto-generated by NAPI-RS */
|
|
||||||
|
|
||||||
import { createRequire } from 'node:module'
|
|
||||||
const require = createRequire(import.meta.url)
|
|
||||||
const __dirname = new URL('.', import.meta.url).pathname
|
|
||||||
|
|
||||||
const { readFileSync } = require('node:fs')
|
|
||||||
let nativeBinding = null
|
|
||||||
const loadErrors = []
|
|
||||||
|
|
||||||
const isMusl = () => {
|
|
||||||
let musl = false
|
|
||||||
if (process.platform === 'linux') {
|
|
||||||
musl = isMuslFromFilesystem()
|
|
||||||
if (musl === null) {
|
|
||||||
musl = isMuslFromReport()
|
|
||||||
}
|
|
||||||
if (musl === null) {
|
|
||||||
musl = isMuslFromChildProcess()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return musl
|
|
||||||
}
|
|
||||||
|
|
||||||
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
|
|
||||||
|
|
||||||
const isMuslFromFilesystem = () => {
|
|
||||||
try {
|
|
||||||
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
|
|
||||||
} catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isMuslFromReport = () => {
|
|
||||||
let report = null
|
|
||||||
if (typeof process.report?.getReport === 'function') {
|
|
||||||
process.report.excludeNetwork = true
|
|
||||||
report = process.report.getReport()
|
|
||||||
}
|
|
||||||
if (!report) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
if (report.header && report.header.glibcVersionRuntime) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (Array.isArray(report.sharedObjects)) {
|
|
||||||
if (report.sharedObjects.some(isFileMusl)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const isMuslFromChildProcess = () => {
|
|
||||||
try {
|
|
||||||
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
|
|
||||||
} catch (e) {
|
|
||||||
// If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function requireNative() {
|
|
||||||
if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
|
|
||||||
try {
|
|
||||||
nativeBinding = require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH);
|
|
||||||
} catch (err) {
|
|
||||||
loadErrors.push(err)
|
|
||||||
}
|
|
||||||
} else if (process.platform === 'android') {
|
|
||||||
if (process.arch === 'arm64') {
|
|
||||||
try {
|
|
||||||
return require('./turso.android-arm64.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-android-arm64')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else if (process.arch === 'arm') {
|
|
||||||
try {
|
|
||||||
return require('./turso.android-arm-eabi.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-android-arm-eabi')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
|
|
||||||
}
|
|
||||||
} else if (process.platform === 'win32') {
|
|
||||||
if (process.arch === 'x64') {
|
|
||||||
try {
|
|
||||||
return require('./turso.win32-x64-msvc.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-win32-x64-msvc')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else if (process.arch === 'ia32') {
|
|
||||||
try {
|
|
||||||
return require('./turso.win32-ia32-msvc.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-win32-ia32-msvc')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else if (process.arch === 'arm64') {
|
|
||||||
try {
|
|
||||||
return require('./turso.win32-arm64-msvc.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-win32-arm64-msvc')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
|
|
||||||
}
|
|
||||||
} else if (process.platform === 'darwin') {
|
|
||||||
try {
|
|
||||||
return require('./turso.darwin-universal.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-darwin-universal')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
if (process.arch === 'x64') {
|
|
||||||
try {
|
|
||||||
return require('./turso.darwin-x64.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-darwin-x64')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else if (process.arch === 'arm64') {
|
|
||||||
try {
|
|
||||||
return require('./turso.darwin-arm64.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-darwin-arm64')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
|
|
||||||
}
|
|
||||||
} else if (process.platform === 'freebsd') {
|
|
||||||
if (process.arch === 'x64') {
|
|
||||||
try {
|
|
||||||
return require('./turso.freebsd-x64.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-freebsd-x64')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else if (process.arch === 'arm64') {
|
|
||||||
try {
|
|
||||||
return require('./turso.freebsd-arm64.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-freebsd-arm64')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
|
|
||||||
}
|
|
||||||
} else if (process.platform === 'linux') {
|
|
||||||
if (process.arch === 'x64') {
|
|
||||||
if (isMusl()) {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-x64-musl.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-x64-musl')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-x64-gnu.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-x64-gnu')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (process.arch === 'arm64') {
|
|
||||||
if (isMusl()) {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-arm64-musl.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-arm64-musl')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-arm64-gnu.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-arm64-gnu')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (process.arch === 'arm') {
|
|
||||||
if (isMusl()) {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-arm-musleabihf.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-arm-musleabihf')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-arm-gnueabihf.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-arm-gnueabihf')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (process.arch === 'riscv64') {
|
|
||||||
if (isMusl()) {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-riscv64-musl.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-riscv64-musl')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-riscv64-gnu.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-riscv64-gnu')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (process.arch === 'ppc64') {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-ppc64-gnu.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-ppc64-gnu')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else if (process.arch === 's390x') {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-s390x-gnu.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-s390x-gnu')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
|
|
||||||
}
|
|
||||||
} else if (process.platform === 'openharmony') {
|
|
||||||
if (process.arch === 'arm64') {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-arm64-ohos.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-arm64-ohos')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else if (process.arch === 'x64') {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-x64-ohos.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-x64-ohos')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else if (process.arch === 'arm') {
|
|
||||||
try {
|
|
||||||
return require('./turso.linux-arm-ohos.node')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return require('@tursodatabase/database-linux-arm-ohos')
|
|
||||||
} catch (e) {
|
|
||||||
loadErrors.push(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loadErrors.push(new Error(`Unsupported architecture on OpenHarmony: ${process.arch}`))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nativeBinding = requireNative()
|
|
||||||
|
|
||||||
if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
|
|
||||||
try {
|
|
||||||
nativeBinding = require('./turso.wasi.cjs')
|
|
||||||
} catch (err) {
|
|
||||||
if (process.env.NAPI_RS_FORCE_WASI) {
|
|
||||||
loadErrors.push(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!nativeBinding) {
|
|
||||||
try {
|
|
||||||
nativeBinding = require('@tursodatabase/database-wasm32-wasi')
|
|
||||||
} catch (err) {
|
|
||||||
if (process.env.NAPI_RS_FORCE_WASI) {
|
|
||||||
loadErrors.push(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nativeBinding) {
|
|
||||||
if (loadErrors.length > 0) {
|
|
||||||
throw new Error(
|
|
||||||
`Cannot find native binding. ` +
|
|
||||||
`npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` +
|
|
||||||
'Please try `npm i` again after removing both package-lock.json and node_modules directory.',
|
|
||||||
{ cause: loadErrors }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
throw new Error(`Failed to load native binding`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { Database, Statement } = nativeBinding
|
|
||||||
export { Database }
|
|
||||||
export { Statement }
|
|
||||||
3993
bindings/javascript/package-lock.json
generated
3993
bindings/javascript/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,59 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@tursodatabase/database-browser",
|
|
||||||
"version": "0.1.5-pre.2",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/tursodatabase/turso"
|
|
||||||
},
|
|
||||||
"description": "The Turso database library specifically for browser/web environment",
|
|
||||||
"module": "./dist/promise.js",
|
|
||||||
"main": "./dist/promise.js",
|
|
||||||
"type": "module",
|
|
||||||
"exports": {
|
|
||||||
".": "./dist/promise.js",
|
|
||||||
"./compat": "./dist/compat.js"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"browser.js",
|
|
||||||
"index.js",
|
|
||||||
"index.d.ts",
|
|
||||||
"dist/**"
|
|
||||||
],
|
|
||||||
"types": "index.d.ts",
|
|
||||||
"napi": {
|
|
||||||
"binaryName": "turso",
|
|
||||||
"targets": [
|
|
||||||
"wasm32-wasip1-threads"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
|
||||||
"@napi-rs/cli": "^3.0.4",
|
|
||||||
"@napi-rs/wasm-runtime": "^1.0.1",
|
|
||||||
"ava": "^6.0.1",
|
|
||||||
"better-sqlite3": "^11.9.1",
|
|
||||||
"typescript": "^5.9.2"
|
|
||||||
},
|
|
||||||
"ava": {
|
|
||||||
"timeout": "3m"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"artifacts": "napi artifacts",
|
|
||||||
"build": "npm exec tsc && napi build --platform --release --esm",
|
|
||||||
"build:debug": "npm exec tsc && napi build --platform",
|
|
||||||
"prepublishOnly": "npm exec tsc && napi prepublish -t npm --skip-optional-publish",
|
|
||||||
"test": "true",
|
|
||||||
"universal": "napi universalize",
|
|
||||||
"version": "napi version"
|
|
||||||
},
|
|
||||||
"packageManager": "yarn@4.9.2",
|
|
||||||
"imports": {
|
|
||||||
"#entry-point": {
|
|
||||||
"types": "./index.d.ts",
|
|
||||||
"browser": "./browser.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "@tursodatabase/database",
|
|
||||||
"version": "0.1.5-pre.3",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/tursodatabase/turso"
|
|
||||||
},
|
|
||||||
"description": "The Turso database library",
|
|
||||||
"module": "./dist/promise.js",
|
|
||||||
"main": "./dist/promise.js",
|
|
||||||
"type": "module",
|
|
||||||
"exports": {
|
|
||||||
".": "./dist/promise.js",
|
|
||||||
"./compat": "./dist/compat.js"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"browser.js",
|
|
||||||
"index.js",
|
|
||||||
"index.d.ts",
|
|
||||||
"dist/**"
|
|
||||||
],
|
|
||||||
"types": "index.d.ts",
|
|
||||||
"napi": {
|
|
||||||
"binaryName": "turso",
|
|
||||||
"targets": [
|
|
||||||
"x86_64-unknown-linux-gnu",
|
|
||||||
"x86_64-pc-windows-msvc",
|
|
||||||
"universal-apple-darwin",
|
|
||||||
"aarch64-unknown-linux-gnu",
|
|
||||||
"wasm32-wasip1-threads"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
|
||||||
"@napi-rs/cli": "^3.0.4",
|
|
||||||
"@napi-rs/wasm-runtime": "^1.0.1",
|
|
||||||
"ava": "^6.0.1",
|
|
||||||
"better-sqlite3": "^11.9.1",
|
|
||||||
"typescript": "^5.9.2"
|
|
||||||
},
|
|
||||||
"ava": {
|
|
||||||
"timeout": "3m"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"artifacts": "napi artifacts",
|
"build": "npm run build --workspaces"
|
||||||
"build": "npm exec tsc && napi build --platform --release --esm",
|
|
||||||
"build:debug": "npm exec tsc && napi build --platform",
|
|
||||||
"prepublishOnly": "npm exec tsc && napi prepublish -t npm",
|
|
||||||
"test": "true",
|
|
||||||
"universal": "napi universalize",
|
|
||||||
"version": "napi version"
|
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@4.9.2",
|
"workspaces": [
|
||||||
"imports": {
|
"packages/core",
|
||||||
"#entry-point": {
|
"packages/native",
|
||||||
"types": "./index.d.ts",
|
"packages/browser"
|
||||||
"browser": "./browser.js",
|
]
|
||||||
"node": "./index.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
createOnMessage as __wasmCreateOnMessageForFsProxy,
|
createOnMessage as __wasmCreateOnMessageForFsProxy,
|
||||||
getDefaultContext as __emnapiGetDefaultContext,
|
getDefaultContext as __emnapiGetDefaultContext,
|
||||||
instantiateNapiModuleSync as __emnapiInstantiateNapiModuleSync,
|
instantiateNapiModule as __emnapiInstantiateNapiModule,
|
||||||
WASI as __WASI,
|
WASI as __WASI,
|
||||||
} from '@napi-rs/wasm-runtime'
|
} from '@napi-rs/wasm-runtime'
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ const __wasi = new __WASI({
|
|||||||
version: 'preview1',
|
version: 'preview1',
|
||||||
})
|
})
|
||||||
|
|
||||||
const __wasmUrl = new URL('./turso.wasm32-wasi.wasm', import.meta.url).href
|
const __wasmUrl = new URL('./turso.wasm32-wasi.debug.wasm', import.meta.url).href
|
||||||
const __emnapiContext = __emnapiGetDefaultContext()
|
const __emnapiContext = __emnapiGetDefaultContext()
|
||||||
|
|
||||||
|
|
||||||
@@ -23,19 +23,25 @@ const __sharedMemory = new WebAssembly.Memory({
|
|||||||
|
|
||||||
const __wasmFile = await fetch(__wasmUrl).then((res) => res.arrayBuffer())
|
const __wasmFile = await fetch(__wasmUrl).then((res) => res.arrayBuffer())
|
||||||
|
|
||||||
|
export let MainWorker = null;
|
||||||
|
|
||||||
|
function panic(name) {
|
||||||
|
throw new Error(`method ${name} must be invoked only from the main thread`);
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
instance: __napiInstance,
|
instance: __napiInstance,
|
||||||
module: __wasiModule,
|
module: __wasiModule,
|
||||||
napiModule: __napiModule,
|
napiModule: __napiModule,
|
||||||
} = __emnapiInstantiateNapiModuleSync(__wasmFile, {
|
} = await __emnapiInstantiateNapiModule(__wasmFile, {
|
||||||
context: __emnapiContext,
|
context: __emnapiContext,
|
||||||
asyncWorkPoolSize: 4,
|
asyncWorkPoolSize: 1,
|
||||||
wasi: __wasi,
|
wasi: __wasi,
|
||||||
onCreateWorker() {
|
onCreateWorker() {
|
||||||
const worker = new Worker(new URL('./wasi-worker-browser.mjs', import.meta.url), {
|
const worker = new Worker(new URL('./worker.mjs', import.meta.url), {
|
||||||
type: 'module',
|
type: 'module',
|
||||||
})
|
})
|
||||||
|
MainWorker = worker;
|
||||||
return worker
|
return worker
|
||||||
},
|
},
|
||||||
overwriteImports(importObject) {
|
overwriteImports(importObject) {
|
||||||
@@ -44,6 +50,13 @@ const {
|
|||||||
...importObject.napi,
|
...importObject.napi,
|
||||||
...importObject.emnapi,
|
...importObject.emnapi,
|
||||||
memory: __sharedMemory,
|
memory: __sharedMemory,
|
||||||
|
is_web_worker: () => false,
|
||||||
|
lookup_file: () => panic("lookup_file"),
|
||||||
|
read: () => panic("read"),
|
||||||
|
write: () => panic("write"),
|
||||||
|
sync: () => panic("sync"),
|
||||||
|
truncate: () => panic("truncate"),
|
||||||
|
size: () => panic("size"),
|
||||||
}
|
}
|
||||||
return importObject
|
return importObject
|
||||||
},
|
},
|
||||||
@@ -57,4 +70,8 @@ const {
|
|||||||
})
|
})
|
||||||
export default __napiModule.exports
|
export default __napiModule.exports
|
||||||
export const Database = __napiModule.exports.Database
|
export const Database = __napiModule.exports.Database
|
||||||
|
export const Opfs = __napiModule.exports.Opfs
|
||||||
|
export const OpfsFile = __napiModule.exports.OpfsFile
|
||||||
export const Statement = __napiModule.exports.Statement
|
export const Statement = __napiModule.exports.Statement
|
||||||
|
export const connect = __napiModule.exports.connect
|
||||||
|
export const initThreadPool = __napiModule.exports.initThreadPool
|
||||||
34
bindings/javascript/packages/browser/package.json
Normal file
34
bindings/javascript/packages/browser/package.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"name": "@tursodatabase/database-browser",
|
||||||
|
"version": "0.1.5-pre.4",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tursodatabase/turso"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "index.js",
|
||||||
|
"packageManager": "yarn@4.9.2",
|
||||||
|
"devDependencies": {
|
||||||
|
"@napi-rs/cli": "^3.1.5",
|
||||||
|
"@napi-rs/wasm-runtime": "^1.0.3",
|
||||||
|
"@vitest/browser": "^3.2.4",
|
||||||
|
"playwright": "^1.55.0",
|
||||||
|
"typescript": "^5.9.2",
|
||||||
|
"vitest": "^3.2.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"napi-build": "napi build --features browser --release --platform --target wasm32-wasip1-threads --no-js --manifest-path ../../Cargo.toml --output-dir . && rm index.d.ts turso.wasi* wasi* browser.js",
|
||||||
|
"tsc-build": "npm exec tsc",
|
||||||
|
"build": "npm run napi-build && npm run tsc-build",
|
||||||
|
"test": "CI=1 vitest --browser=chromium --run && CI=1 vitest --browser=firefox --run"
|
||||||
|
},
|
||||||
|
"napi": {
|
||||||
|
"binaryName": "turso",
|
||||||
|
"targets": [
|
||||||
|
"wasm32-wasip1-threads"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tursodatabase/database-core": "^0.1.5-pre.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
55
bindings/javascript/packages/browser/promise.test.ts
Normal file
55
bindings/javascript/packages/browser/promise.test.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { expect, test, afterEach } from 'vitest'
|
||||||
|
import { connect } from './promise.js'
|
||||||
|
|
||||||
|
test('in-memory db', async () => {
|
||||||
|
const db = await connect(":memory:");
|
||||||
|
await db.exec("CREATE TABLE t(x)");
|
||||||
|
await db.exec("INSERT INTO t VALUES (1), (2), (3)");
|
||||||
|
const stmt = db.prepare("SELECT * FROM t WHERE x % 2 = ?");
|
||||||
|
const rows = await stmt.all([1]);
|
||||||
|
expect(rows).toEqual([{ x: 1 }, { x: 3 }]);
|
||||||
|
})
|
||||||
|
|
||||||
|
test('on-disk db', async () => {
|
||||||
|
const path = `test-${(Math.random() * 10000) | 0}.db`;
|
||||||
|
const db1 = await connect(path);
|
||||||
|
await db1.exec("CREATE TABLE t(x)");
|
||||||
|
await db1.exec("INSERT INTO t VALUES (1), (2), (3)");
|
||||||
|
const stmt1 = db1.prepare("SELECT * FROM t WHERE x % 2 = ?");
|
||||||
|
expect(stmt1.columns()).toEqual([{ name: "x", column: null, database: null, table: null, type: null }]);
|
||||||
|
const rows1 = await stmt1.all([1]);
|
||||||
|
expect(rows1).toEqual([{ x: 1 }, { x: 3 }]);
|
||||||
|
await db1.close();
|
||||||
|
|
||||||
|
const db2 = await connect(path);
|
||||||
|
const stmt2 = db2.prepare("SELECT * FROM t WHERE x % 2 = ?");
|
||||||
|
expect(stmt2.columns()).toEqual([{ name: "x", column: null, database: null, table: null, type: null }]);
|
||||||
|
const rows2 = await stmt2.all([1]);
|
||||||
|
expect(rows2).toEqual([{ x: 1 }, { x: 3 }]);
|
||||||
|
db2.close();
|
||||||
|
})
|
||||||
|
|
||||||
|
test('attach', async () => {
|
||||||
|
const path1 = `test-${(Math.random() * 10000) | 0}.db`;
|
||||||
|
const path2 = `test-${(Math.random() * 10000) | 0}.db`;
|
||||||
|
const db1 = await connect(path1);
|
||||||
|
await db1.exec("CREATE TABLE t(x)");
|
||||||
|
await db1.exec("INSERT INTO t VALUES (1), (2), (3)");
|
||||||
|
const db2 = await connect(path2);
|
||||||
|
await db2.exec("CREATE TABLE q(x)");
|
||||||
|
await db2.exec("INSERT INTO q VALUES (4), (5), (6)");
|
||||||
|
|
||||||
|
await db1.exec(`ATTACH '${path2}' as secondary`);
|
||||||
|
|
||||||
|
const stmt = db1.prepare("SELECT * FROM t UNION ALL SELECT * FROM secondary.q");
|
||||||
|
expect(stmt.columns()).toEqual([{ name: "x", column: null, database: null, table: null, type: null }]);
|
||||||
|
const rows = await stmt.all([1]);
|
||||||
|
expect(rows).toEqual([{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }, { x: 5 }, { x: 6 }]);
|
||||||
|
})
|
||||||
|
|
||||||
|
test('blobs', async () => {
|
||||||
|
const db = await connect(":memory:");
|
||||||
|
const rows = await db.prepare("SELECT x'1020' as x").all();
|
||||||
|
expect(rows).toEqual([{ x: new Uint8Array([16, 32]) }])
|
||||||
|
})
|
||||||
|
|
||||||
78
bindings/javascript/packages/browser/promise.ts
Normal file
78
bindings/javascript/packages/browser/promise.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { DatabasePromise, NativeDatabase, DatabaseOpts, SqliteError } from "@tursodatabase/database-core"
|
||||||
|
import { connect as nativeConnect, initThreadPool, MainWorker } from "./index.js";
|
||||||
|
|
||||||
|
let workerRequestId = 0;
|
||||||
|
class Database extends DatabasePromise {
|
||||||
|
files: string[];
|
||||||
|
constructor(db: NativeDatabase, files: string[], opts: DatabaseOpts = {}) {
|
||||||
|
super(db, opts)
|
||||||
|
this.files = files;
|
||||||
|
}
|
||||||
|
async close() {
|
||||||
|
let currentId = workerRequestId;
|
||||||
|
workerRequestId += this.files.length;
|
||||||
|
|
||||||
|
let tasks = [];
|
||||||
|
for (const file of this.files) {
|
||||||
|
(MainWorker as any).postMessage({ __turso__: "unregister", path: file, id: currentId });
|
||||||
|
tasks.push(waitFor(currentId));
|
||||||
|
currentId += 1;
|
||||||
|
}
|
||||||
|
await Promise.all(tasks);
|
||||||
|
this.db.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitFor(id: number): Promise<any> {
|
||||||
|
let waitResolve, waitReject;
|
||||||
|
const callback = msg => {
|
||||||
|
if (msg.data.id == id) {
|
||||||
|
if (msg.data.error != null) {
|
||||||
|
waitReject(msg.data.error)
|
||||||
|
} else {
|
||||||
|
waitResolve()
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const cleanup = () => (MainWorker as any).removeEventListener("message", callback);
|
||||||
|
|
||||||
|
(MainWorker as any).addEventListener("message", callback);
|
||||||
|
const result = new Promise((resolve, reject) => {
|
||||||
|
waitResolve = resolve;
|
||||||
|
waitReject = reject;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new database connection asynchronously.
|
||||||
|
*
|
||||||
|
* @param {string} path - Path to the database file.
|
||||||
|
* @param {Object} opts - Options for database behavior.
|
||||||
|
* @returns {Promise<Database>} - A promise that resolves to a Database instance.
|
||||||
|
*/
|
||||||
|
async function connect(path: string, opts: DatabaseOpts = {}): Promise<Database> {
|
||||||
|
if (path == ":memory:") {
|
||||||
|
const db = await nativeConnect(path, { tracing: opts.tracing });
|
||||||
|
return new Database(db, [], opts);
|
||||||
|
}
|
||||||
|
await initThreadPool();
|
||||||
|
if (MainWorker == null) {
|
||||||
|
throw new Error("panic: MainWorker is not set");
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentId = workerRequestId;
|
||||||
|
workerRequestId += 2;
|
||||||
|
|
||||||
|
let dbHandlePromise = waitFor(currentId);
|
||||||
|
let walHandlePromise = waitFor(currentId + 1);
|
||||||
|
(MainWorker as any).postMessage({ __turso__: "register", path: `${path}`, id: currentId });
|
||||||
|
(MainWorker as any).postMessage({ __turso__: "register", path: `${path}-wal`, id: currentId + 1 });
|
||||||
|
await Promise.all([dbHandlePromise, walHandlePromise]);
|
||||||
|
const db = await nativeConnect(path, { tracing: opts.tracing });
|
||||||
|
const files = [path, `${path}-wal`];
|
||||||
|
return new Database(db, files, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { connect, Database, SqliteError }
|
||||||
16
bindings/javascript/packages/browser/tsconfig.json
Normal file
16
bindings/javascript/packages/browser/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"module": "nodenext",
|
||||||
|
"target": "esnext",
|
||||||
|
"outDir": "dist/",
|
||||||
|
"lib": [
|
||||||
|
"es2020"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
}
|
||||||
23
bindings/javascript/packages/browser/vitest.config.ts
Normal file
23
bindings/javascript/packages/browser/vitest.config.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { defineConfig } from 'vitest/config'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
define: {
|
||||||
|
'process.env.NODE_DEBUG_NATIVE': 'false',
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
headers: {
|
||||||
|
"Cross-Origin-Embedder-Policy": "require-corp",
|
||||||
|
"Cross-Origin-Opener-Policy": "same-origin"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
browser: {
|
||||||
|
enabled: true,
|
||||||
|
provider: 'playwright',
|
||||||
|
instances: [
|
||||||
|
{ browser: 'chromium' },
|
||||||
|
{ browser: 'firefox' }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
160
bindings/javascript/packages/browser/worker.mjs
Normal file
160
bindings/javascript/packages/browser/worker.mjs
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
import { instantiateNapiModuleSync, MessageHandler, WASI } from '@napi-rs/wasm-runtime'
|
||||||
|
|
||||||
|
var fileByPath = new Map();
|
||||||
|
var fileByHandle = new Map();
|
||||||
|
let fileHandles = 0;
|
||||||
|
var memory = null;
|
||||||
|
|
||||||
|
function getUint8ArrayFromWasm(ptr, len) {
|
||||||
|
ptr = ptr >>> 0;
|
||||||
|
return new Uint8Array(memory.buffer).subarray(ptr, ptr + len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function registerFile(path) {
|
||||||
|
if (fileByPath.has(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const opfsRoot = await navigator.storage.getDirectory();
|
||||||
|
const opfsHandle = await opfsRoot.getFileHandle(path, { create: true });
|
||||||
|
const opfsSync = await opfsHandle.createSyncAccessHandle();
|
||||||
|
fileHandles += 1;
|
||||||
|
fileByPath.set(path, { handle: fileHandles, sync: opfsSync });
|
||||||
|
fileByHandle.set(fileHandles, opfsSync);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unregisterFile(path) {
|
||||||
|
const file = fileByPath.get(path);
|
||||||
|
if (file == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileByPath.delete(path);
|
||||||
|
fileByHandle.delete(file.handle);
|
||||||
|
file.sync.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookup_file(pathPtr, pathLen) {
|
||||||
|
try {
|
||||||
|
const buffer = getUint8ArrayFromWasm(pathPtr, pathLen);
|
||||||
|
const notShared = new Uint8Array(buffer.length);
|
||||||
|
notShared.set(buffer);
|
||||||
|
const decoder = new TextDecoder('utf-8');
|
||||||
|
const path = decoder.decode(notShared);
|
||||||
|
const file = fileByPath.get(path);
|
||||||
|
if (file == null) {
|
||||||
|
return -404;
|
||||||
|
}
|
||||||
|
return file.handle;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('lookupFile', pathPtr, pathLen, e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function read(handle, bufferPtr, bufferLen, offset) {
|
||||||
|
try {
|
||||||
|
const buffer = getUint8ArrayFromWasm(bufferPtr, bufferLen);
|
||||||
|
const file = fileByHandle.get(Number(handle));
|
||||||
|
const result = file.read(buffer, { at: Number(offset) });
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('read', handle, bufferPtr, bufferLen, offset, e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function write(handle, bufferPtr, bufferLen, offset) {
|
||||||
|
try {
|
||||||
|
const buffer = getUint8ArrayFromWasm(bufferPtr, bufferLen);
|
||||||
|
const file = fileByHandle.get(Number(handle));
|
||||||
|
const result = file.write(buffer, { at: Number(offset) });
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('write', handle, bufferPtr, bufferLen, offset, e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function sync(handle) {
|
||||||
|
try {
|
||||||
|
const file = fileByHandle.get(Number(handle));
|
||||||
|
file.flush();
|
||||||
|
return 0;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('sync', handle, e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function truncate(handle, size) {
|
||||||
|
try {
|
||||||
|
const file = fileByHandle.get(Number(handle));
|
||||||
|
const result = file.truncate(size);
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('truncate', handle, size, e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function size(handle) {
|
||||||
|
try {
|
||||||
|
const file = fileByHandle.get(Number(handle));
|
||||||
|
const size = file.getSize()
|
||||||
|
return size;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('size', handle, e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handler = new MessageHandler({
|
||||||
|
onLoad({ wasmModule, wasmMemory }) {
|
||||||
|
memory = 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,
|
||||||
|
is_web_worker: () => true,
|
||||||
|
lookup_file: lookup_file,
|
||||||
|
read: read,
|
||||||
|
write: write,
|
||||||
|
sync: sync,
|
||||||
|
truncate: truncate,
|
||||||
|
size: size,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
globalThis.onmessage = async function (e) {
|
||||||
|
if (e.data.__turso__ == 'register') {
|
||||||
|
try {
|
||||||
|
await registerFile(e.data.path)
|
||||||
|
self.postMessage({ id: e.data.id })
|
||||||
|
} catch (error) {
|
||||||
|
self.postMessage({ id: e.data.id, error: error });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (e.data.__turso__ == 'unregister') {
|
||||||
|
try {
|
||||||
|
await unregisterFile(e.data.path)
|
||||||
|
self.postMessage({ id: e.data.id })
|
||||||
|
} catch (error) {
|
||||||
|
self.postMessage({ id: e.data.id, error: error });
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handler.handle(e)
|
||||||
|
}
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
import { Database as NativeDB, Statement as NativeStatement } from "#entry-point";
|
|
||||||
import { bindParams } from "./bind.js";
|
import { bindParams } from "./bind.js";
|
||||||
|
|
||||||
import { SqliteError } from "./sqlite-error.js";
|
import { SqliteError } from "./sqlite-error.js";
|
||||||
|
import { NativeDatabase, NativeStatement, STEP_IO, STEP_ROW, STEP_DONE } from "./types.js";
|
||||||
// Step result constants
|
|
||||||
const STEP_ROW = 1;
|
|
||||||
const STEP_DONE = 2;
|
|
||||||
const STEP_IO = 3;
|
|
||||||
|
|
||||||
const convertibleErrorTypes = { TypeError };
|
const convertibleErrorTypes = { TypeError };
|
||||||
const CONVERTIBLE_ERROR_PREFIX = "[TURSO_CONVERT_TYPE]";
|
const CONVERTIBLE_ERROR_PREFIX = "[TURSO_CONVERT_TYPE]";
|
||||||
@@ -35,7 +29,7 @@ function createErrorByName(name, message) {
|
|||||||
* Database represents a connection that can prepare and execute SQL statements.
|
* Database represents a connection that can prepare and execute SQL statements.
|
||||||
*/
|
*/
|
||||||
class Database {
|
class Database {
|
||||||
db: NativeDB;
|
db: NativeDatabase;
|
||||||
memory: boolean;
|
memory: boolean;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
private _inTransaction: boolean = false;
|
private _inTransaction: boolean = false;
|
||||||
@@ -50,15 +44,14 @@ class Database {
|
|||||||
* @param {boolean} [opts.fileMustExist=false] - If true, throws if database file does not exist.
|
* @param {boolean} [opts.fileMustExist=false] - If true, throws if database file does not exist.
|
||||||
* @param {number} [opts.timeout=0] - Timeout duration in milliseconds for database operations. Defaults to 0 (no timeout).
|
* @param {number} [opts.timeout=0] - Timeout duration in milliseconds for database operations. Defaults to 0 (no timeout).
|
||||||
*/
|
*/
|
||||||
constructor(path: string, opts: any = {}) {
|
constructor(db: NativeDatabase, opts: any = {}) {
|
||||||
opts.readonly = opts.readonly === undefined ? false : opts.readonly;
|
opts.readonly = opts.readonly === undefined ? false : opts.readonly;
|
||||||
opts.fileMustExist =
|
opts.fileMustExist =
|
||||||
opts.fileMustExist === undefined ? false : opts.fileMustExist;
|
opts.fileMustExist === undefined ? false : opts.fileMustExist;
|
||||||
opts.timeout = opts.timeout === undefined ? 0 : opts.timeout;
|
opts.timeout = opts.timeout === undefined ? 0 : opts.timeout;
|
||||||
|
|
||||||
this.db = new NativeDB(path);
|
this.db = db;
|
||||||
this.memory = this.db.memory;
|
this.memory = this.db.memory;
|
||||||
const db = this.db;
|
|
||||||
|
|
||||||
Object.defineProperties(this, {
|
Object.defineProperties(this, {
|
||||||
inTransaction: {
|
inTransaction: {
|
||||||
@@ -66,7 +59,7 @@ class Database {
|
|||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
get() {
|
get() {
|
||||||
return path;
|
return db.path;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
readonly: {
|
readonly: {
|
||||||
@@ -199,7 +192,7 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.db.batch(sql);
|
this.db.batchSync(sql);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw convertError(err);
|
throw convertError(err);
|
||||||
}
|
}
|
||||||
@@ -301,7 +294,7 @@ class Statement {
|
|||||||
this.stmt.reset();
|
this.stmt.reset();
|
||||||
bindParams(this.stmt, bindParameters);
|
bindParams(this.stmt, bindParameters);
|
||||||
for (; ;) {
|
for (; ;) {
|
||||||
const stepResult = this.stmt.step();
|
const stepResult = this.stmt.stepSync();
|
||||||
if (stepResult === STEP_IO) {
|
if (stepResult === STEP_IO) {
|
||||||
this.db.db.ioLoopSync();
|
this.db.db.ioLoopSync();
|
||||||
continue;
|
continue;
|
||||||
@@ -330,7 +323,7 @@ class Statement {
|
|||||||
this.stmt.reset();
|
this.stmt.reset();
|
||||||
bindParams(this.stmt, bindParameters);
|
bindParams(this.stmt, bindParameters);
|
||||||
for (; ;) {
|
for (; ;) {
|
||||||
const stepResult = this.stmt.step();
|
const stepResult = this.stmt.stepSync();
|
||||||
if (stepResult === STEP_IO) {
|
if (stepResult === STEP_IO) {
|
||||||
this.db.db.ioLoopSync();
|
this.db.db.ioLoopSync();
|
||||||
continue;
|
continue;
|
||||||
@@ -354,7 +347,7 @@ class Statement {
|
|||||||
bindParams(this.stmt, bindParameters);
|
bindParams(this.stmt, bindParameters);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const stepResult = this.stmt.step();
|
const stepResult = this.stmt.stepSync();
|
||||||
if (stepResult === STEP_IO) {
|
if (stepResult === STEP_IO) {
|
||||||
this.db.db.ioLoopSync();
|
this.db.db.ioLoopSync();
|
||||||
continue;
|
continue;
|
||||||
@@ -378,7 +371,7 @@ class Statement {
|
|||||||
bindParams(this.stmt, bindParameters);
|
bindParams(this.stmt, bindParameters);
|
||||||
const rows: any[] = [];
|
const rows: any[] = [];
|
||||||
for (; ;) {
|
for (; ;) {
|
||||||
const stepResult = this.stmt.step();
|
const stepResult = this.stmt.stepSync();
|
||||||
if (stepResult === STEP_IO) {
|
if (stepResult === STEP_IO) {
|
||||||
this.db.db.ioLoopSync();
|
this.db.db.ioLoopSync();
|
||||||
continue;
|
continue;
|
||||||
@@ -417,4 +410,4 @@ class Statement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Database, SqliteError }
|
export { Database, Statement }
|
||||||
6
bindings/javascript/packages/core/index.ts
Normal file
6
bindings/javascript/packages/core/index.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { NativeDatabase, NativeStatement, DatabaseOpts } from "./types.js";
|
||||||
|
import { Database as DatabaseCompat, Statement as StatementCompat } from "./compat.js";
|
||||||
|
import { Database as DatabasePromise, Statement as StatementPromise } from "./promise.js";
|
||||||
|
import { SqliteError } from "./sqlite-error.js";
|
||||||
|
|
||||||
|
export { DatabaseCompat, StatementCompat, DatabasePromise, StatementPromise, NativeDatabase, NativeStatement, SqliteError, DatabaseOpts }
|
||||||
23
bindings/javascript/packages/core/package.json
Normal file
23
bindings/javascript/packages/core/package.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "@tursodatabase/database-core",
|
||||||
|
"version": "0.1.5-pre.4",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tursodatabase/turso"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"packageManager": "yarn@4.9.2",
|
||||||
|
"files": [
|
||||||
|
"dist/**"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.9.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"tsc-build": "npm exec tsc",
|
||||||
|
"build": "npm run tsc-build"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,6 @@
|
|||||||
import { Database as NativeDB, Statement as NativeStatement } from "#entry-point";
|
|
||||||
import { bindParams } from "./bind.js";
|
import { bindParams } from "./bind.js";
|
||||||
|
|
||||||
import { SqliteError } from "./sqlite-error.js";
|
import { SqliteError } from "./sqlite-error.js";
|
||||||
|
import { NativeDatabase, NativeStatement, STEP_IO, STEP_ROW, STEP_DONE, DatabaseOpts } from "./types.js";
|
||||||
// Step result constants
|
|
||||||
const STEP_ROW = 1;
|
|
||||||
const STEP_DONE = 2;
|
|
||||||
const STEP_IO = 3;
|
|
||||||
|
|
||||||
const convertibleErrorTypes = { TypeError };
|
const convertibleErrorTypes = { TypeError };
|
||||||
const CONVERTIBLE_ERROR_PREFIX = "[TURSO_CONVERT_TYPE]";
|
const CONVERTIBLE_ERROR_PREFIX = "[TURSO_CONVERT_TYPE]";
|
||||||
@@ -35,7 +29,7 @@ function createErrorByName(name, message) {
|
|||||||
* Database represents a connection that can prepare and execute SQL statements.
|
* Database represents a connection that can prepare and execute SQL statements.
|
||||||
*/
|
*/
|
||||||
class Database {
|
class Database {
|
||||||
db: NativeDB;
|
db: NativeDatabase;
|
||||||
memory: boolean;
|
memory: boolean;
|
||||||
open: boolean;
|
open: boolean;
|
||||||
private _inTransaction: boolean = false;
|
private _inTransaction: boolean = false;
|
||||||
@@ -49,19 +43,18 @@ class Database {
|
|||||||
* @param {boolean} [opts.fileMustExist=false] - If true, throws if database file does not exist.
|
* @param {boolean} [opts.fileMustExist=false] - If true, throws if database file does not exist.
|
||||||
* @param {number} [opts.timeout=0] - Timeout duration in milliseconds for database operations. Defaults to 0 (no timeout).
|
* @param {number} [opts.timeout=0] - Timeout duration in milliseconds for database operations. Defaults to 0 (no timeout).
|
||||||
*/
|
*/
|
||||||
constructor(path: string, opts: any = {}) {
|
constructor(db: NativeDatabase, opts: DatabaseOpts = {}) {
|
||||||
opts.readonly = opts.readonly === undefined ? false : opts.readonly;
|
opts.readonly = opts.readonly === undefined ? false : opts.readonly;
|
||||||
opts.fileMustExist =
|
opts.fileMustExist =
|
||||||
opts.fileMustExist === undefined ? false : opts.fileMustExist;
|
opts.fileMustExist === undefined ? false : opts.fileMustExist;
|
||||||
opts.timeout = opts.timeout === undefined ? 0 : opts.timeout;
|
opts.timeout = opts.timeout === undefined ? 0 : opts.timeout;
|
||||||
|
|
||||||
const db = new NativeDB(path);
|
this.initialize(db, opts.name, opts.readonly);
|
||||||
this.initialize(db, opts.path, opts.readonly);
|
|
||||||
}
|
}
|
||||||
static create() {
|
static create() {
|
||||||
return Object.create(this.prototype);
|
return Object.create(this.prototype);
|
||||||
}
|
}
|
||||||
initialize(db: NativeDB, name, readonly) {
|
initialize(db: NativeDatabase, name, readonly) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.memory = db.memory;
|
this.memory = db.memory;
|
||||||
Object.defineProperties(this, {
|
Object.defineProperties(this, {
|
||||||
@@ -112,22 +105,22 @@ class Database {
|
|||||||
*
|
*
|
||||||
* @param {function} fn - The function to wrap in a transaction.
|
* @param {function} fn - The function to wrap in a transaction.
|
||||||
*/
|
*/
|
||||||
transaction(fn) {
|
transaction(fn: (...any) => Promise<any>) {
|
||||||
if (typeof fn !== "function")
|
if (typeof fn !== "function")
|
||||||
throw new TypeError("Expected first argument to be a function");
|
throw new TypeError("Expected first argument to be a function");
|
||||||
|
|
||||||
const db = this;
|
const db = this;
|
||||||
const wrapTxn = (mode) => {
|
const wrapTxn = (mode) => {
|
||||||
return (...bindParameters) => {
|
return async (...bindParameters) => {
|
||||||
db.exec("BEGIN " + mode);
|
await db.exec("BEGIN " + mode);
|
||||||
db._inTransaction = true;
|
db._inTransaction = true;
|
||||||
try {
|
try {
|
||||||
const result = fn(...bindParameters);
|
const result = await fn(...bindParameters);
|
||||||
db.exec("COMMIT");
|
await db.exec("COMMIT");
|
||||||
db._inTransaction = false;
|
db._inTransaction = false;
|
||||||
return result;
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
db.exec("ROLLBACK");
|
await db.exec("ROLLBACK");
|
||||||
db._inTransaction = false;
|
db._inTransaction = false;
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -147,7 +140,7 @@ class Database {
|
|||||||
return properties.default.value;
|
return properties.default.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma(source, options) {
|
async pragma(source, options) {
|
||||||
if (options == null) options = {};
|
if (options == null) options = {};
|
||||||
|
|
||||||
if (typeof source !== "string")
|
if (typeof source !== "string")
|
||||||
@@ -158,8 +151,8 @@ class Database {
|
|||||||
|
|
||||||
const pragma = `PRAGMA ${source}`;
|
const pragma = `PRAGMA ${source}`;
|
||||||
|
|
||||||
const stmt = this.prepare(pragma);
|
const stmt = await this.prepare(pragma);
|
||||||
const results = stmt.all();
|
const results = await stmt.all();
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
@@ -197,13 +190,13 @@ class Database {
|
|||||||
*
|
*
|
||||||
* @param {string} sql - The SQL statement string to execute.
|
* @param {string} sql - The SQL statement string to execute.
|
||||||
*/
|
*/
|
||||||
exec(sql) {
|
async exec(sql) {
|
||||||
if (!this.open) {
|
if (!this.open) {
|
||||||
throw new TypeError("The database connection is not open");
|
throw new TypeError("The database connection is not open");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.db.batch(sql);
|
await this.db.batchAsync(sql);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw convertError(err);
|
throw convertError(err);
|
||||||
}
|
}
|
||||||
@@ -228,7 +221,7 @@ class Database {
|
|||||||
/**
|
/**
|
||||||
* Closes the database connection.
|
* Closes the database connection.
|
||||||
*/
|
*/
|
||||||
close() {
|
async close() {
|
||||||
this.db.close();
|
this.db.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,7 +298,7 @@ class Statement {
|
|||||||
bindParams(this.stmt, bindParameters);
|
bindParams(this.stmt, bindParameters);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const stepResult = this.stmt.step();
|
const stepResult = await this.stmt.stepAsync();
|
||||||
if (stepResult === STEP_IO) {
|
if (stepResult === STEP_IO) {
|
||||||
await this.db.db.ioLoopAsync();
|
await this.db.db.ioLoopAsync();
|
||||||
continue;
|
continue;
|
||||||
@@ -335,7 +328,7 @@ class Statement {
|
|||||||
bindParams(this.stmt, bindParameters);
|
bindParams(this.stmt, bindParameters);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const stepResult = this.stmt.step();
|
const stepResult = await this.stmt.stepAsync();
|
||||||
if (stepResult === STEP_IO) {
|
if (stepResult === STEP_IO) {
|
||||||
await this.db.db.ioLoopAsync();
|
await this.db.db.ioLoopAsync();
|
||||||
continue;
|
continue;
|
||||||
@@ -359,7 +352,7 @@ class Statement {
|
|||||||
bindParams(this.stmt, bindParameters);
|
bindParams(this.stmt, bindParameters);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const stepResult = this.stmt.step();
|
const stepResult = await this.stmt.stepAsync();
|
||||||
if (stepResult === STEP_IO) {
|
if (stepResult === STEP_IO) {
|
||||||
await this.db.db.ioLoopAsync();
|
await this.db.db.ioLoopAsync();
|
||||||
continue;
|
continue;
|
||||||
@@ -384,7 +377,7 @@ class Statement {
|
|||||||
const rows: any[] = [];
|
const rows: any[] = [];
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const stepResult = this.stmt.step();
|
const stepResult = await this.stmt.stepAsync();
|
||||||
if (stepResult === STEP_IO) {
|
if (stepResult === STEP_IO) {
|
||||||
await this.db.db.ioLoopAsync();
|
await this.db.db.ioLoopAsync();
|
||||||
continue;
|
continue;
|
||||||
@@ -422,16 +415,4 @@ class Statement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export { Database, Statement }
|
||||||
/**
|
|
||||||
* Creates a new database connection asynchronously.
|
|
||||||
*
|
|
||||||
* @param {string} path - Path to the database file.
|
|
||||||
* @param {Object} opts - Options for database behavior.
|
|
||||||
* @returns {Promise<Database>} - A promise that resolves to a Database instance.
|
|
||||||
*/
|
|
||||||
async function connect(path: string, opts: any = {}): Promise<Database> {
|
|
||||||
return new Database(path, opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Database, SqliteError, connect }
|
|
||||||
@@ -1,17 +1,14 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"outDir": "dist/",
|
"outDir": "dist/",
|
||||||
"lib": [
|
"lib": [
|
||||||
"es2020"
|
"es2020"
|
||||||
],
|
],
|
||||||
"paths": {
|
|
||||||
"#entry-point": [
|
|
||||||
"./index.js"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"*"
|
"*"
|
||||||
45
bindings/javascript/packages/core/types.ts
Normal file
45
bindings/javascript/packages/core/types.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
export interface DatabaseOpts {
|
||||||
|
readonly?: boolean,
|
||||||
|
fileMustExist?: boolean,
|
||||||
|
timeout?: number
|
||||||
|
name?: string
|
||||||
|
tracing?: 'info' | 'debug' | 'trace'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NativeDatabase {
|
||||||
|
memory: boolean,
|
||||||
|
path: string,
|
||||||
|
new(path: string): NativeDatabase;
|
||||||
|
batchSync(sql: string);
|
||||||
|
batchAsync(sql: string): Promise<void>;
|
||||||
|
|
||||||
|
ioLoopSync();
|
||||||
|
ioLoopAsync(): Promise<void>;
|
||||||
|
|
||||||
|
prepare(sql: string): NativeStatement;
|
||||||
|
|
||||||
|
pluck(pluckMode: boolean);
|
||||||
|
defaultSafeIntegers(toggle: boolean);
|
||||||
|
totalChanges(): number;
|
||||||
|
changes(): number;
|
||||||
|
lastInsertRowid(): number;
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Step result constants
|
||||||
|
export const STEP_ROW = 1;
|
||||||
|
export const STEP_DONE = 2;
|
||||||
|
export const STEP_IO = 3;
|
||||||
|
|
||||||
|
export interface NativeStatement {
|
||||||
|
stepAsync(): Promise<number>;
|
||||||
|
stepSync(): number;
|
||||||
|
|
||||||
|
pluck(pluckMode: boolean);
|
||||||
|
safeIntegers(toggle: boolean);
|
||||||
|
raw(toggle: boolean);
|
||||||
|
columns(): string[];
|
||||||
|
row(): any;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
129
bindings/javascript/packages/native/README.md
Normal file
129
bindings/javascript/packages/native/README.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<p align="center">
|
||||||
|
<h1 align="center">Turso Database for JavaScript</h1>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a title="JavaScript" target="_blank" href="https://www.npmjs.com/package/@tursodatabase/database"><img alt="npm" src="https://img.shields.io/npm/v/@tursodatabase/database"></a>
|
||||||
|
<a title="MIT" target="_blank" href="https://github.com/tursodatabase/turso/blob/main/LICENSE.md"><img src="http://img.shields.io/badge/license-MIT-orange.svg?style=flat-square"></a>
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<a title="Users Discord" target="_blank" href="https://tur.so/discord"><img alt="Chat with other users of Turso on Discord" src="https://img.shields.io/discord/933071162680958986?label=Discord&logo=Discord&style=social"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
This package is the Turso embedded database library for JavaScript.
|
||||||
|
|
||||||
|
> **⚠️ Warning:** This software is ALPHA, only use for development, testing, and experimentation. We are working to make it production ready, but do not use it for critical data right now.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **SQLite compatible:** SQLite query language and file format support ([status](https://github.com/tursodatabase/turso/blob/main/COMPAT.md)).
|
||||||
|
- **In-process**: No network overhead, runs directly in your Node.js process
|
||||||
|
- **TypeScript support**: Full TypeScript definitions included
|
||||||
|
- **Cross-platform**: Supports Linux (x86 and arm64), macOS, Windows and browsers (through WebAssembly)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @tursodatabase/database
|
||||||
|
```
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
### In-Memory Database
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { connect } from '@tursodatabase/database';
|
||||||
|
|
||||||
|
// Create an in-memory database
|
||||||
|
const db = await connect(':memory:');
|
||||||
|
|
||||||
|
// Create a table
|
||||||
|
db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)');
|
||||||
|
|
||||||
|
// Insert data
|
||||||
|
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
|
||||||
|
insert.run('Alice', 'alice@example.com');
|
||||||
|
insert.run('Bob', 'bob@example.com');
|
||||||
|
|
||||||
|
// Query data
|
||||||
|
const users = db.prepare('SELECT * FROM users').all();
|
||||||
|
console.log(users);
|
||||||
|
// Output: [
|
||||||
|
// { id: 1, name: 'Alice', email: 'alice@example.com' },
|
||||||
|
// { id: 2, name: 'Bob', email: 'bob@example.com' }
|
||||||
|
// ]
|
||||||
|
```
|
||||||
|
|
||||||
|
### File-Based Database
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { connect } from '@tursodatabase/database';
|
||||||
|
|
||||||
|
// Create or open a database file
|
||||||
|
const db = await connect('my-database.db');
|
||||||
|
|
||||||
|
// Create a table
|
||||||
|
db.exec(`
|
||||||
|
CREATE TABLE IF NOT EXISTS posts (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
content TEXT,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Insert a post
|
||||||
|
const insertPost = db.prepare('INSERT INTO posts (title, content) VALUES (?, ?)');
|
||||||
|
const result = insertPost.run('Hello World', 'This is my first blog post!');
|
||||||
|
|
||||||
|
console.log(`Inserted post with ID: ${result.lastInsertRowid}`);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transactions
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { connect } from '@tursodatabase/database';
|
||||||
|
|
||||||
|
const db = await connect('transactions.db');
|
||||||
|
|
||||||
|
// Using transactions for atomic operations
|
||||||
|
const transaction = db.transaction((users) => {
|
||||||
|
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
|
||||||
|
for (const user of users) {
|
||||||
|
insert.run(user.name, user.email);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Execute transaction
|
||||||
|
transaction([
|
||||||
|
{ name: 'Alice', email: 'alice@example.com' },
|
||||||
|
{ name: 'Bob', email: 'bob@example.com' }
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebAssembly Support
|
||||||
|
|
||||||
|
Turso Database can run in browsers using WebAssembly from separate package. Check the `@tursodatabase/database-browser` for more details.
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
For complete API documentation, see [JavaScript API Reference](../../../../docs/javascript-api-reference.md).
|
||||||
|
|
||||||
|
## Related Packages
|
||||||
|
|
||||||
|
* The [@tursodatabase/serverless](https://www.npmjs.com/package/@tursodatabase/serverless) package provides a serverless driver with the same API.
|
||||||
|
* The [@tursodatabase/sync](https://www.npmjs.com/package/@tursodatabase/sync) package provides bidirectional sync between a local Turso database and Turso Cloud.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the [MIT license](../../LICENSE.md).
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
- [GitHub Issues](https://github.com/tursodatabase/turso/issues)
|
||||||
|
- [Documentation](https://docs.turso.tech)
|
||||||
|
- [Discord Community](https://tur.so/discord)
|
||||||
67
bindings/javascript/packages/native/compat.test.ts
Normal file
67
bindings/javascript/packages/native/compat.test.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { unlinkSync } from "node:fs";
|
||||||
|
import { expect, test } from 'vitest'
|
||||||
|
import { Database } from './compat.js'
|
||||||
|
|
||||||
|
test('in-memory db', () => {
|
||||||
|
const db = new Database(":memory:");
|
||||||
|
db.exec("CREATE TABLE t(x)");
|
||||||
|
db.exec("INSERT INTO t VALUES (1), (2), (3)");
|
||||||
|
const stmt = db.prepare("SELECT * FROM t WHERE x % 2 = ?");
|
||||||
|
const rows = stmt.all([1]);
|
||||||
|
expect(rows).toEqual([{ x: 1 }, { x: 3 }]);
|
||||||
|
})
|
||||||
|
|
||||||
|
test('on-disk db', () => {
|
||||||
|
const path = `test-${(Math.random() * 10000) | 0}.db`;
|
||||||
|
try {
|
||||||
|
const db1 = new Database(path);
|
||||||
|
db1.exec("CREATE TABLE t(x)");
|
||||||
|
db1.exec("INSERT INTO t VALUES (1), (2), (3)");
|
||||||
|
const stmt1 = db1.prepare("SELECT * FROM t WHERE x % 2 = ?");
|
||||||
|
expect(stmt1.columns()).toEqual([{ name: "x", column: null, database: null, table: null, type: null }]);
|
||||||
|
const rows1 = stmt1.all([1]);
|
||||||
|
expect(rows1).toEqual([{ x: 1 }, { x: 3 }]);
|
||||||
|
db1.close();
|
||||||
|
|
||||||
|
const db2 = new Database(path);
|
||||||
|
const stmt2 = db2.prepare("SELECT * FROM t WHERE x % 2 = ?");
|
||||||
|
expect(stmt2.columns()).toEqual([{ name: "x", column: null, database: null, table: null, type: null }]);
|
||||||
|
const rows2 = stmt2.all([1]);
|
||||||
|
expect(rows2).toEqual([{ x: 1 }, { x: 3 }]);
|
||||||
|
db2.close();
|
||||||
|
} finally {
|
||||||
|
unlinkSync(path);
|
||||||
|
unlinkSync(`${path}-wal`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('attach', () => {
|
||||||
|
const path1 = `test-${(Math.random() * 10000) | 0}.db`;
|
||||||
|
const path2 = `test-${(Math.random() * 10000) | 0}.db`;
|
||||||
|
try {
|
||||||
|
const db1 = new Database(path1);
|
||||||
|
db1.exec("CREATE TABLE t(x)");
|
||||||
|
db1.exec("INSERT INTO t VALUES (1), (2), (3)");
|
||||||
|
const db2 = new Database(path2);
|
||||||
|
db2.exec("CREATE TABLE q(x)");
|
||||||
|
db2.exec("INSERT INTO q VALUES (4), (5), (6)");
|
||||||
|
|
||||||
|
db1.exec(`ATTACH '${path2}' as secondary`);
|
||||||
|
|
||||||
|
const stmt = db1.prepare("SELECT * FROM t UNION ALL SELECT * FROM secondary.q");
|
||||||
|
expect(stmt.columns()).toEqual([{ name: "x", column: null, database: null, table: null, type: null }]);
|
||||||
|
const rows = stmt.all([1]);
|
||||||
|
expect(rows).toEqual([{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }, { x: 5 }, { x: 6 }]);
|
||||||
|
} finally {
|
||||||
|
unlinkSync(path1);
|
||||||
|
unlinkSync(`${path1}-wal`);
|
||||||
|
unlinkSync(path2);
|
||||||
|
unlinkSync(`${path2}-wal`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('blobs', () => {
|
||||||
|
const db = new Database(":memory:");
|
||||||
|
const rows = db.prepare("SELECT x'1020' as x").all();
|
||||||
|
expect(rows).toEqual([{ x: Buffer.from([16, 32]) }])
|
||||||
|
})
|
||||||
10
bindings/javascript/packages/native/compat.ts
Normal file
10
bindings/javascript/packages/native/compat.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { DatabaseCompat, NativeDatabase, SqliteError, DatabaseOpts } from "@tursodatabase/database-core"
|
||||||
|
import { Database as NativeDB } from "#index";
|
||||||
|
|
||||||
|
class Database extends DatabaseCompat {
|
||||||
|
constructor(path: string, opts: DatabaseOpts = {}) {
|
||||||
|
super(new NativeDB(path, { tracing: opts.tracing }) as unknown as NativeDatabase, opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Database, SqliteError }
|
||||||
@@ -8,13 +8,13 @@ export declare class Database {
|
|||||||
* # Arguments
|
* # Arguments
|
||||||
* * `path` - The path to the database file.
|
* * `path` - The path to the database file.
|
||||||
*/
|
*/
|
||||||
constructor(path: string)
|
constructor(path: string, opts?: DatabaseOpts | undefined | null)
|
||||||
/** Returns whether the database is in memory-only mode. */
|
/** Returns whether the database is in memory-only mode. */
|
||||||
get memory(): boolean
|
get memory(): boolean
|
||||||
/** Returns whether the database connection is open. */
|
/** Returns whether the database connection is open. */
|
||||||
get open(): boolean
|
get open(): boolean
|
||||||
/**
|
/**
|
||||||
* Executes a batch of SQL statements.
|
* Executes a batch of SQL statements on main thread
|
||||||
*
|
*
|
||||||
* # Arguments
|
* # Arguments
|
||||||
*
|
*
|
||||||
@@ -22,7 +22,17 @@ export declare class Database {
|
|||||||
*
|
*
|
||||||
* # Returns
|
* # Returns
|
||||||
*/
|
*/
|
||||||
batch(sql: string): void
|
batchSync(sql: string): void
|
||||||
|
/**
|
||||||
|
* Executes a batch of SQL statements outside of main thread
|
||||||
|
*
|
||||||
|
* # Arguments
|
||||||
|
*
|
||||||
|
* * `sql` - The SQL statements to execute.
|
||||||
|
*
|
||||||
|
* # Returns
|
||||||
|
*/
|
||||||
|
batchAsync(sql: string): Promise<unknown>
|
||||||
/**
|
/**
|
||||||
* Prepares a statement for execution.
|
* Prepares a statement for execution.
|
||||||
*
|
*
|
||||||
@@ -105,10 +115,15 @@ export declare class Statement {
|
|||||||
*/
|
*/
|
||||||
bindAt(index: number, value: unknown): void
|
bindAt(index: number, value: unknown): void
|
||||||
/**
|
/**
|
||||||
* Step the statement and return result code:
|
* Step the statement and return result code (executed on the main thread):
|
||||||
* 1 = Row available, 2 = Done, 3 = I/O needed
|
* 1 = Row available, 2 = Done, 3 = I/O needed
|
||||||
*/
|
*/
|
||||||
step(): number
|
stepSync(): number
|
||||||
|
/**
|
||||||
|
* Step the statement and return result code (executed on the background thread):
|
||||||
|
* 1 = Row available, 2 = Done, 3 = I/O needed
|
||||||
|
*/
|
||||||
|
stepAsync(): Promise<unknown>
|
||||||
/** Get the current row data according to the presentation mode */
|
/** Get the current row data according to the presentation mode */
|
||||||
row(): unknown
|
row(): unknown
|
||||||
/** Sets the presentation mode to raw. */
|
/** Sets the presentation mode to raw. */
|
||||||
@@ -128,3 +143,7 @@ export declare class Statement {
|
|||||||
/** Finalizes the statement. */
|
/** Finalizes the statement. */
|
||||||
finalize(): void
|
finalize(): void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DatabaseOpts {
|
||||||
|
tracing?: string
|
||||||
|
}
|
||||||
513
bindings/javascript/packages/native/index.js
Normal file
513
bindings/javascript/packages/native/index.js
Normal file
@@ -0,0 +1,513 @@
|
|||||||
|
// prettier-ignore
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
/* auto-generated by NAPI-RS */
|
||||||
|
|
||||||
|
import { createRequire } from 'node:module'
|
||||||
|
const require = createRequire(import.meta.url)
|
||||||
|
const __dirname = new URL('.', import.meta.url).pathname
|
||||||
|
|
||||||
|
const { readFileSync } = require('node:fs')
|
||||||
|
let nativeBinding = null
|
||||||
|
const loadErrors = []
|
||||||
|
|
||||||
|
const isMusl = () => {
|
||||||
|
let musl = false
|
||||||
|
if (process.platform === 'linux') {
|
||||||
|
musl = isMuslFromFilesystem()
|
||||||
|
if (musl === null) {
|
||||||
|
musl = isMuslFromReport()
|
||||||
|
}
|
||||||
|
if (musl === null) {
|
||||||
|
musl = isMuslFromChildProcess()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return musl
|
||||||
|
}
|
||||||
|
|
||||||
|
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
|
||||||
|
|
||||||
|
const isMuslFromFilesystem = () => {
|
||||||
|
try {
|
||||||
|
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMuslFromReport = () => {
|
||||||
|
let report = null
|
||||||
|
if (typeof process.report?.getReport === 'function') {
|
||||||
|
process.report.excludeNetwork = true
|
||||||
|
report = process.report.getReport()
|
||||||
|
}
|
||||||
|
if (!report) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (report.header && report.header.glibcVersionRuntime) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (Array.isArray(report.sharedObjects)) {
|
||||||
|
if (report.sharedObjects.some(isFileMusl)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMuslFromChildProcess = () => {
|
||||||
|
try {
|
||||||
|
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
|
||||||
|
} catch (e) {
|
||||||
|
// If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireNative() {
|
||||||
|
if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
|
||||||
|
try {
|
||||||
|
nativeBinding = require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH);
|
||||||
|
} catch (err) {
|
||||||
|
loadErrors.push(err)
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'android') {
|
||||||
|
if (process.arch === 'arm64') {
|
||||||
|
try {
|
||||||
|
return require('./turso.android-arm64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-android-arm64')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-android-arm64/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm') {
|
||||||
|
try {
|
||||||
|
return require('./turso.android-arm-eabi.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-android-arm-eabi')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-android-arm-eabi/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'win32') {
|
||||||
|
if (process.arch === 'x64') {
|
||||||
|
try {
|
||||||
|
return require('./turso.win32-x64-msvc.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-win32-x64-msvc')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-win32-x64-msvc/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'ia32') {
|
||||||
|
try {
|
||||||
|
return require('./turso.win32-ia32-msvc.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-win32-ia32-msvc')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-win32-ia32-msvc/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm64') {
|
||||||
|
try {
|
||||||
|
return require('./turso.win32-arm64-msvc.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-win32-arm64-msvc')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-win32-arm64-msvc/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'darwin') {
|
||||||
|
try {
|
||||||
|
return require('./turso.darwin-universal.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-darwin-universal')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-darwin-universal/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
if (process.arch === 'x64') {
|
||||||
|
try {
|
||||||
|
return require('./turso.darwin-x64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-darwin-x64')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-darwin-x64/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm64') {
|
||||||
|
try {
|
||||||
|
return require('./turso.darwin-arm64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-darwin-arm64')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-darwin-arm64/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'freebsd') {
|
||||||
|
if (process.arch === 'x64') {
|
||||||
|
try {
|
||||||
|
return require('./turso.freebsd-x64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-freebsd-x64')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-freebsd-x64/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm64') {
|
||||||
|
try {
|
||||||
|
return require('./turso.freebsd-arm64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-freebsd-arm64')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-freebsd-arm64/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'linux') {
|
||||||
|
if (process.arch === 'x64') {
|
||||||
|
if (isMusl()) {
|
||||||
|
try {
|
||||||
|
return require('./turso.linux-x64-musl.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-linux-x64-musl')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-linux-x64-musl/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return require('./turso.linux-x64-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-linux-x64-gnu')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-linux-x64-gnu/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm64') {
|
||||||
|
if (isMusl()) {
|
||||||
|
try {
|
||||||
|
return require('./turso.linux-arm64-musl.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-linux-arm64-musl')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-linux-arm64-musl/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return require('./turso.linux-arm64-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-linux-arm64-gnu')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-linux-arm64-gnu/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm') {
|
||||||
|
if (isMusl()) {
|
||||||
|
try {
|
||||||
|
return require('./turso.linux-arm-musleabihf.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-linux-arm-musleabihf')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-linux-arm-musleabihf/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return require('./turso.linux-arm-gnueabihf.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-linux-arm-gnueabihf')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-linux-arm-gnueabihf/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'riscv64') {
|
||||||
|
if (isMusl()) {
|
||||||
|
try {
|
||||||
|
return require('./turso.linux-riscv64-musl.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-linux-riscv64-musl')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-linux-riscv64-musl/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return require('./turso.linux-riscv64-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-linux-riscv64-gnu')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-linux-riscv64-gnu/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'ppc64') {
|
||||||
|
try {
|
||||||
|
return require('./turso.linux-ppc64-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-linux-ppc64-gnu')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-linux-ppc64-gnu/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 's390x') {
|
||||||
|
try {
|
||||||
|
return require('./turso.linux-s390x-gnu.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-linux-s390x-gnu')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-linux-s390x-gnu/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else if (process.platform === 'openharmony') {
|
||||||
|
if (process.arch === 'arm64') {
|
||||||
|
try {
|
||||||
|
return require('./turso.openharmony-arm64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-openharmony-arm64')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-openharmony-arm64/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'x64') {
|
||||||
|
try {
|
||||||
|
return require('./turso.openharmony-x64.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-openharmony-x64')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-openharmony-x64/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else if (process.arch === 'arm') {
|
||||||
|
try {
|
||||||
|
return require('./turso.openharmony-arm.node')
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const binding = require('@tursodatabase/database-openharmony-arm')
|
||||||
|
const bindingPackageVersion = require('@tursodatabase/database-openharmony-arm/package.json').version
|
||||||
|
if (bindingPackageVersion !== '0.1.5-pre.4' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
|
||||||
|
throw new Error(`Native binding package version mismatch, expected 0.1.5-pre.4 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
|
||||||
|
}
|
||||||
|
return binding
|
||||||
|
} catch (e) {
|
||||||
|
loadErrors.push(e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported architecture on OpenHarmony: ${process.arch}`))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nativeBinding = requireNative()
|
||||||
|
|
||||||
|
if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
|
||||||
|
try {
|
||||||
|
nativeBinding = require('./turso.wasi.cjs')
|
||||||
|
} catch (err) {
|
||||||
|
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||||
|
loadErrors.push(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!nativeBinding) {
|
||||||
|
try {
|
||||||
|
nativeBinding = require('@tursodatabase/database-wasm32-wasi')
|
||||||
|
} catch (err) {
|
||||||
|
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||||
|
loadErrors.push(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nativeBinding) {
|
||||||
|
if (loadErrors.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot find native binding. ` +
|
||||||
|
`npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` +
|
||||||
|
'Please try `npm i` again after removing both package-lock.json and node_modules directory.',
|
||||||
|
{ cause: loadErrors }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
throw new Error(`Failed to load native binding`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { Database, Statement } = nativeBinding
|
||||||
|
export { Database }
|
||||||
|
export { Statement }
|
||||||
48
bindings/javascript/packages/native/package.json
Normal file
48
bindings/javascript/packages/native/package.json
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"name": "@tursodatabase/database",
|
||||||
|
"version": "0.1.5-pre.4",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/tursodatabase/turso"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"module": "./dist/promise.js",
|
||||||
|
"main": "./dist/promise.js",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": "./dist/promise.js",
|
||||||
|
"./compat": "./dist/compat.js"
|
||||||
|
},
|
||||||
|
"packageManager": "yarn@4.9.2",
|
||||||
|
"devDependencies": {
|
||||||
|
"@napi-rs/cli": "^3.1.5",
|
||||||
|
"@napi-rs/wasm-runtime": "^1.0.3",
|
||||||
|
"@types/node": "^24.3.1",
|
||||||
|
"typescript": "^5.9.2",
|
||||||
|
"vitest": "^3.2.4"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"napi-build": "napi build --platform --release --esm --manifest-path ../../Cargo.toml --output-dir . && rm turso.wasi* wasi* browser.js",
|
||||||
|
"napi-dirs": "napi create-npm-dirs",
|
||||||
|
"napi-artifacts": "napi artifacts --output-dir .",
|
||||||
|
"tsc-build": "npm exec tsc",
|
||||||
|
"build": "npm run napi-build && npm run tsc-build",
|
||||||
|
"test": "vitest --run"
|
||||||
|
},
|
||||||
|
"napi": {
|
||||||
|
"binaryName": "turso",
|
||||||
|
"targets": [
|
||||||
|
"x86_64-unknown-linux-gnu",
|
||||||
|
"x86_64-pc-windows-msvc",
|
||||||
|
"universal-apple-darwin",
|
||||||
|
"aarch64-unknown-linux-gnu",
|
||||||
|
"wasm32-wasip1-threads"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tursodatabase/database-core": "^0.1.5-pre.4"
|
||||||
|
},
|
||||||
|
"imports": {
|
||||||
|
"#index": "./index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
67
bindings/javascript/packages/native/promise.test.ts
Normal file
67
bindings/javascript/packages/native/promise.test.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { unlinkSync } from "node:fs";
|
||||||
|
import { expect, test } from 'vitest'
|
||||||
|
import { connect } from './promise.js'
|
||||||
|
|
||||||
|
test('in-memory db', async () => {
|
||||||
|
const db = await connect(":memory:");
|
||||||
|
await db.exec("CREATE TABLE t(x)");
|
||||||
|
await db.exec("INSERT INTO t VALUES (1), (2), (3)");
|
||||||
|
const stmt = db.prepare("SELECT * FROM t WHERE x % 2 = ?");
|
||||||
|
const rows = await stmt.all([1]);
|
||||||
|
expect(rows).toEqual([{ x: 1 }, { x: 3 }]);
|
||||||
|
})
|
||||||
|
|
||||||
|
test('on-disk db', async () => {
|
||||||
|
const path = `test-${(Math.random() * 10000) | 0}.db`;
|
||||||
|
try {
|
||||||
|
const db1 = await connect(path);
|
||||||
|
await db1.exec("CREATE TABLE t(x)");
|
||||||
|
await db1.exec("INSERT INTO t VALUES (1), (2), (3)");
|
||||||
|
const stmt1 = db1.prepare("SELECT * FROM t WHERE x % 2 = ?");
|
||||||
|
expect(stmt1.columns()).toEqual([{ name: "x", column: null, database: null, table: null, type: null }]);
|
||||||
|
const rows1 = await stmt1.all([1]);
|
||||||
|
expect(rows1).toEqual([{ x: 1 }, { x: 3 }]);
|
||||||
|
db1.close();
|
||||||
|
|
||||||
|
const db2 = await connect(path);
|
||||||
|
const stmt2 = db2.prepare("SELECT * FROM t WHERE x % 2 = ?");
|
||||||
|
expect(stmt2.columns()).toEqual([{ name: "x", column: null, database: null, table: null, type: null }]);
|
||||||
|
const rows2 = await stmt2.all([1]);
|
||||||
|
expect(rows2).toEqual([{ x: 1 }, { x: 3 }]);
|
||||||
|
db2.close();
|
||||||
|
} finally {
|
||||||
|
unlinkSync(path);
|
||||||
|
unlinkSync(`${path}-wal`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('attach', async () => {
|
||||||
|
const path1 = `test-${(Math.random() * 10000) | 0}.db`;
|
||||||
|
const path2 = `test-${(Math.random() * 10000) | 0}.db`;
|
||||||
|
try {
|
||||||
|
const db1 = await connect(path1);
|
||||||
|
await db1.exec("CREATE TABLE t(x)");
|
||||||
|
await db1.exec("INSERT INTO t VALUES (1), (2), (3)");
|
||||||
|
const db2 = await connect(path2);
|
||||||
|
await db2.exec("CREATE TABLE q(x)");
|
||||||
|
await db2.exec("INSERT INTO q VALUES (4), (5), (6)");
|
||||||
|
|
||||||
|
await db1.exec(`ATTACH '${path2}' as secondary`);
|
||||||
|
|
||||||
|
const stmt = db1.prepare("SELECT * FROM t UNION ALL SELECT * FROM secondary.q");
|
||||||
|
expect(stmt.columns()).toEqual([{ name: "x", column: null, database: null, table: null, type: null }]);
|
||||||
|
const rows = await stmt.all([1]);
|
||||||
|
expect(rows).toEqual([{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }, { x: 5 }, { x: 6 }]);
|
||||||
|
} finally {
|
||||||
|
unlinkSync(path1);
|
||||||
|
unlinkSync(`${path1}-wal`);
|
||||||
|
unlinkSync(path2);
|
||||||
|
unlinkSync(`${path2}-wal`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('blobs', async () => {
|
||||||
|
const db = await connect(":memory:");
|
||||||
|
const rows = await db.prepare("SELECT x'1020' as x").all();
|
||||||
|
expect(rows).toEqual([{ x: Buffer.from([16, 32]) }])
|
||||||
|
})
|
||||||
21
bindings/javascript/packages/native/promise.ts
Normal file
21
bindings/javascript/packages/native/promise.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { DatabasePromise, NativeDatabase, SqliteError, DatabaseOpts } from "@tursodatabase/database-core"
|
||||||
|
import { Database as NativeDB } from "#index";
|
||||||
|
|
||||||
|
class Database extends DatabasePromise {
|
||||||
|
constructor(path: string, opts: DatabaseOpts = {}) {
|
||||||
|
super(new NativeDB(path, { tracing: opts.tracing }) as unknown as NativeDatabase, opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new database connection asynchronously.
|
||||||
|
*
|
||||||
|
* @param {string} path - Path to the database file.
|
||||||
|
* @param {Object} opts - Options for database behavior.
|
||||||
|
* @returns {Promise<Database>} - A promise that resolves to a Database instance.
|
||||||
|
*/
|
||||||
|
async function connect(path: string, opts: any = {}): Promise<Database> {
|
||||||
|
return new Database(path, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { connect, Database, SqliteError }
|
||||||
21
bindings/javascript/packages/native/tsconfig.json
Normal file
21
bindings/javascript/packages/native/tsconfig.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"module": "nodenext",
|
||||||
|
"target": "esnext",
|
||||||
|
"outDir": "dist/",
|
||||||
|
"lib": [
|
||||||
|
"es2020"
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"#index": [
|
||||||
|
"./index.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
}
|
||||||
31
bindings/javascript/perf/package-lock.json
generated
31
bindings/javascript/perf/package-lock.json
generated
@@ -6,28 +6,33 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "turso-perf",
|
"name": "turso-perf",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tursodatabase/database": "..",
|
"@tursodatabase/database": "../packages/native",
|
||||||
"better-sqlite3": "^9.5.0",
|
"better-sqlite3": "^9.5.0",
|
||||||
"mitata": "^0.1.11"
|
"mitata": "^0.1.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"..": {
|
"..": {
|
||||||
"name": "@tursodatabase/database",
|
"workspaces": [
|
||||||
"version": "0.1.4-pre.4",
|
"packages/core",
|
||||||
"license": "MIT",
|
"packages/native",
|
||||||
"devDependencies": {
|
"packages/browser"
|
||||||
"@napi-rs/cli": "^3.0.4",
|
]
|
||||||
"@napi-rs/wasm-runtime": "^1.0.1",
|
|
||||||
"ava": "^6.0.1",
|
|
||||||
"better-sqlite3": "^11.9.1",
|
|
||||||
"typescript": "^5.9.2"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"../packages/native": {
|
||||||
"node": ">= 10"
|
"name": "@tursodatabase/database",
|
||||||
|
"version": "0.1.5-pre.4",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@tursodatabase/database-core": "^0.1.5-pre.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@napi-rs/cli": "^3.1.5",
|
||||||
|
"@napi-rs/wasm-runtime": "^1.0.3",
|
||||||
|
"typescript": "^5.9.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tursodatabase/database": {
|
"node_modules/@tursodatabase/database": {
|
||||||
"resolved": "..",
|
"resolved": "../packages/native",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
"name": "turso-perf",
|
"name": "turso-perf",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"better-sqlite3": "^9.5.0",
|
"better-sqlite3": "^9.5.0",
|
||||||
"@tursodatabase/database": "..",
|
"@tursodatabase/database": "../packages/native",
|
||||||
"mitata": "^0.1.11"
|
"mitata": "^0.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { run, bench, group, baseline } from 'mitata';
|
import { run, bench, group, baseline } from 'mitata';
|
||||||
|
|
||||||
import Database from '@tursodatabase/database';
|
import { Database } from '@tursodatabase/database';
|
||||||
|
|
||||||
const db = new Database(':memory:');
|
const db = new Database(':memory:');
|
||||||
|
|
||||||
|
|||||||
@@ -1,112 +0,0 @@
|
|||||||
/* 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/database-wasm32-wasi')
|
|
||||||
} catch {
|
|
||||||
throw new Error('Cannot find turso.wasm32-wasi.wasm file, and @tursodatabase/database-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
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
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);
|
|
||||||
};
|
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user