From c34f9de4ea05f80d5458d91909fb2c8884f74b76 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Tue, 29 Jul 2025 16:04:35 +0300 Subject: [PATCH 1/4] serverless: Add URL validation on Connection constructor --- packages/turso-serverless/src/connection.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/turso-serverless/src/connection.ts b/packages/turso-serverless/src/connection.ts index 907354671..a7846e8dc 100644 --- a/packages/turso-serverless/src/connection.ts +++ b/packages/turso-serverless/src/connection.ts @@ -17,6 +17,9 @@ export class Connection { private session: Session; constructor(config: Config) { + if (!config.url) { + throw new Error("invalid config: url is required"); + } this.config = config; this.session = new Session(config); } From b4dc9bebe0f37c8dc3ec857371f97bb0f21f451e Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Tue, 29 Jul 2025 16:05:28 +0300 Subject: [PATCH 2/4] testing/javascript: Improve error message if TURSO_DATABASE_URL is not set --- testing/javascript/__test__/async.test.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/testing/javascript/__test__/async.test.js b/testing/javascript/__test__/async.test.js index c1250ec6a..e0066f014 100644 --- a/testing/javascript/__test__/async.test.js +++ b/testing/javascript/__test__/async.test.js @@ -413,10 +413,13 @@ const connect = async (path, options = {}) => { if (provider === "serverless") { const x = await import("@tursodatabase/serverless"); const url = process.env.TURSO_DATABASE_URL; + if (!url) { + throw new Error("TURSO_DATABASE_URL is not set"); + } const authToken = process.env.TURSO_AUTH_TOKEN; const db = new x.connect({ - url: process.env.TURSO_DATABASE_URL, - authToken: process.env.TURSO_AUTH_TOKEN, + url, + authToken, }); return [db, x.SqliteError]; } From c39f0e75572e87e04ca6688cd9fbf24d715a7b9e Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Tue, 29 Jul 2025 16:07:35 +0300 Subject: [PATCH 3/4] testing/javascript: Add libsql to compatibility test suite --- testing/javascript/__test__/async.test.js | 5 + testing/javascript/__test__/sync.test.js | 5 + testing/javascript/package-lock.json | 169 +++++++++++++++++++++- testing/javascript/package.json | 6 +- 4 files changed, 181 insertions(+), 4 deletions(-) diff --git a/testing/javascript/__test__/async.test.js b/testing/javascript/__test__/async.test.js index e0066f014..9ae77cf56 100644 --- a/testing/javascript/__test__/async.test.js +++ b/testing/javascript/__test__/async.test.js @@ -410,6 +410,11 @@ const connect = async (path, options = {}) => { const db = new x.default(path, options); return [db, x.SqliteError]; } + if (provider === "libsql") { + const x = await import("libsql/promise"); + const db = new x.default(path, options); + return [db, x.SqliteError, path]; + } if (provider === "serverless") { const x = await import("@tursodatabase/serverless"); const url = process.env.TURSO_DATABASE_URL; diff --git a/testing/javascript/__test__/sync.test.js b/testing/javascript/__test__/sync.test.js index 7a1b0f25a..bdcf6e90e 100644 --- a/testing/javascript/__test__/sync.test.js +++ b/testing/javascript/__test__/sync.test.js @@ -453,6 +453,11 @@ const connect = async (path, options = {}) => { const db = new x.default(path, options); return [db, x.SqliteError, provider]; } + if (provider === "libsql") { + const x = await import("libsql"); + const db = new x.default(path, options); + return [db, x.SqliteError, provider, path]; + } if (provider == "better-sqlite3") { const x = await import("better-sqlite3"); const db = x.default(path, options); diff --git a/testing/javascript/package-lock.json b/testing/javascript/package-lock.json index 112be4ff7..0a2c51688 100644 --- a/testing/javascript/package-lock.json +++ b/testing/javascript/package-lock.json @@ -8,7 +8,8 @@ "dependencies": { "@tursodatabase/serverless": "../../packages/turso-serverless", "@tursodatabase/turso": "../../bindings/javascript", - "better-sqlite3": "^11.9.1" + "better-sqlite3": "^11.9.1", + "libsql": "^0.5.17" }, "devDependencies": { "ava": "^5.3.0" @@ -21,7 +22,7 @@ "devDependencies": { "@napi-rs/cli": "^3.0.4", "@napi-rs/wasm-runtime": "^1.0.1", - "ava": "^5.3.0", + "ava": "^6.0.1", "better-sqlite3": "^11.9.1" }, "engines": { @@ -38,6 +39,129 @@ "typescript": "^5.8.3" } }, + "node_modules/@libsql/darwin-arm64": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@libsql/darwin-arm64/-/darwin-arm64-0.5.17.tgz", + "integrity": "sha512-WTYG2skZsUnZmfZ2v7WFj7s3/5s2PfrYBZOWBKOnxHA8g4XCDc/4bFDaqob9Q2e88+GC7cWeJ8VNkVBFpD2Xxg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@libsql/darwin-x64": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@libsql/darwin-x64/-/darwin-x64-0.5.17.tgz", + "integrity": "sha512-ab0RlTR4KYrxgjNrZhAhY/10GibKoq6G0W4oi0kdm+eYiAv/Ip8GDMpSaZdAcoKA4T+iKR/ehczKHnMEB8MFxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@libsql/linux-arm-gnueabihf": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm-gnueabihf/-/linux-arm-gnueabihf-0.5.17.tgz", + "integrity": "sha512-PcASh4k47RqC+kMWAbLUKf1y6Do0q8vnUGi0yhKY4ghJcimMExViBimjbjYRSa+WIb/zh3QxNoXOhQAXx3tiuw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-arm-musleabihf": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm-musleabihf/-/linux-arm-musleabihf-0.5.17.tgz", + "integrity": "sha512-vxOkSLG9Wspit+SNle84nuIzMtr2G2qaxFzW7BhsZBjlZ8+kErf9RXcT2YJQdJYxmBYRbsOrc91gg0jLEQVCqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-arm64-gnu": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-gnu/-/linux-arm64-gnu-0.5.17.tgz", + "integrity": "sha512-L8jnaN01TxjBJlDuDTX2W2BKzBkAOhcnKfCOf3xzvvygblxnDOK0whkYwIXeTfwtd/rr4jN/d6dZD/bcHiDxEQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-arm64-musl": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-musl/-/linux-arm64-musl-0.5.17.tgz", + "integrity": "sha512-HfFD7TzQtmmTwyQsuiHhWZdMRtdNpKJ1p4tbMMTMRECk+971NFHrj69D64cc2ClVTAmn7fA9XibKPil7WN/Q7w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-x64-gnu": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-gnu/-/linux-x64-gnu-0.5.17.tgz", + "integrity": "sha512-5l3XxWqUPVFrtX0xnZaXwqsXs0BFbP4w6ahRFTPSdXU50YBfUOajFznJRB6bJTMsCvraDSD0IkHhjSNfrE1CuQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-x64-musl": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-musl/-/linux-x64-musl-0.5.17.tgz", + "integrity": "sha512-FvSpWlwc+dIeYIFYlsSv+UdQ/NiZWr+SstwVji+QZ//8NnvzwWQU9cgP+Vpps6Qiq4jyYQm9chJhTYOVT9Y3BA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/win32-x64-msvc": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@libsql/win32-x64-msvc/-/win32-x64-msvc-0.5.17.tgz", + "integrity": "sha512-f5bGH8+3A5sn6Lrqg8FsQ09a1pYXPnKGXGTFiAYlfQXVst1tUTxDTugnuWcJYKXyzDe/T7ccxyIZXeSmPOhq8A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@neon-rs/load": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@neon-rs/load/-/load-0.0.4.tgz", + "integrity": "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==", + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1211,6 +1335,47 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/libsql": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/libsql/-/libsql-0.5.17.tgz", + "integrity": "sha512-RRlj5XQI9+Wq+/5UY8EnugSWfRmHEw4hn3DKlPrkUgZONsge1PwTtHcpStP6MSNi8ohcbsRgEHJaymA33a8cBw==", + "cpu": [ + "x64", + "arm64", + "wasm32", + "arm" + ], + "license": "MIT", + "os": [ + "darwin", + "linux", + "win32" + ], + "dependencies": { + "@neon-rs/load": "^0.0.4", + "detect-libc": "2.0.2" + }, + "optionalDependencies": { + "@libsql/darwin-arm64": "0.5.17", + "@libsql/darwin-x64": "0.5.17", + "@libsql/linux-arm-gnueabihf": "0.5.17", + "@libsql/linux-arm-musleabihf": "0.5.17", + "@libsql/linux-arm64-gnu": "0.5.17", + "@libsql/linux-arm64-musl": "0.5.17", + "@libsql/linux-x64-gnu": "0.5.17", + "@libsql/linux-x64-musl": "0.5.17", + "@libsql/win32-x64-msvc": "0.5.17" + } + }, + "node_modules/libsql/node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/load-json-file": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", diff --git a/testing/javascript/package.json b/testing/javascript/package.json index a3a03da05..b4067e3ce 100644 --- a/testing/javascript/package.json +++ b/testing/javascript/package.json @@ -6,14 +6,16 @@ "test": "npm run test:turso && npm run test:serverless && npm run test:better-sqlite3", "test:turso": "PROVIDER=turso ava __test__/*.test.js", "test:serverless": "PROVIDER=serverless ava __test__/async.test.js", + "test:libsql": "PROVIDER=libsql ava __test__/*.test.js", "test:better-sqlite3": "PROVIDER=better-sqlite3 ava __test__/sync.test.js" }, "devDependencies": { "ava": "^5.3.0" }, "dependencies": { - "@tursodatabase/turso": "../../bindings/javascript", "@tursodatabase/serverless": "../../packages/turso-serverless", - "better-sqlite3": "^11.9.1" + "@tursodatabase/turso": "../../bindings/javascript", + "better-sqlite3": "^11.9.1", + "libsql": "^0.5.17" } } From 6c6d74993c48cbae3ff3dd1909849e056df1377a Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Tue, 29 Jul 2025 16:17:11 +0300 Subject: [PATCH 4/4] testing/javascript: Clean up after test runs --- testing/javascript/__test__/async.test.js | 26 +++++++++++++++++---- testing/javascript/__test__/sync.test.js | 28 ++++++++++++++++++----- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/testing/javascript/__test__/async.test.js b/testing/javascript/__test__/async.test.js index 9ae77cf56..2b6264d84 100644 --- a/testing/javascript/__test__/async.test.js +++ b/testing/javascript/__test__/async.test.js @@ -4,7 +4,7 @@ import fs from 'fs'; test.beforeEach(async (t) => { - const [db, errorType] = await connect(); + const [db, path,errorType] = await connect(); await db.exec(` DROP TABLE IF EXISTS users; CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT) @@ -17,14 +17,30 @@ test.beforeEach(async (t) => { ); t.context = { db, + path, errorType }; }); -test.after.always(async (t) => { +test.afterEach.always(async (t) => { + // Close the database connection if (t.context.db != undefined) { t.context.db.close(); } + // Remove the database file if it exists + if (t.context.path) { + const walPath = t.context.path + "-wal"; + const shmPath = t.context.path + "-shm"; + if (fs.existsSync(t.context.path)) { + fs.unlinkSync(t.context.path); + } + if (fs.existsSync(walPath)) { + fs.unlinkSync(walPath); + } + if (fs.existsSync(shmPath)) { + fs.unlinkSync(shmPath); + } + } }); test.serial("Open in-memory database", async (t) => { @@ -408,12 +424,12 @@ const connect = async (path, options = {}) => { if (provider === "turso") { const x = await import("@tursodatabase/turso"); const db = new x.default(path, options); - return [db, x.SqliteError]; + return [db, path, x.SqliteError]; } if (provider === "libsql") { const x = await import("libsql/promise"); const db = new x.default(path, options); - return [db, x.SqliteError, path]; + return [db, path, x.SqliteError, path]; } if (provider === "serverless") { const x = await import("@tursodatabase/serverless"); @@ -426,7 +442,7 @@ const connect = async (path, options = {}) => { url, authToken, }); - return [db, x.SqliteError]; + return [db, null, x.SqliteError]; } }; diff --git a/testing/javascript/__test__/sync.test.js b/testing/javascript/__test__/sync.test.js index bdcf6e90e..d71e37ba4 100644 --- a/testing/javascript/__test__/sync.test.js +++ b/testing/javascript/__test__/sync.test.js @@ -3,7 +3,7 @@ import crypto from 'crypto'; import fs from 'fs'; test.beforeEach(async (t) => { - const [db, errorType, provider] = await connect(); + const [db, path, provider, errorType] = await connect(); db.exec(` DROP TABLE IF EXISTS users; CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT) @@ -16,15 +16,31 @@ test.beforeEach(async (t) => { ); t.context = { db, + path, + provider, errorType, - provider }; }); -test.after.always(async (t) => { +test.afterEach.always(async (t) => { + // Close the database connection if (t.context.db != undefined) { t.context.db.close(); } + // Remove the database file if it exists + if (t.context.path) { + const walPath = t.context.path + "-wal"; + const shmPath = t.context.path + "-shm"; + if (fs.existsSync(t.context.path)) { + fs.unlinkSync(t.context.path); + } + if (fs.existsSync(walPath)) { + fs.unlinkSync(walPath); + } + if (fs.existsSync(shmPath)) { + fs.unlinkSync(shmPath); + } + } }); test.serial("Open in-memory database", async (t) => { @@ -451,17 +467,17 @@ const connect = async (path, options = {}) => { if (provider === "turso") { const x = await import("@tursodatabase/turso/sync"); const db = new x.default(path, options); - return [db, x.SqliteError, provider]; + return [db, path, provider, x.SqliteError]; } if (provider === "libsql") { const x = await import("libsql"); const db = new x.default(path, options); - return [db, x.SqliteError, provider, path]; + return [db, path, provider, x.SqliteError]; } if (provider == "better-sqlite3") { const x = await import("better-sqlite3"); const db = x.default(path, options); - return [db, x.default.SqliteError, provider]; + return [db, path, provider, x.default.SqliteError]; } throw new Error("Unknown provider: " + provider); };