From 974feac27bbe57d1d89a90da9b77981000fb517d Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Wed, 17 Sep 2025 21:38:36 +0400 Subject: [PATCH 01/14] move compute to the main thread for browser and node - now, most of the work is happening on the main thread - for database in browser, we still have dedicated WebWorker - but it is used only for OPFS access and only for that - for syn in browser we still offload sync operations to the WebWorker --- .../packages/browser-common/index.ts | 188 +++++++++++--- .../packages/browser/index-bundle.ts | 2 +- .../packages/browser/index-default.ts | 2 +- .../packages/browser/index-turbopack-hack.ts | 2 +- .../packages/browser/index-vite-dev-hack.ts | 4 +- .../packages/browser/promise-bundle.ts | 14 +- .../packages/browser/promise-default.ts | 14 +- .../browser/promise-turbopack-hack.ts | 14 +- .../packages/browser/promise-vite-dev-hack.ts | 14 +- .../packages/browser/promise.test.ts | 56 +++-- bindings/javascript/packages/common/compat.ts | 11 +- .../javascript/packages/common/promise.ts | 15 +- .../javascript/packages/native/index.d.ts | 20 -- bindings/javascript/perf/package-lock.json | 4 +- bindings/javascript/perf/perf-turso.js | 18 +- bindings/javascript/src/browser.rs | 238 ++++++++++++------ bindings/javascript/src/lib.rs | 87 +++---- .../sync/packages/native/index.d.ts | 38 ++- .../javascript/sync/packages/native/index.js | 5 +- 19 files changed, 471 insertions(+), 275 deletions(-) diff --git a/bindings/javascript/packages/browser-common/index.ts b/bindings/javascript/packages/browser-common/index.ts index dc7c334f7..828f49306 100644 --- a/bindings/javascript/packages/browser-common/index.ts +++ b/bindings/javascript/packages/browser-common/index.ts @@ -24,46 +24,94 @@ interface BrowserImports { is_web_worker(): boolean; lookup_file(ptr: number, len: number): number; read(handle: number, ptr: number, len: number, offset: number): number; + read_async(handle: number, ptr: number, len: number, offset: number, c: number); write(handle: number, ptr: number, len: number, offset: number): number; + write_async(handle: number, ptr: number, len: number, offset: number, c: number); sync(handle: number): number; + sync_async(handle: number, c: number); truncate(handle: number, len: number): number; + truncate_async(handle: number, len: number, c: number); size(handle: number): number; } -function panic(name): never { +function panicMain(name): never { + throw new Error(`method ${name} must be invoked only from the worker thread`); +} + +function panicWorker(name): never { throw new Error(`method ${name} must be invoked only from the main thread`); } -const MainDummyImports: BrowserImports = { - is_web_worker: function (): boolean { - return false; - }, - lookup_file: function (ptr: number, len: number): number { - panic("lookup_file") - }, - read: function (handle: number, ptr: number, len: number, offset: number): number { - panic("read") - }, - write: function (handle: number, ptr: number, len: number, offset: number): number { - panic("write") - }, - sync: function (handle: number): number { - panic("sync") - }, - truncate: function (handle: number, len: number): number { - panic("truncate") - }, - size: function (handle: number): number { - panic("size") - } +let completeOpfs: any = null; + +function mainImports(worker: Worker): BrowserImports { + return { + is_web_worker(): boolean { + return false; + }, + write_async(handle, ptr, len, offset, c) { + writeFileAtWorker(worker, handle, ptr, len, offset) + .then(result => { + completeOpfs(c, result); + }, err => { + console.error('write_async', err); + completeOpfs(c, -1); + }); + }, + sync_async(handle, c) { + syncFileAtWorker(worker, handle) + .then(result => { + completeOpfs(c, result); + }, err => { + console.error('sync_async', err); + completeOpfs(c, -1); + }); + }, + read_async(handle, ptr, len, offset, c) { + readFileAtWorker(worker, handle, ptr, len, offset) + .then(result => { + completeOpfs(c, result); + }, err => { + console.error('read_async', err); + completeOpfs(c, -1); + }); + }, + truncate_async(handle, len, c) { + truncateFileAtWorker(worker, handle, len) + .then(result => { + completeOpfs(c, result); + }, err => { + console.error('truncate_async', err); + completeOpfs(c, -1); + }); + }, + lookup_file(ptr, len): number { + panicMain("lookup_file") + }, + read(handle, ptr, len, offset): number { + panicMain("read") + }, + write(handle, ptr, len, offset): number { + panicMain("write") + }, + sync(handle): number { + panicMain("sync") + }, + truncate(handle, len): number { + panicMain("truncate") + }, + size(handle): number { + panicMain("size") + } + }; }; function workerImports(opfs: OpfsDirectory, memory: WebAssembly.Memory): BrowserImports { return { - is_web_worker: function (): boolean { + is_web_worker(): boolean { return true; }, - lookup_file: function (ptr: number, len: number): number { + lookup_file(ptr, len): number { try { const handle = opfs.lookupFileHandle(getStringFromMemory(memory, ptr, len)); return handle == null ? -404 : handle; @@ -71,29 +119,28 @@ function workerImports(opfs: OpfsDirectory, memory: WebAssembly.Memory): Browser return -1; } }, - read: function (handle: number, ptr: number, len: number, offset: number): number { + read(handle, ptr, len, offset): number { try { return opfs.read(handle, getUint8ArrayFromMemory(memory, ptr, len), offset); } catch (e) { return -1; } }, - write: function (handle: number, ptr: number, len: number, offset: number): number { + write(handle, ptr, len, offset): number { try { return opfs.write(handle, getUint8ArrayFromMemory(memory, ptr, len), offset) } catch (e) { return -1; } }, - sync: function (handle: number): number { + sync(handle): number { try { - opfs.sync(handle); - return 0; + return opfs.sync(handle); } catch (e) { return -1; } }, - truncate: function (handle: number, len: number): number { + truncate(handle, len): number { try { opfs.truncate(handle, len); return 0; @@ -101,13 +148,25 @@ function workerImports(opfs: OpfsDirectory, memory: WebAssembly.Memory): Browser return -1; } }, - size: function (handle: number): number { + size(handle): number { try { return opfs.size(handle); } catch (e) { return -1; } - } + }, + read_async(handle, ptr, len, offset, completion) { + panicWorker("read_async") + }, + write_async(handle, ptr, len, offset, completion) { + panicWorker("write_async") + }, + sync_async(handle, completion) { + panicWorker("sync_async") + }, + truncate_async(handle, len, c) { + panicWorker("truncate_async") + }, } } @@ -175,10 +234,11 @@ class OpfsDirectory { throw e; } } - sync(handle: number) { + sync(handle: number): number { try { const file = this.fileByHandle.get(handle); file.flush(); + return 0; } catch (e) { console.error('sync', handle, e); throw e; @@ -187,8 +247,8 @@ class OpfsDirectory { truncate(handle: number, size: number) { try { const file = this.fileByHandle.get(handle); - const result = file.truncate(size); - return result; + file.truncate(size); + return 0; } catch (e) { console.error('truncate', handle, size, e); throw e; @@ -214,7 +274,7 @@ function waitForWorkerResponse(worker: Worker, id: number): Promise { if (msg.data.error != null) { waitReject(msg.data.error) } else { - waitResolve() + waitResolve(msg.data.result) } cleanup(); } @@ -229,6 +289,38 @@ function waitForWorkerResponse(worker: Worker, id: number): Promise { return result; } +function readFileAtWorker(worker: Worker, handle: number, ptr: number, len: number, offset: number) { + workerRequestId += 1; + const currentId = workerRequestId; + const promise = waitForWorkerResponse(worker, currentId); + worker.postMessage({ __turso__: "read_async", handle: handle, ptr: ptr, len: len, offset: offset, id: currentId }); + return promise; +} + +function writeFileAtWorker(worker: Worker, handle: number, ptr: number, len: number, offset: number) { + workerRequestId += 1; + const currentId = workerRequestId; + const promise = waitForWorkerResponse(worker, currentId); + worker.postMessage({ __turso__: "write_async", handle: handle, ptr: ptr, len: len, offset: offset, id: currentId }); + return promise; +} + +function syncFileAtWorker(worker: Worker, handle: number) { + workerRequestId += 1; + const currentId = workerRequestId; + const promise = waitForWorkerResponse(worker, currentId); + worker.postMessage({ __turso__: "sync_async", handle: handle, id: currentId }); + return promise; +} + +function truncateFileAtWorker(worker: Worker, handle: number, len: number) { + workerRequestId += 1; + const currentId = workerRequestId; + const promise = waitForWorkerResponse(worker, currentId); + worker.postMessage({ __turso__: "truncate_async", handle: handle, len: len, id: currentId }); + return promise; +} + function registerFileAtWorker(worker: Worker, path: string): Promise { workerRequestId += 1; const currentId = workerRequestId; @@ -299,12 +391,25 @@ function setupWebWorker() { self.postMessage({ id: e.data.id, error: error }); } return; + } else if (e.data.__turso__ == 'read_async') { + let result = opfs.read(e.data.handle, getUint8ArrayFromMemory(memory, e.data.ptr, e.data.len), e.data.offset); + self.postMessage({ id: e.data.id, result: result }); + } else if (e.data.__turso__ == 'write_async') { + let result = opfs.write(e.data.handle, getUint8ArrayFromMemory(memory, e.data.ptr, e.data.len), e.data.offset); + self.postMessage({ id: e.data.id, result: result }); + } else if (e.data.__turso__ == 'sync_async') { + let result = opfs.sync(e.data.handle); + self.postMessage({ id: e.data.id, result: result }); + } else if (e.data.__turso__ == 'truncate_async') { + let result = opfs.truncate(e.data.handle, e.data.len); + self.postMessage({ id: e.data.id, result: result }); } handler.handle(e) } } async function setupMainThread(wasmFile: ArrayBuffer, factory: () => Worker): Promise { + const worker = factory(); const __emnapiContext = __emnapiGetDefaultContext() const __wasi = new __WASI({ version: 'preview1', @@ -322,13 +427,13 @@ async function setupMainThread(wasmFile: ArrayBuffer, factory: () => Worker): Pr context: __emnapiContext, asyncWorkPoolSize: 1, wasi: __wasi, - onCreateWorker() { return factory() }, + onCreateWorker() { return worker; }, overwriteImports(importObject) { importObject.env = { ...importObject.env, ...importObject.napi, ...importObject.emnapi, - ...MainDummyImports, + ...mainImports(worker), memory: __sharedMemory, } return importObject @@ -340,8 +445,9 @@ async function setupMainThread(wasmFile: ArrayBuffer, factory: () => Worker): Pr } } }, - }) + }); + completeOpfs = __napiModule.exports.completeOpfs; return __napiModule; } -export { OpfsDirectory, workerImports, MainDummyImports, waitForWorkerResponse, registerFileAtWorker, unregisterFileAtWorker, isWebWorker, setupWebWorker, setupMainThread } \ No newline at end of file +export { OpfsDirectory, workerImports, mainImports as MainDummyImports, waitForWorkerResponse, registerFileAtWorker, unregisterFileAtWorker, isWebWorker, setupWebWorker, setupMainThread } \ No newline at end of file diff --git a/bindings/javascript/packages/browser/index-bundle.ts b/bindings/javascript/packages/browser/index-bundle.ts index 2b74b8114..9aff00c28 100644 --- a/bindings/javascript/packages/browser/index-bundle.ts +++ b/bindings/javascript/packages/browser/index-bundle.ts @@ -20,5 +20,5 @@ 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 connect = napiModule.exports.connect +export const connectDbAsync = napiModule.exports.connectDbAsync export const initThreadPool = napiModule.exports.initThreadPool diff --git a/bindings/javascript/packages/browser/index-default.ts b/bindings/javascript/packages/browser/index-default.ts index 53c70e413..844e2c91b 100644 --- a/bindings/javascript/packages/browser/index-default.ts +++ b/bindings/javascript/packages/browser/index-default.ts @@ -18,5 +18,5 @@ 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 connect = napiModule.exports.connect +export const connectDbAsync = napiModule.exports.connectDbAsync export const initThreadPool = napiModule.exports.initThreadPool diff --git a/bindings/javascript/packages/browser/index-turbopack-hack.ts b/bindings/javascript/packages/browser/index-turbopack-hack.ts index f43d41624..8dc807f4a 100644 --- a/bindings/javascript/packages/browser/index-turbopack-hack.ts +++ b/bindings/javascript/packages/browser/index-turbopack-hack.ts @@ -21,5 +21,5 @@ 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 connect = napiModule.exports.connect +export const connectDbAsync = napiModule.exports.connectDbAsync export const initThreadPool = napiModule.exports.initThreadPool diff --git a/bindings/javascript/packages/browser/index-vite-dev-hack.ts b/bindings/javascript/packages/browser/index-vite-dev-hack.ts index 6f1d42c4a..3c36191de 100644 --- a/bindings/javascript/packages/browser/index-vite-dev-hack.ts +++ b/bindings/javascript/packages/browser/index-vite-dev-hack.ts @@ -7,7 +7,7 @@ let napiModule = { Opfs: {} as any, OpfsFile: {} as any, Statement: {} as any, - connect: {} as any, + connectDbAsync: {} as any, initThreadPool: {} as any, } }; @@ -37,5 +37,5 @@ 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 connect = napiModule.exports.connect +export const connectDbAsync = napiModule.exports.connectDbAsync export const initThreadPool = napiModule.exports.initThreadPool diff --git a/bindings/javascript/packages/browser/promise-bundle.ts b/bindings/javascript/packages/browser/promise-bundle.ts index fc28be689..103739e67 100644 --- a/bindings/javascript/packages/browser/promise-bundle.ts +++ b/bindings/javascript/packages/browser/promise-bundle.ts @@ -1,6 +1,6 @@ import { DatabaseOpts, SqliteError, } from "@tursodatabase/database-common" -import { connect as promiseConnect, Database } from "./promise.js"; -import { connect as nativeConnect, initThreadPool, MainWorker } from "./index-bundle.js"; +import { Database, connect as promiseConnect } from "./promise.js"; +import { initThreadPool, MainWorker, connectDbAsync } from "./index-bundle.js"; /** * Creates a new database connection asynchronously. @@ -10,13 +10,19 @@ import { connect as nativeConnect, initThreadPool, MainWorker } from "./index-bu * @returns {Promise} - A promise that resolves to a Database instance. */ async function connect(path: string, opts: DatabaseOpts = {}): Promise { - return await promiseConnect(path, opts, nativeConnect, async () => { + const init = async () => { await initThreadPool(); if (MainWorker == null) { throw new Error("panic: MainWorker is not initialized"); } return MainWorker; - }); + }; + return await promiseConnect( + path, + opts, + connectDbAsync, + init + ); } export { connect, Database, SqliteError } diff --git a/bindings/javascript/packages/browser/promise-default.ts b/bindings/javascript/packages/browser/promise-default.ts index a4dc99dfb..454ded33d 100644 --- a/bindings/javascript/packages/browser/promise-default.ts +++ b/bindings/javascript/packages/browser/promise-default.ts @@ -1,6 +1,6 @@ import { DatabaseOpts, SqliteError, } from "@tursodatabase/database-common" -import { connect as promiseConnect, Database } from "./promise.js"; -import { connect as nativeConnect, initThreadPool, MainWorker } from "./index-default.js"; +import { Database, connect as promiseConnect } from "./promise.js"; +import { initThreadPool, MainWorker, connectDbAsync } from "./index-default.js"; /** * Creates a new database connection asynchronously. @@ -10,13 +10,19 @@ import { connect as nativeConnect, initThreadPool, MainWorker } from "./index-de * @returns {Promise} - A promise that resolves to a Database instance. */ async function connect(path: string, opts: DatabaseOpts = {}): Promise { - return await promiseConnect(path, opts, nativeConnect, async () => { + const init = async () => { await initThreadPool(); if (MainWorker == null) { throw new Error("panic: MainWorker is not initialized"); } return MainWorker; - }); + }; + return await promiseConnect( + path, + opts, + connectDbAsync, + init + ); } export { connect, Database, SqliteError } diff --git a/bindings/javascript/packages/browser/promise-turbopack-hack.ts b/bindings/javascript/packages/browser/promise-turbopack-hack.ts index 359e79e40..b6b4bf09b 100644 --- a/bindings/javascript/packages/browser/promise-turbopack-hack.ts +++ b/bindings/javascript/packages/browser/promise-turbopack-hack.ts @@ -1,6 +1,6 @@ import { DatabaseOpts, SqliteError, } from "@tursodatabase/database-common" -import { connect as promiseConnect, Database } from "./promise.js"; -import { connect as nativeConnect, initThreadPool, MainWorker } from "./index-turbopack-hack.js"; +import { Database, connect as promiseConnect } from "./promise.js"; +import { initThreadPool, MainWorker, connectDbAsync } from "./index-turbopack-hack.js"; /** * Creates a new database connection asynchronously. @@ -10,13 +10,19 @@ import { connect as nativeConnect, initThreadPool, MainWorker } from "./index-tu * @returns {Promise} - A promise that resolves to a Database instance. */ async function connect(path: string, opts: DatabaseOpts = {}): Promise { - return await promiseConnect(path, opts, nativeConnect, async () => { + const init = async () => { await initThreadPool(); if (MainWorker == null) { throw new Error("panic: MainWorker is not initialized"); } return MainWorker; - }); + }; + return await promiseConnect( + path, + opts, + connectDbAsync, + init + ); } export { connect, Database, SqliteError } diff --git a/bindings/javascript/packages/browser/promise-vite-dev-hack.ts b/bindings/javascript/packages/browser/promise-vite-dev-hack.ts index 9e3e59e14..5b3c4acda 100644 --- a/bindings/javascript/packages/browser/promise-vite-dev-hack.ts +++ b/bindings/javascript/packages/browser/promise-vite-dev-hack.ts @@ -1,6 +1,6 @@ import { DatabaseOpts, SqliteError, } from "@tursodatabase/database-common" -import { connect as promiseConnect, Database } from "./promise.js"; -import { connect as nativeConnect, initThreadPool, MainWorker } from "./index-vite-dev-hack.js"; +import { Database, connect as promiseConnect } from "./promise.js"; +import { initThreadPool, MainWorker, connectDbAsync } from "./index-vite-dev-hack.js"; /** * Creates a new database connection asynchronously. @@ -10,13 +10,19 @@ import { connect as nativeConnect, initThreadPool, MainWorker } from "./index-vi * @returns {Promise} - A promise that resolves to a Database instance. */ async function connect(path: string, opts: DatabaseOpts = {}): Promise { - return await promiseConnect(path, opts, nativeConnect, async () => { + const init = async () => { await initThreadPool(); if (MainWorker == null) { throw new Error("panic: MainWorker is not initialized"); } return MainWorker; - }); + }; + return await promiseConnect( + path, + opts, + connectDbAsync, + init + ); } export { connect, Database, SqliteError } diff --git a/bindings/javascript/packages/browser/promise.test.ts b/bindings/javascript/packages/browser/promise.test.ts index 7e76ec029..741e77276 100644 --- a/bindings/javascript/packages/browser/promise.test.ts +++ b/bindings/javascript/packages/browser/promise.test.ts @@ -1,4 +1,4 @@ -import { expect, test, afterEach } from 'vitest' +import { expect, test } from 'vitest' import { connect } from './promise-default.js' test('in-memory db', async () => { @@ -10,6 +10,28 @@ test('in-memory db', async () => { expect(rows).toEqual([{ x: 1 }, { x: 3 }]); }) +test('on-disk db large inserts', async () => { + const path = `test-${(Math.random() * 10000) | 0}.db`; + const db1 = await connect(path); + await db1.prepare("CREATE TABLE t(x)").run(); + await db1.prepare("INSERT INTO t VALUES (randomblob(10 * 4096 + 0))").run(); + await db1.prepare("INSERT INTO t VALUES (randomblob(10 * 4096 + 1))").run(); + await db1.prepare("INSERT INTO t VALUES (randomblob(10 * 4096 + 2))").run(); + const stmt1 = db1.prepare("SELECT length(x) as l FROM t"); + expect(stmt1.columns()).toEqual([{ name: "l", column: null, database: null, table: null, type: null }]); + const rows1 = await stmt1.all(); + expect(rows1).toEqual([{ l: 10 * 4096 }, { l: 10 * 4096 + 1 }, { l: 10 * 4096 + 2 }]); + + await db1.exec("BEGIN"); + await db1.exec("INSERT INTO t VALUES (1)"); + await db1.exec("ROLLBACK"); + + const rows2 = await db1.prepare("SELECT length(x) as l FROM t").all(); + expect(rows2).toEqual([{ l: 10 * 4096 }, { l: 10 * 4096 + 1 }, { l: 10 * 4096 + 2 }]); + + await db1.prepare("PRAGMA wal_checkpoint(TRUNCATE)").run(); +}) + test('on-disk db', async () => { const path = `test-${(Math.random() * 10000) | 0}.db`; const db1 = await connect(path); @@ -19,8 +41,8 @@ test('on-disk db', async () => { 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(); stmt1.close(); + await db1.close(); const db2 = await connect(path); const stmt2 = db2.prepare("SELECT * FROM t WHERE x % 2 = ?"); @@ -30,23 +52,23 @@ test('on-disk db', async () => { 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)"); +// 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`); +// 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 }]); -}) +// 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:"); diff --git a/bindings/javascript/packages/common/compat.ts b/bindings/javascript/packages/common/compat.ts index d7bd493bb..be8c46d56 100644 --- a/bindings/javascript/packages/common/compat.ts +++ b/bindings/javascript/packages/common/compat.ts @@ -192,7 +192,12 @@ class Database { } try { - this.db.batchSync(sql); + let stmt = this.prepare(sql); + try { + stmt.run(); + } finally { + stmt.close(); + } } catch (err) { throw convertError(err); } @@ -408,6 +413,10 @@ class Statement { throw convertError(err); } } + + close() { + this.stmt.finalize(); + } } export { Database, Statement } \ No newline at end of file diff --git a/bindings/javascript/packages/common/promise.ts b/bindings/javascript/packages/common/promise.ts index e81795833..f1a22260c 100644 --- a/bindings/javascript/packages/common/promise.ts +++ b/bindings/javascript/packages/common/promise.ts @@ -196,7 +196,12 @@ class Database { } try { - await this.db.batchAsync(sql); + const stmt = this.prepare(sql); + try { + await stmt.run(); + } finally { + stmt.close(); + } } catch (err) { throw convertError(err); } @@ -298,7 +303,7 @@ class Statement { bindParams(this.stmt, bindParameters); while (true) { - const stepResult = await this.stmt.stepAsync(); + const stepResult = this.stmt.stepSync(); if (stepResult === STEP_IO) { await this.db.db.ioLoopAsync(); continue; @@ -328,7 +333,7 @@ class Statement { bindParams(this.stmt, bindParameters); while (true) { - const stepResult = await this.stmt.stepAsync(); + const stepResult = this.stmt.stepSync(); if (stepResult === STEP_IO) { await this.db.db.ioLoopAsync(); continue; @@ -352,7 +357,7 @@ class Statement { bindParams(this.stmt, bindParameters); while (true) { - const stepResult = await this.stmt.stepAsync(); + const stepResult = this.stmt.stepSync(); if (stepResult === STEP_IO) { await this.db.db.ioLoopAsync(); continue; @@ -377,7 +382,7 @@ class Statement { const rows: any[] = []; while (true) { - const stepResult = await this.stmt.stepAsync(); + const stepResult = this.stmt.stepSync(); if (stepResult === STEP_IO) { await this.db.db.ioLoopAsync(); continue; diff --git a/bindings/javascript/packages/native/index.d.ts b/bindings/javascript/packages/native/index.d.ts index 8654b88d2..cfd72609e 100644 --- a/bindings/javascript/packages/native/index.d.ts +++ b/bindings/javascript/packages/native/index.d.ts @@ -15,26 +15,6 @@ export declare class Database { get path(): string /** Returns whether the database connection is open. */ get open(): boolean - /** - * Executes a batch of SQL statements on main thread - * - * # Arguments - * - * * `sql` - The SQL statements to execute. - * - * # Returns - */ - 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 /** * Prepares a statement for execution. * diff --git a/bindings/javascript/perf/package-lock.json b/bindings/javascript/perf/package-lock.json index bf737b714..7ec8c6bdf 100644 --- a/bindings/javascript/perf/package-lock.json +++ b/bindings/javascript/perf/package-lock.json @@ -20,10 +20,10 @@ }, "../packages/native": { "name": "@tursodatabase/database", - "version": "0.1.5-pre.3", + "version": "0.2.0-pre.3", "license": "MIT", "dependencies": { - "@tursodatabase/database-common": "^0.1.5-pre.3" + "@tursodatabase/database-common": "^0.2.0-pre.3" }, "devDependencies": { "@napi-rs/cli": "^3.1.5", diff --git a/bindings/javascript/perf/perf-turso.js b/bindings/javascript/perf/perf-turso.js index 092730265..91c2b7d2a 100644 --- a/bindings/javascript/perf/perf-turso.js +++ b/bindings/javascript/perf/perf-turso.js @@ -1,26 +1,26 @@ import { run, bench, group, baseline } from 'mitata'; -import { Database } from '@tursodatabase/database/compat'; +import { Database } from '@tursodatabase/database'; const db = new Database(':memory:'); -db.exec("CREATE TABLE users (id INTEGER, name TEXT, email TEXT)"); -db.exec("INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.org')"); +await db.exec("CREATE TABLE users (id INTEGER, name TEXT, email TEXT)"); +await db.exec("INSERT INTO users (id, name, email) VALUES (1, 'Alice', 'alice@example.org')"); const stmtSelect = db.prepare("SELECT * FROM users WHERE id = ?"); const rawStmtSelect = db.prepare("SELECT * FROM users WHERE id = ?").raw(); const stmtInsert = db.prepare("INSERT INTO users (id, name, email) VALUES (?, ?, ?)"); -bench('Statement.get() with bind parameters [expanded]', () => { - stmtSelect.get(1); +bench('Statement.get() with bind parameters [expanded]', async () => { + await stmtSelect.get(1); }); -bench('Statement.get() with bind parameters [raw]', () => { - rawStmtSelect.get(1); +bench('Statement.get() with bind parameters [raw]', async () => { + await rawStmtSelect.get(1); }); -bench('Statement.run() with bind parameters', () => { - stmtInsert.run([1, 'foobar', 'foobar@example.com']); +bench('Statement.run() with bind parameters', async () => { + await stmtInsert.run([1, 'foobar', 'foobar@example.com']); }); await run({ diff --git a/bindings/javascript/src/browser.rs b/bindings/javascript/src/browser.rs index 92c818b4c..c59d86e7f 100644 --- a/bindings/javascript/src/browser.rs +++ b/bindings/javascript/src/browser.rs @@ -1,10 +1,10 @@ -use std::sync::Arc; +use std::{cell::RefCell, collections::HashMap, sync::Arc}; use napi::bindgen_prelude::*; use napi_derive::napi; -use turso_core::{storage::database::DatabaseFile, Clock, File, Instant, IO}; +use turso_core::{Clock, Completion, File, Instant, MemoryIO, IO}; -use crate::{init_tracing, is_memory, Database, DatabaseOpts}; +use crate::{is_memory, Database, DatabaseOpts}; pub struct NoopTask; @@ -29,11 +29,11 @@ pub fn init_thread_pool() -> napi::Result> { pub struct ConnectTask { path: String, io: Arc, + opts: Option, } pub struct ConnectResult { - db: Arc, - conn: Arc, + db: Database, } unsafe impl Send for ConnectResult {} @@ -43,73 +43,98 @@ impl Task for ConnectTask { type JsValue = Database; fn compute(&mut self) -> Result { - let file = self - .io - .open_file(&self.path, turso_core::OpenFlags::Create, false) - .map_err(|e| Error::new(Status::GenericFailure, format!("Failed to open file: {e}")))?; - - let db_file = Arc::new(DatabaseFile::new(file)); - let db = turso_core::Database::open(self.io.clone(), &self.path, db_file, false, true) - .map_err(|e| { - Error::new( - Status::GenericFailure, - format!("Failed to open database: {e}"), - ) - })?; - - let conn = db - .connect() - .map_err(|e| Error::new(Status::GenericFailure, format!("Failed to connect: {e}")))?; - - Ok(ConnectResult { db, conn }) + let db = Database::new_io(self.path.clone(), self.io.clone(), self.opts.clone())?; + Ok(ConnectResult { db }) } fn resolve(&mut self, _: Env, result: Self::Output) -> Result { - Ok(Database::create( - Some(result.db), - self.io.clone(), - result.conn, - self.path.clone(), - )) + Ok(result.db) } } -#[napi] -// we offload connect to the web-worker because: -// 1. browser main-thread do not support Atomic.wait operations -// 2. turso-db use blocking IO [io.wait_for_completion(c)] in few places during initialization path -// -// so, we offload connect to the worker thread -pub fn connect(path: String, opts: Option) -> Result> { - if let Some(opts) = opts { - init_tracing(opts.tracing); - } - let task = if is_memory(&path) { - ConnectTask { - io: Arc::new(turso_core::MemoryIO::new()), - path, - } - } else { - let io = Arc::new(Opfs::new()?); - ConnectTask { io, path } - }; - Ok(AsyncTask::new(task)) -} #[napi] #[derive(Clone)] -pub struct Opfs; +pub struct Opfs { + inner: Arc, +} + +pub struct OpfsInner { + completion_no: RefCell, + completions: RefCell>, +} + +thread_local! { + static OPFS: Arc = Arc::new(Opfs::new()); +} #[napi] #[derive(Clone)] struct OpfsFile { handle: i32, + opfs: Opfs, +} + +// unsafe impl Send for OpfsFile {} +// unsafe impl Sync for OpfsFile {} + +unsafe impl Send for Opfs {} +unsafe impl Sync for Opfs {} + +#[napi] +// we offload connect to the web-worker because +// turso-db use blocking IO [io.wait_for_completion(c)] in few places during initialization path +pub fn connect_db_async( + path: String, + opts: Option, +) -> Result> { + let io: Arc = if is_memory(&path) { + Arc::new(MemoryIO::new()) + } else { + // we must create OPFS IO on the main thread + opfs() + }; + let task = ConnectTask { path, io, opts }; + Ok(AsyncTask::new(task)) } #[napi] +pub fn complete_opfs(completion_no: u32, result: i32) { + OPFS.with(|opfs| opfs.complete(completion_no, result)) +} + +pub fn opfs() -> Arc { + OPFS.with(|opfs| opfs.clone()) +} + impl Opfs { - #[napi(constructor)] - pub fn new() -> napi::Result { - Ok(Self) + pub fn new() -> Self { + Self { + inner: Arc::new(OpfsInner { + completion_no: RefCell::new(0), + completions: RefCell::new(HashMap::new()), + }), + } + } + + pub fn complete(&self, completion_no: u32, result: i32) { + let completion = { + let mut completions = self.inner.completions.borrow_mut(); + completions.remove(&completion_no).unwrap() + }; + completion.complete(result); + } + + fn register_completion(&self, c: Completion) -> u32 { + let inner = &self.inner; + *inner.completion_no.borrow_mut() += 1; + let completion_no = *inner.completion_no.borrow(); + tracing::debug!( + "register completion: {} {:?}", + completion_no, + Arc::as_ptr(inner) + ); + inner.completions.borrow_mut().insert(completion_no, c); + completion_no } } @@ -127,6 +152,13 @@ extern "C" { fn sync(handle: i32) -> i32; fn truncate(handle: i32, length: usize) -> i32; fn size(handle: i32) -> i32; + + fn write_async(handle: i32, buffer: *const u8, buffer_len: usize, offset: i32, c: u32); + fn sync_async(handle: i32, c: u32); + fn read_async(handle: i32, buffer: *mut u8, buffer_len: usize, offset: i32, c: u32); + fn truncate_async(handle: i32, length: usize, c: u32); + // fn size_async(handle: i32) -> i32; + fn is_web_worker() -> bool; } @@ -144,7 +176,12 @@ impl IO for Opfs { tracing::info!("open_file: {}", path); let result = unsafe { lookup_file(path.as_ptr(), path.len()) }; if result >= 0 { - Ok(Arc::new(OpfsFile { handle: result })) + Ok(Arc::new(OpfsFile { + handle: result, + opfs: Opfs { + inner: self.inner.clone(), + }, + })) } else if result == -404 { Err(turso_core::LimboError::InternalError(format!( "unexpected path {path}: files must be created in advance for OPFS IO" @@ -175,17 +212,32 @@ impl File for OpfsFile { pos: u64, c: turso_core::Completion, ) -> turso_core::Result { - assert!( - is_web_worker_safe(), - "opfs must be used only from web worker for now" + let web_worker = is_web_worker_safe(); + tracing::debug!( + "pread({}, is_web_worker={}): pos={}", + self.handle, + web_worker, + pos ); - tracing::debug!("pread({}): pos={}", self.handle, pos); let handle = self.handle; let read_c = c.as_read(); let buffer = read_c.buf_arc(); let buffer = buffer.as_mut_slice(); - let result = unsafe { read(handle, buffer.as_mut_ptr(), buffer.len(), pos as i32) }; - c.complete(result as i32); + if web_worker { + let result = unsafe { read(handle, buffer.as_mut_ptr(), buffer.len(), pos as i32) }; + c.complete(result as i32); + } else { + let completion_no = self.opfs.register_completion(c.clone()); + unsafe { + read_async( + handle, + buffer.as_mut_ptr(), + buffer.len(), + pos as i32, + completion_no, + ) + }; + } Ok(c) } @@ -195,27 +247,44 @@ impl File for OpfsFile { buffer: Arc, c: turso_core::Completion, ) -> turso_core::Result { - assert!( - is_web_worker_safe(), - "opfs must be used only from web worker for now" + let web_worker = is_web_worker_safe(); + tracing::debug!( + "pwrite({}, is_web_worker={}): pos={}", + self.handle, + web_worker, + pos ); - tracing::debug!("pwrite({}): pos={}", self.handle, pos); let handle = self.handle; let buffer = buffer.as_slice(); - let result = unsafe { write(handle, buffer.as_ptr(), buffer.len(), pos as i32) }; - c.complete(result as i32); + if web_worker { + let result = unsafe { write(handle, buffer.as_ptr(), buffer.len(), pos as i32) }; + c.complete(result as i32); + } else { + let completion_no = self.opfs.register_completion(c.clone()); + unsafe { + write_async( + handle, + buffer.as_ptr(), + buffer.len(), + pos as i32, + completion_no, + ) + }; + } Ok(c) } fn sync(&self, c: turso_core::Completion) -> turso_core::Result { - assert!( - is_web_worker_safe(), - "opfs must be used only from web worker for now" - ); - tracing::debug!("sync({})", self.handle); + let web_worker = is_web_worker_safe(); + tracing::debug!("sync({}, is_web_worker={})", self.handle, web_worker); let handle = self.handle; - let result = unsafe { sync(handle) }; - c.complete(result as i32); + if web_worker { + let result = unsafe { sync(handle) }; + c.complete(result as i32); + } else { + let completion_no = self.opfs.register_completion(c.clone()); + unsafe { sync_async(handle, completion_no) }; + } Ok(c) } @@ -224,14 +293,21 @@ impl File for OpfsFile { len: u64, c: turso_core::Completion, ) -> turso_core::Result { - assert!( - is_web_worker_safe(), - "opfs must be used only from web worker for now" + let web_worker = is_web_worker_safe(); + tracing::debug!( + "truncate({}, is_web_worker={}): len={}", + self.handle, + web_worker, + len ); - tracing::debug!("truncate({}): len={}", self.handle, len); let handle = self.handle; - let result = unsafe { truncate(handle, len as usize) }; - c.complete(result as i32); + if web_worker { + let result = unsafe { truncate(handle, len as usize) }; + c.complete(result as i32); + } else { + let completion_no = self.opfs.register_completion(c.clone()); + unsafe { truncate_async(handle, len as usize, completion_no) }; + } Ok(c) } diff --git a/bindings/javascript/src/lib.rs b/bindings/javascript/src/lib.rs index d479f101d..3a9970680 100644 --- a/bindings/javascript/src/lib.rs +++ b/bindings/javascript/src/lib.rs @@ -10,8 +10,10 @@ //! - Iterating through query results //! - Managing the I/O event loop -#[cfg(feature = "browser")] +// #[cfg(feature = "browser")] pub mod browser; +// #[cfg(feature = "browser")] +use crate::browser::opfs; use napi::bindgen_prelude::*; use napi::{Env, Task}; @@ -76,10 +78,6 @@ pub(crate) fn init_tracing(level_filter: Option) { } pub enum DbTask { - Batch { - conn: Arc, - sql: String, - }, Step { stmt: Arc>>, }, @@ -93,10 +91,6 @@ impl Task for DbTask { fn compute(&mut self) -> Result { match self { - DbTask::Batch { conn, sql } => { - batch_sync(conn, sql)?; - Ok(0) - } DbTask::Step { stmt } => step_sync(stmt), } } @@ -107,20 +101,11 @@ impl Task for DbTask { } #[napi(object)] +#[derive(Clone)] pub struct DatabaseOpts { pub tracing: Option, } -fn batch_sync(conn: &Arc, sql: &str) -> napi::Result<()> { - conn.prepare_execute_batch(sql).map_err(|e| { - Error::new( - Status::GenericFailure, - format!("Failed to execute batch: {e}"), - ) - })?; - Ok(()) -} - fn step_sync(stmt: &Arc>>) -> napi::Result { let mut stmt_ref = stmt.borrow_mut(); let stmt = stmt_ref @@ -152,21 +137,38 @@ impl Database { /// # Arguments /// * `path` - The path to the database file. #[napi(constructor)] + pub fn new_napi(path: String, opts: Option) -> Result { + Self::new(path, opts) + } + pub fn new(path: String, opts: Option) -> Result { - if let Some(opts) = opts { - init_tracing(opts.tracing); - } let io: Arc = if is_memory(&path) { Arc::new(turso_core::MemoryIO::new()) } else { - Arc::new(turso_core::PlatformIO::new().map_err(|e| { - Error::new(Status::GenericFailure, format!("Failed to create IO: {e}")) - })?) + #[cfg(not(feature = "browser"))] + { + Arc::new(turso_core::PlatformIO::new().map_err(|e| { + Error::new(Status::GenericFailure, format!("Failed to create IO: {e}")) + })?) + } + #[cfg(feature = "browser")] + { + return Err(napi::Error::new( + napi::Status::GenericFailure, + "FS-backed db must be initialized through connectDbAsync function in the browser", + )); + } }; + Self::new_io(path, io, opts) + } - #[cfg(feature = "browser")] - if !is_memory(&path) { - return Err(Error::new(Status::GenericFailure, "sync constructor is not supported for FS-backed databases in the WASM. Use async connect(...) method instead".to_string())); + pub fn new_io( + path: String, + io: Arc, + opts: Option, + ) -> Result { + if let Some(opts) = opts { + init_tracing(opts.tracing); } let file = io @@ -233,33 +235,6 @@ impl Database { self.is_open.get() } - /// Executes a batch of SQL statements on main thread - /// - /// # Arguments - /// - /// * `sql` - The SQL statements to execute. - /// - /// # Returns - #[napi] - pub fn batch_sync(&self, sql: String) -> Result<()> { - batch_sync(&self.conn()?, &sql) - } - - /// Executes a batch of SQL statements outside of main thread - /// - /// # Arguments - /// - /// * `sql` - The SQL statements to execute. - /// - /// # Returns - #[napi(ts_return_type = "Promise")] - pub fn batch_async(&self, sql: String) -> Result> { - Ok(AsyncTask::new(DbTask::Batch { - conn: self.conn()?.clone(), - sql, - })) - } - /// Prepares a statement for execution. /// /// # Arguments @@ -325,8 +300,8 @@ impl Database { #[napi] pub fn close(&mut self) -> Result<()> { self.is_open.set(false); - let _ = self._db.take(); let _ = self.conn.take().unwrap(); + let _ = self._db.take(); Ok(()) } diff --git a/bindings/javascript/sync/packages/native/index.d.ts b/bindings/javascript/sync/packages/native/index.d.ts index 3ff5f0390..4ff3c2f91 100644 --- a/bindings/javascript/sync/packages/native/index.d.ts +++ b/bindings/javascript/sync/packages/native/index.d.ts @@ -15,26 +15,6 @@ export declare class Database { get path(): string /** Returns whether the database connection is open. */ get open(): boolean - /** - * Executes a batch of SQL statements on main thread - * - * # Arguments - * - * * `sql` - The SQL statements to execute. - * - * # Returns - */ - 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 /** * Prepares a statement for execution. * @@ -93,6 +73,16 @@ export declare class Database { ioLoopAsync(): Promise } +export declare class Opfs { + constructor() + connectDb(path: string, opts?: DatabaseOpts | undefined | null): Promise + complete(completionNo: number, result: number): void +} + +export declare class OpfsFile { + +} + /** A prepared statement. */ export declare class Statement { reset(): void @@ -149,6 +139,12 @@ export declare class Statement { export interface DatabaseOpts { tracing?: string } + +/** + * turso-db in the the browser requires explicit thread pool initialization + * so, we just put no-op task on the thread pool and force emnapi to allocate web worker + */ +export declare function initThreadPool(): Promise export declare class GeneratorHolder { resumeSync(error?: string | undefined | null): GeneratorResponse resumeAsync(error?: string | undefined | null): Promise @@ -220,7 +216,7 @@ export type DatabaseRowTransformResultJs = export type GeneratorResponse = | { type: 'IO' } | { type: 'Done' } - | { type: 'SyncEngineStats', operations: number, mainWal: number, revertWal: number, lastPullUnixTime: number, lastPushUnixTime?: number } + | { type: 'SyncEngineStats', operations: number, mainWal: number, revertWal: number, lastPullUnixTime: number, lastPushUnixTime?: number, revision?: string } export type JsProtocolRequest = | { type: 'Http', method: string, path: string, body?: Array, headers: Array<[string, string]> } diff --git a/bindings/javascript/sync/packages/native/index.js b/bindings/javascript/sync/packages/native/index.js index 709ca74e4..53bff489f 100644 --- a/bindings/javascript/sync/packages/native/index.js +++ b/bindings/javascript/sync/packages/native/index.js @@ -508,9 +508,12 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { Database, Statement, GeneratorHolder, JsDataCompletion, JsProtocolIo, JsProtocolRequestBytes, SyncEngine, DatabaseChangeTypeJs, SyncEngineProtocolVersion } = nativeBinding +const { Database, Opfs, OpfsFile, Statement, initThreadPool, GeneratorHolder, JsDataCompletion, JsProtocolIo, JsProtocolRequestBytes, SyncEngine, DatabaseChangeTypeJs, SyncEngineProtocolVersion } = nativeBinding export { Database } +export { Opfs } +export { OpfsFile } export { Statement } +export { initThreadPool } export { GeneratorHolder } export { JsDataCompletion } export { JsProtocolIo } From acc536d54276a5ff93e4b92ca2a9557ae347f272 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Wed, 17 Sep 2025 23:30:44 +0400 Subject: [PATCH 02/14] use global opfs IO in the sync --- bindings/javascript/sync/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/javascript/sync/src/lib.rs b/bindings/javascript/sync/src/lib.rs index e92603508..dd5e3f080 100644 --- a/bindings/javascript/sync/src/lib.rs +++ b/bindings/javascript/sync/src/lib.rs @@ -167,7 +167,7 @@ impl SyncEngine { } #[cfg(feature = "browser")] { - Arc::new(turso_node::browser::Opfs::new()?) + turso_node::browser::opfs() } }; Ok(SyncEngine { From b0f60a29baf94071fa36129152e3f2b1baf05ce8 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Tue, 16 Sep 2025 12:20:22 +0400 Subject: [PATCH 03/14] guard meta with mutex and make all methods to accept shared reference and offload locking of sync engine methods to the external wrapper --- sync/engine/src/database_sync_engine.rs | 50 ++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/sync/engine/src/database_sync_engine.rs b/sync/engine/src/database_sync_engine.rs index 7176d7ede..e98acb182 100644 --- a/sync/engine/src/database_sync_engine.rs +++ b/sync/engine/src/database_sync_engine.rs @@ -1,5 +1,4 @@ use std::{ - cell::RefCell, collections::{HashMap, HashSet}, sync::{Arc, Mutex}, }; @@ -51,7 +50,7 @@ pub struct DatabaseSyncEngine { meta_path: String, changes_file: Arc>>>, opts: DatabaseSyncEngineOpts, - meta: RefCell, + meta: Mutex, client_unique_id: String, } @@ -147,7 +146,7 @@ impl DatabaseSyncEngine

{ tracing::info!("initialize database tape connection: path={}", main_db_path); let main_tape = DatabaseTape::new_with_opts(main_db, tape_opts); let changes_file = io.open_file(&changes_path, OpenFlags::Create, false)?; - let mut db = Self { + let db = Self { io, protocol, db_file, @@ -158,7 +157,7 @@ impl DatabaseSyncEngine

{ meta_path: format!("{main_db_path}-info"), changes_file: Arc::new(Mutex::new(Some(changes_file))), opts, - meta: RefCell::new(meta.clone()), + meta: Mutex::new(meta.clone()), client_unique_id: meta.client_unique_id.clone(), }; @@ -176,7 +175,7 @@ impl DatabaseSyncEngine

{ Ok(db) } - fn open_revert_db_conn(&mut self) -> Result> { + fn open_revert_db_conn(&self) -> Result> { let db = turso_core::Database::open_with_flags_bypass_registry( self.io.clone(), &self.main_db_path, @@ -191,10 +190,7 @@ impl DatabaseSyncEngine

{ Ok(conn) } - async fn checkpoint_passive( - &mut self, - coro: &Coro, - ) -> Result<(Option>, u64)> { + async fn checkpoint_passive(&self, coro: &Coro) -> Result<(Option>, u64)> { let watermark = self.meta().revert_since_wal_watermark; tracing::info!( "checkpoint(path={:?}): revert_since_wal_watermark={}", @@ -273,9 +269,13 @@ impl DatabaseSyncEngine

{ }) } - pub async fn checkpoint(&mut self, coro: &Coro) -> Result<()> { + pub async fn checkpoint(&self, coro: &Coro) -> Result<()> { let (main_wal_salt, watermark) = self.checkpoint_passive(coro).await?; + tracing::info!( + "checkpoint(path={:?}): passive checkpoint is done", + self.main_db_path + ); let main_conn = connect_untracked(&self.main_tape)?; let revert_conn = self.open_revert_db_conn()?; @@ -419,10 +419,17 @@ impl DatabaseSyncEngine

{ /// This method will **not** send local changed to the remote /// This method will block writes for the period of pull pub async fn apply_changes_from_remote( - &mut self, + &self, coro: &Coro, remote_changes: DbChangesStatus, ) -> Result<()> { + if remote_changes.file_slot.is_none() { + self.update_meta(coro, |m| { + m.last_pull_unix_time = remote_changes.time.secs; + }) + .await?; + return Ok(()); + } assert!(remote_changes.file_slot.is_some(), "file_slot must be set"); let changes_file = remote_changes.file_slot.as_ref().unwrap().value.clone(); let pull_result = self.apply_changes_internal(coro, &changes_file).await; @@ -447,7 +454,7 @@ impl DatabaseSyncEngine

{ Ok(()) } async fn apply_changes_internal( - &mut self, + &self, coro: &Coro, changes_file: &Arc, ) -> Result { @@ -652,7 +659,7 @@ impl DatabaseSyncEngine

{ /// Sync local changes to remote DB and bring new changes from remote to local /// This method will block writes for the period of sync - pub async fn sync(&mut self, coro: &Coro) -> Result<()> { + pub async fn sync(&self, coro: &Coro) -> Result<()> { // todo(sivukhin): this is bit suboptimal as both 'push' and 'pull' will call pull_synced_from_remote // but for now - keep it simple self.push_changes_to_remote(coro).await?; @@ -660,21 +667,14 @@ impl DatabaseSyncEngine

{ Ok(()) } - pub async fn pull_changes_from_remote(&mut self, coro: &Coro) -> Result<()> { + pub async fn pull_changes_from_remote(&self, coro: &Coro) -> Result<()> { let changes = self.wait_changes_from_remote(coro).await?; - if changes.file_slot.is_some() { - self.apply_changes_from_remote(coro, changes).await?; - } else { - self.update_meta(coro, |m| { - m.last_pull_unix_time = changes.time.secs; - }) - .await?; - } + self.apply_changes_from_remote(coro, changes).await?; Ok(()) } - fn meta(&self) -> std::cell::Ref<'_, DatabaseMetadata> { - self.meta.borrow() + fn meta(&self) -> std::sync::MutexGuard<'_, DatabaseMetadata> { + self.meta.lock().unwrap() } async fn update_meta( @@ -688,7 +688,7 @@ impl DatabaseSyncEngine

{ let completion = self.protocol.full_write(&self.meta_path, meta.dump()?)?; // todo: what happen if we will actually update the metadata on disk but fail and so in memory state will not be updated wait_all_results(coro, &completion).await?; - self.meta.replace(meta); + *self.meta.lock().unwrap() = meta; Ok(()) } } From 160119b12ef0fa787fe48857c37f4d34e2c608e0 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Tue, 16 Sep 2025 12:20:59 +0400 Subject: [PATCH 04/14] propagate long poll timeout --- sync/engine/src/database_sync_engine.rs | 2 ++ sync/engine/src/database_sync_operations.rs | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sync/engine/src/database_sync_engine.rs b/sync/engine/src/database_sync_engine.rs index e98acb182..0257ca629 100644 --- a/sync/engine/src/database_sync_engine.rs +++ b/sync/engine/src/database_sync_engine.rs @@ -36,6 +36,7 @@ pub struct DatabaseSyncEngineOpts { pub tables_ignore: Vec, pub use_transform: bool, pub wal_pull_batch_size: u64, + pub long_poll_timeout: Option, pub protocol_version_hint: DatabaseSyncEngineProtocolVersion, } @@ -386,6 +387,7 @@ impl DatabaseSyncEngine

{ &file.value, &revision, self.opts.wal_pull_batch_size, + self.opts.long_poll_timeout, ) .await?; diff --git a/sync/engine/src/database_sync_operations.rs b/sync/engine/src/database_sync_operations.rs index daec39bc4..d82ace6a4 100644 --- a/sync/engine/src/database_sync_operations.rs +++ b/sync/engine/src/database_sync_operations.rs @@ -166,6 +166,7 @@ pub async fn wal_pull_to_file( frames_file: &Arc, revision: &DatabasePullRevision, wal_pull_batch_size: u64, + long_poll_timeout: Option, ) -> Result { // truncate file before pulling new data let c = Completion::new_trunc(move |result| { @@ -195,7 +196,7 @@ pub async fn wal_pull_to_file( .await } DatabasePullRevision::V1 { revision } => { - wal_pull_to_file_v1(coro, client, frames_file, revision).await + wal_pull_to_file_v1(coro, client, frames_file, revision, long_poll_timeout).await } } } @@ -206,6 +207,7 @@ pub async fn wal_pull_to_file_v1( client: &C, frames_file: &Arc, revision: &str, + long_poll_timeout: Option, ) -> Result { tracing::info!("wal_pull: revision={revision}"); let mut bytes = BytesMut::new(); @@ -214,7 +216,7 @@ pub async fn wal_pull_to_file_v1( encoding: PageUpdatesEncodingReq::Raw as i32, server_revision: String::new(), client_revision: revision.to_string(), - long_poll_timeout_ms: 0, + long_poll_timeout_ms: long_poll_timeout.map(|x| x.as_secs() as u32).unwrap_or(0), server_pages: BytesMut::new().into(), client_pages: BytesMut::new().into(), }; From 83303b8c5b2e8121f07061733e977d477554f131 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Tue, 16 Sep 2025 12:21:17 +0400 Subject: [PATCH 05/14] properly guard access to the sync engine with locks --- bindings/javascript/sync/src/lib.rs | 116 +++++++++++++++++----------- 1 file changed, 72 insertions(+), 44 deletions(-) diff --git a/bindings/javascript/sync/src/lib.rs b/bindings/javascript/sync/src/lib.rs index dd5e3f080..c70932081 100644 --- a/bindings/javascript/sync/src/lib.rs +++ b/bindings/javascript/sync/src/lib.rs @@ -6,7 +6,7 @@ pub mod js_protocol_io; use std::{ collections::HashMap, - sync::{Arc, Mutex, OnceLock, RwLock, RwLockReadGuard, RwLockWriteGuard}, + sync::{Arc, Mutex, MutexGuard, OnceLock, RwLock, RwLockReadGuard}, }; use napi::bindgen_prelude::{AsyncTask, Either5, Null}; @@ -28,17 +28,50 @@ pub struct DatabaseOpts { pub path: String, } +pub struct SyncEngineGuard { + inner: Arc>>>, + wait_lock: Mutex<()>, + push_lock: Mutex<()>, + pull_lock: Mutex<()>, + checkpoint_lock: Mutex<()>, +} + +impl SyncEngineGuard { + fn checkpoint_lock(&self) -> (MutexGuard<'_, ()>, MutexGuard<'_, ()>, MutexGuard<'_, ()>) { + let push = self.push_lock.lock().unwrap(); + let pull = self.pull_lock.lock().unwrap(); + let checkpoint = self.checkpoint_lock.lock().unwrap(); + (push, pull, checkpoint) + } + fn pull_lock(&self) -> (MutexGuard<'_, ()>, MutexGuard<'_, ()>, MutexGuard<'_, ()>) { + let wait = self.wait_lock.lock().unwrap(); + let push = self.push_lock.lock().unwrap(); + let pull = self.pull_lock.lock().unwrap(); + (wait, push, pull) + } + fn push_lock(&self) -> MutexGuard<'_, ()> { + let push = self.push_lock.lock().unwrap(); + push + } + fn wait_lock(&self) -> (MutexGuard<'_, ()>, MutexGuard<'_, ()>) { + let wait = self.wait_lock.lock().unwrap(); + let pull = self.pull_lock.lock().unwrap(); + (wait, pull) + } +} + #[napi] pub struct SyncEngine { path: String, client_name: String, wal_pull_batch_size: u32, + long_poll_timeout: Option, protocol_version: DatabaseSyncEngineProtocolVersion, tables_ignore: Vec, use_transform: bool, io: Option>, protocol: Option>, - sync_engine: Arc>>>, + sync_engine: Arc, opened: Arc>>, } @@ -123,6 +156,7 @@ pub struct SyncEngineOpts { pub path: String, pub client_name: Option, pub wal_pull_batch_size: Option, + pub long_poll_timeout_ms: Option, pub tracing: Option, pub tables_ignore: Option>, pub use_transform: bool, @@ -174,10 +208,19 @@ impl SyncEngine { path: opts.path, client_name: opts.client_name.unwrap_or("turso-sync-js".to_string()), wal_pull_batch_size: opts.wal_pull_batch_size.unwrap_or(100), + long_poll_timeout: opts + .long_poll_timeout_ms + .map(|x| std::time::Duration::from_millis(x as u64)), tables_ignore: opts.tables_ignore.unwrap_or_default(), use_transform: opts.use_transform, #[allow(clippy::arc_with_non_send_sync)] - sync_engine: Arc::new(RwLock::new(None)), + sync_engine: Arc::new(SyncEngineGuard { + inner: Arc::new(RwLock::new(None)), + wait_lock: Mutex::new(()), + push_lock: Mutex::new(()), + pull_lock: Mutex::new(()), + checkpoint_lock: Mutex::new(()), + }), io: Some(io), protocol: Some(Arc::new(JsProtocolIo::default())), #[allow(clippy::arc_with_non_send_sync)] @@ -196,6 +239,7 @@ impl SyncEngine { let opts = DatabaseSyncEngineOpts { client_name: self.client_name.clone(), wal_pull_batch_size: self.wal_pull_batch_size as u64, + long_poll_timeout: self.long_poll_timeout, tables_ignore: self.tables_ignore.clone(), use_transform: self.use_transform, protocol_version_hint: self.protocol_version, @@ -213,7 +257,7 @@ impl SyncEngine { let connection = initialized.connect_rw(&coro).await?; let db = turso_node::Database::create(None, io.clone(), connection, path); - *sync_engine.write().unwrap() = Some(initialized); + *sync_engine.inner.write().unwrap() = Some(initialized); *opened.lock().unwrap() = Some(db); Ok(()) }); @@ -246,9 +290,10 @@ impl SyncEngine { #[napi] pub fn sync(&self) -> GeneratorHolder { - self.run(async move |coro, sync_engine| { - let mut sync_engine = try_write(sync_engine)?; - let sync_engine = try_unwrap_mut(&mut sync_engine)?; + self.run(async move |coro, guard| { + let _lock = guard.pull_lock(); + let sync_engine = try_read(&guard.inner)?; + let sync_engine = try_unwrap(&sync_engine)?; sync_engine.sync(coro).await?; Ok(None) }) @@ -256,8 +301,9 @@ impl SyncEngine { #[napi] pub fn push(&self) -> GeneratorHolder { - self.run(async move |coro, sync_engine| { - let sync_engine = try_read(sync_engine)?; + self.run(async move |coro, guard| { + let _lock = guard.push_lock(); + let sync_engine = try_read(&guard.inner)?; let sync_engine = try_unwrap(&sync_engine)?; sync_engine.push_changes_to_remote(coro).await?; Ok(None) @@ -266,8 +312,8 @@ impl SyncEngine { #[napi] pub fn stats(&self) -> GeneratorHolder { - self.run(async move |coro, sync_engine| { - let sync_engine = try_read(sync_engine)?; + self.run(async move |coro, guard| { + let sync_engine = try_read(&guard.inner)?; let sync_engine = try_unwrap(&sync_engine)?; let stats = sync_engine.stats(coro).await?; Ok(Some(GeneratorResponse::SyncEngineStats { @@ -283,19 +329,25 @@ impl SyncEngine { #[napi] pub fn pull(&self) -> GeneratorHolder { - self.run(async move |coro, sync_engine| { - let mut sync_engine = try_write(sync_engine)?; - let sync_engine = try_unwrap_mut(&mut sync_engine)?; - sync_engine.pull_changes_from_remote(coro).await?; + self.run(async move |coro, guard| { + let sync_engine = try_read(&guard.inner)?; + let sync_engine = try_unwrap(&sync_engine)?; + let changes = { + let _lock = guard.wait_lock(); + sync_engine.wait_changes_from_remote(coro).await? + }; + let _lock = guard.pull_lock(); + sync_engine.apply_changes_from_remote(coro, changes).await?; Ok(None) }) } #[napi] pub fn checkpoint(&self) -> GeneratorHolder { - self.run(async move |coro, sync_engine| { - let mut sync_engine = try_write(sync_engine)?; - let sync_engine = try_unwrap_mut(&mut sync_engine)?; + self.run(async move |coro, guard| { + let _lock = guard.checkpoint_lock(); + let sync_engine = try_read(&guard.inner)?; + let sync_engine = try_unwrap(&sync_engine)?; sync_engine.checkpoint(coro).await?; Ok(None) }) @@ -315,7 +367,7 @@ impl SyncEngine { #[napi] pub fn close(&mut self) { - let _ = self.sync_engine.write().unwrap().take(); + let _ = self.sync_engine.inner.write().unwrap().take(); let _ = self.opened.lock().unwrap().take().unwrap(); let _ = self.io.take(); let _ = self.protocol.take(); @@ -344,7 +396,7 @@ impl SyncEngine { &self, f: impl AsyncFnOnce( &Coro<()>, - &Arc>>>, + &Arc, ) -> turso_sync_engine::Result> + 'static, ) -> GeneratorHolder { @@ -378,18 +430,6 @@ fn try_read( Ok(sync_engine) } -fn try_write( - sync_engine: &RwLock>>, -) -> turso_sync_engine::Result>>> { - let Ok(sync_engine) = sync_engine.try_write() else { - let nasty_error = "sync_engine is busy".to_string(); - return Err(turso_sync_engine::errors::Error::DatabaseSyncEngineError( - nasty_error, - )); - }; - Ok(sync_engine) -} - fn try_unwrap<'a>( sync_engine: &'a RwLockReadGuard<'_, Option>>, ) -> turso_sync_engine::Result<&'a DatabaseSyncEngine> { @@ -401,15 +441,3 @@ fn try_unwrap<'a>( }; Ok(sync_engine) } - -fn try_unwrap_mut<'a>( - sync_engine: &'a mut RwLockWriteGuard<'_, Option>>, -) -> turso_sync_engine::Result<&'a mut DatabaseSyncEngine> { - let Some(sync_engine) = sync_engine.as_mut() else { - let error = "sync_engine must be initialized".to_string(); - return Err(turso_sync_engine::errors::Error::DatabaseSyncEngineError( - error, - )); - }; - Ok(sync_engine) -} From e68b642f4f836a731945c7563050b4c3a12f1360 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Tue, 16 Sep 2025 12:22:02 +0400 Subject: [PATCH 06/14] set longPollTimeoutMs from js bindings --- bindings/javascript/sync/packages/common/types.ts | 1 + bindings/javascript/sync/packages/native/index.d.ts | 1 + bindings/javascript/sync/packages/native/promise.ts | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/bindings/javascript/sync/packages/common/types.ts b/bindings/javascript/sync/packages/common/types.ts index 49b140103..27006de8d 100644 --- a/bindings/javascript/sync/packages/common/types.ts +++ b/bindings/javascript/sync/packages/common/types.ts @@ -36,6 +36,7 @@ export interface SyncOpts { encryptionKey?: string; tablesIgnore?: string[], transform?: Transform, + longPollTimeoutMs?: number, tracing?: string, } diff --git a/bindings/javascript/sync/packages/native/index.d.ts b/bindings/javascript/sync/packages/native/index.d.ts index 4ff3c2f91..02ff2c385 100644 --- a/bindings/javascript/sync/packages/native/index.d.ts +++ b/bindings/javascript/sync/packages/native/index.d.ts @@ -228,6 +228,7 @@ export interface SyncEngineOpts { path: string clientName?: string walPullBatchSize?: number + longPollTimeoutMs?: number tracing?: string tablesIgnore?: Array useTransform: boolean diff --git a/bindings/javascript/sync/packages/native/promise.ts b/bindings/javascript/sync/packages/native/promise.ts index 3d473c8a9..2bff31d29 100644 --- a/bindings/javascript/sync/packages/native/promise.ts +++ b/bindings/javascript/sync/packages/native/promise.ts @@ -83,7 +83,8 @@ async function connect(opts: SyncOpts): Promise { tablesIgnore: opts.tablesIgnore, useTransform: opts.transform != null, tracing: opts.tracing, - protocolVersion: 1 + longPollTimeoutMs: opts.longPollTimeoutMs, + protocolVersion: 1, }); const runOpts: RunOpts = { url: opts.url, From 1185298670d6bfd4b2e05019b792798daa9be44c Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Wed, 17 Sep 2025 10:37:26 +0400 Subject: [PATCH 07/14] fix replay generator --- sync/engine/src/database_replay_generator.rs | 9 ++++----- sync/engine/src/database_sync_operations.rs | 13 ++++++------- sync/engine/src/database_tape.rs | 8 ++++---- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/sync/engine/src/database_replay_generator.rs b/sync/engine/src/database_replay_generator.rs index daa3b9f0f..02532d825 100644 --- a/sync/engine/src/database_replay_generator.rs +++ b/sync/engine/src/database_replay_generator.rs @@ -264,13 +264,12 @@ impl DatabaseReplayGenerator { let update = self.update_query(coro, table_name, &columns).await?; Ok(update) } else { - let columns = [true].repeat(after.len()); - let update = self.update_query(coro, table_name, &columns).await?; - Ok(update) + let upsert = self.upsert_query(coro, table_name, after.len()).await?; + Ok(upsert) } } DatabaseTapeRowChangeType::Insert { after } => { - let insert = self.insert_query(coro, table_name, after.len()).await?; + let insert = self.upsert_query(coro, table_name, after.len()).await?; Ok(insert) } } @@ -320,7 +319,7 @@ impl DatabaseReplayGenerator { is_ddl_replay: false, }) } - pub(crate) async fn insert_query( + pub(crate) async fn upsert_query( &self, coro: &Coro, table_name: &str, diff --git a/sync/engine/src/database_sync_operations.rs b/sync/engine/src/database_sync_operations.rs index d82ace6a4..5197e04e0 100644 --- a/sync/engine/src/database_sync_operations.rs +++ b/sync/engine/src/database_sync_operations.rs @@ -216,7 +216,7 @@ pub async fn wal_pull_to_file_v1( encoding: PageUpdatesEncodingReq::Raw as i32, server_revision: String::new(), client_revision: revision.to_string(), - long_poll_timeout_ms: long_poll_timeout.map(|x| x.as_secs() as u32).unwrap_or(0), + long_poll_timeout_ms: long_poll_timeout.map(|x| x.as_millis() as u32).unwrap_or(0), server_pages: BytesMut::new().into(), client_pages: BytesMut::new().into(), }; @@ -807,12 +807,11 @@ pub async fn push_logical_changes( }), DatabaseTapeOperation::RowChange(change) => { let replay_info = generator.replay_info(coro, &change).await?; - let change_type = (&change.change).into(); match change.change { DatabaseTapeRowChangeType::Delete { before } => { let values = generator.replay_values( &replay_info, - change_type, + replay_info.change_type, change.id, before, None, @@ -829,7 +828,7 @@ pub async fn push_logical_changes( DatabaseTapeRowChangeType::Insert { after } => { let values = generator.replay_values( &replay_info, - change_type, + replay_info.change_type, change.id, after, None, @@ -850,7 +849,7 @@ pub async fn push_logical_changes( } => { let values = generator.replay_values( &replay_info, - change_type, + replay_info.change_type, change.id, after, Some(updates), @@ -871,7 +870,7 @@ pub async fn push_logical_changes( } => { let values = generator.replay_values( &replay_info, - change_type, + replay_info.change_type, change.id, after, None, @@ -1361,7 +1360,7 @@ pub async fn wait_proto_message( Error::DatabaseSyncEngineError(format!("unable to deserialize protobuf message: {e}")) })?; let _ = bytes.split_to(message_length + prefix_length); - tracing::debug!( + tracing::trace!( "wait_proto_message: elapsed={:?}", std::time::Instant::now().duration_since(start_time) ); diff --git a/sync/engine/src/database_tape.rs b/sync/engine/src/database_tape.rs index b98cd0847..b8dfdb820 100644 --- a/sync/engine/src/database_tape.rs +++ b/sync/engine/src/database_tape.rs @@ -10,7 +10,7 @@ use crate::{ database_sync_operations::WAL_FRAME_HEADER, errors::Error, types::{ - Coro, DatabaseChange, DatabaseTapeOperation, DatabaseTapeRowChange, + Coro, DatabaseChange, DatabaseChangeType, DatabaseTapeOperation, DatabaseTapeRowChange, DatabaseTapeRowChangeType, ProtocolCommand, }, wal_session::WalSession, @@ -584,7 +584,7 @@ impl DatabaseReplaySession { cached.stmt.reset(); let values = self.generator.replay_values( &cached.info, - change_type, + DatabaseChangeType::Delete, change.id, before, None, @@ -600,7 +600,7 @@ impl DatabaseReplaySession { cached.stmt.reset(); let values = self.generator.replay_values( &cached.info, - change_type, + DatabaseChangeType::Insert, change.id, after, None, @@ -643,7 +643,7 @@ impl DatabaseReplaySession { table, columns ); - let info = self.generator.insert_query(coro, table, columns).await?; + let info = self.generator.upsert_query(coro, table, columns).await?; let stmt = self.conn.prepare(&info.query)?; self.cached_insert_stmt .insert(key.clone(), CachedStmt { stmt, info }); From 66de28d84b92757373ff974e9df0fb9282d33eb6 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Wed, 17 Sep 2025 10:38:05 +0400 Subject: [PATCH 08/14] wip --- Cargo.lock | 1 + .../examples/browser/package-lock.json | 335 +++ .../examples/drizzle/package-lock.json | 626 ++++++ bindings/javascript/package-lock.json | 1934 ++++++++++++++++- .../packages/browser/promise.test.ts | 1 + .../javascript/packages/common/async-lock.ts | 29 + bindings/javascript/packages/common/index.ts | 3 +- .../javascript/packages/common/promise.ts | 154 +- .../javascript/packages/native/package.json | 3 + .../packages/native/promise.test.ts | 20 + bindings/javascript/sync/Cargo.toml | 3 +- bindings/javascript/sync/packages/browser/a | Bin 0 -> 57344 bytes .../javascript/sync/packages/browser/a-shm | Bin 0 -> 32768 bytes .../sync/packages/browser/package.json | 2 +- .../sync/packages/browser/promise.test.ts | 99 + .../sync/packages/browser/promise.ts | 17 +- .../javascript/sync/packages/common/index.ts | 4 +- .../sync/packages/common/package.json | 3 + .../javascript/sync/packages/common/run.ts | 68 + .../sync/packages/common/tsconfig.json | 3 +- .../javascript/sync/packages/common/types.ts | 2 +- .../sync/packages/native/index.d.ts | 9 +- .../javascript/sync/packages/native/index.js | 3 +- bindings/javascript/sync/packages/native/log | 14 + .../sync/packages/native/package.json | 2 +- .../sync/packages/native/promise.test.ts | 115 +- .../sync/packages/native/promise.ts | 16 +- bindings/javascript/sync/src/generator.rs | 12 +- bindings/javascript/sync/src/lib.rs | 94 +- bindings/javascript/yarn.lock | 793 ++++++- 30 files changed, 4175 insertions(+), 190 deletions(-) create mode 100644 bindings/javascript/examples/browser/package-lock.json create mode 100644 bindings/javascript/examples/drizzle/package-lock.json create mode 100644 bindings/javascript/packages/common/async-lock.ts create mode 100755 bindings/javascript/sync/packages/browser/a create mode 100755 bindings/javascript/sync/packages/browser/a-shm create mode 100644 bindings/javascript/sync/packages/native/log diff --git a/Cargo.lock b/Cargo.lock index 9e2bb330d..f42165992 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4326,6 +4326,7 @@ dependencies = [ "napi", "napi-build", "napi-derive", + "tracing", "tracing-subscriber", "turso_core", "turso_node", diff --git a/bindings/javascript/examples/browser/package-lock.json b/bindings/javascript/examples/browser/package-lock.json new file mode 100644 index 000000000..24eade16e --- /dev/null +++ b/bindings/javascript/examples/browser/package-lock.json @@ -0,0 +1,335 @@ +{ + "name": "wasm", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "wasm", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@tursodatabase/database-browser": "../../packages/browser" + }, + "devDependencies": { + "vite": "^7.1.4" + } + }, + "../../packages/browser": { + "name": "@tursodatabase/database-browser", + "version": "0.1.5", + "license": "MIT", + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.3", + "@tursodatabase/database-browser-common": "^0.1.5", + "@tursodatabase/database-common": "^0.1.5" + }, + "devDependencies": { + "@napi-rs/cli": "^3.1.5", + "@vitest/browser": "^3.2.4", + "playwright": "^1.55.0", + "typescript": "^5.9.2", + "vitest": "^3.2.4" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.50.0", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.50.0", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@tursodatabase/database-browser": { + "resolved": "../../packages/browser", + "link": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.9", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.50.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.50.0", + "@rollup/rollup-android-arm64": "4.50.0", + "@rollup/rollup-darwin-arm64": "4.50.0", + "@rollup/rollup-darwin-x64": "4.50.0", + "@rollup/rollup-freebsd-arm64": "4.50.0", + "@rollup/rollup-freebsd-x64": "4.50.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.0", + "@rollup/rollup-linux-arm-musleabihf": "4.50.0", + "@rollup/rollup-linux-arm64-gnu": "4.50.0", + "@rollup/rollup-linux-arm64-musl": "4.50.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.50.0", + "@rollup/rollup-linux-ppc64-gnu": "4.50.0", + "@rollup/rollup-linux-riscv64-gnu": "4.50.0", + "@rollup/rollup-linux-riscv64-musl": "4.50.0", + "@rollup/rollup-linux-s390x-gnu": "4.50.0", + "@rollup/rollup-linux-x64-gnu": "4.50.0", + "@rollup/rollup-linux-x64-musl": "4.50.0", + "@rollup/rollup-openharmony-arm64": "4.50.0", + "@rollup/rollup-win32-arm64-msvc": "4.50.0", + "@rollup/rollup-win32-ia32-msvc": "4.50.0", + "@rollup/rollup-win32-x64-msvc": "4.50.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vite": { + "version": "7.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/bindings/javascript/examples/drizzle/package-lock.json b/bindings/javascript/examples/drizzle/package-lock.json new file mode 100644 index 000000000..7867fc918 --- /dev/null +++ b/bindings/javascript/examples/drizzle/package-lock.json @@ -0,0 +1,626 @@ +{ + "name": "drizzle", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "drizzle", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@tursodatabase/database": "../../packages/native", + "better-sqlite3": "^12.2.0", + "drizzle-orm": "^0.44.3" + } + }, + "../..": { + "version": "0.1.5", + "workspaces": [ + "packages/common", + "packages/native", + "packages/browser", + "packages/browser-common", + "sync/packages/common", + "sync/packages/native", + "sync/packages/browser" + ] + }, + "../../packages/browser": { + "name": "@tursodatabase/database-browser", + "version": "0.1.5", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.3", + "@tursodatabase/database-browser-common": "^0.1.5", + "@tursodatabase/database-common": "^0.1.5" + }, + "devDependencies": { + "@napi-rs/cli": "^3.1.5", + "@vitest/browser": "^3.2.4", + "playwright": "^1.55.0", + "typescript": "^5.9.2", + "vitest": "^3.2.4" + } + }, + "../../packages/browser-common": { + "name": "@tursodatabase/database-browser-common", + "version": "0.1.5", + "extraneous": true, + "license": "MIT", + "devDependencies": { + "typescript": "^5.9.2" + } + }, + "../../packages/common": { + "name": "@tursodatabase/database-common", + "version": "0.1.5", + "extraneous": true, + "license": "MIT", + "devDependencies": { + "typescript": "^5.9.2" + } + }, + "../../packages/native": { + "name": "@tursodatabase/database", + "version": "0.1.5", + "license": "MIT", + "dependencies": { + "@tursodatabase/database-common": "^0.1.5" + }, + "devDependencies": { + "@napi-rs/cli": "^3.1.5", + "@types/node": "^24.3.1", + "typescript": "^5.9.2", + "vitest": "^3.2.4" + } + }, + "../../sync/packages/browser": { + "name": "@tursodatabase/sync-browser", + "version": "0.1.5", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.3", + "@tursodatabase/database-browser-common": "^0.1.5", + "@tursodatabase/database-common": "^0.1.5", + "@tursodatabase/sync-common": "^0.1.5" + }, + "devDependencies": { + "@napi-rs/cli": "^3.1.5", + "@vitest/browser": "^3.2.4", + "playwright": "^1.55.0", + "typescript": "^5.9.2", + "vitest": "^3.2.4" + } + }, + "../../sync/packages/common": { + "name": "@tursodatabase/sync-common", + "version": "0.1.5", + "extraneous": true, + "license": "MIT", + "devDependencies": { + "typescript": "^5.9.2" + } + }, + "../../sync/packages/native": { + "name": "@tursodatabase/sync", + "version": "0.1.5", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@tursodatabase/database-common": "^0.1.5", + "@tursodatabase/sync-common": "^0.1.5" + }, + "devDependencies": { + "@napi-rs/cli": "^3.1.5", + "@types/node": "^24.3.1", + "typescript": "^5.9.2", + "vitest": "^3.2.4" + } + }, + "node_modules/@tursodatabase/database": { + "resolved": "../../packages/native", + "link": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/better-sqlite3": { + "version": "12.2.0", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "license": "ISC" + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/drizzle-orm": { + "version": "0.44.4", + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=4", + "@electric-sql/pglite": ">=0.2.0", + "@libsql/client": ">=0.10.0", + "@libsql/client-wasm": ">=0.10.0", + "@neondatabase/serverless": ">=0.10.0", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1.13", + "@prisma/client": "*", + "@tidbcloud/serverless": "*", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/sql.js": "*", + "@upstash/redis": ">=1.34.7", + "@vercel/postgres": ">=0.8.0", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=14.0.0", + "gel": ">=2", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@libsql/client-wasm": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@prisma/client": { + "optional": true + }, + "@tidbcloud/serverless": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "gel": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "prisma": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "license": "MIT" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "license": "ISC" + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "license": "MIT" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.75.0", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/once": { + "version": "1.4.0", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar-fs": { + "version": "2.1.3", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "license": "ISC" + } + } +} diff --git a/bindings/javascript/package-lock.json b/bindings/javascript/package-lock.json index e0bc3a671..3df161dd7 100644 --- a/bindings/javascript/package-lock.json +++ b/bindings/javascript/package-lock.json @@ -59,6 +59,13 @@ "node": ">=6.9.0" } }, + "node_modules/@drizzle-team/brocli": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", + "integrity": "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@emnapi/core": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", @@ -87,6 +94,238 @@ "tslib": "^2.4.0" } }, + "node_modules/@esbuild-kit/core-utils": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", + "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", + "deprecated": "Merged into tsx: https://tsx.is", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.18.20", + "source-map-support": "^0.5.21" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/@esbuild-kit/esm-loader": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", + "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", + "deprecated": "Merged into tsx: https://tsx.is", + "dev": true, + "license": "MIT", + "dependencies": { + "@esbuild-kit/core-utils": "^3.3.2", + "get-tsconfig": "^4.7.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/linux-arm64": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", @@ -104,6 +343,278 @@ "node": ">=18" } }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@inquirer/checkbox": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.0.tgz", @@ -524,6 +1035,209 @@ "dev": true, "license": "MIT" }, + "node_modules/@libsql/client": { + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.15.15.tgz", + "integrity": "sha512-twC0hQxPNHPKfeOv3sNT6u2pturQjLcI+CnpTM0SjRpocEGgfiZ7DWKXLNnsothjyJmDqEsBQJ5ztq9Wlu470w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@libsql/core": "^0.15.14", + "@libsql/hrana-client": "^0.7.0", + "js-base64": "^3.7.5", + "libsql": "^0.5.22", + "promise-limit": "^2.7.0" + } + }, + "node_modules/@libsql/core": { + "version": "0.15.15", + "resolved": "https://registry.npmjs.org/@libsql/core/-/core-0.15.15.tgz", + "integrity": "sha512-C88Z6UKl+OyuKKPwz224riz02ih/zHYI3Ho/LAcVOgjsunIRZoBw7fjRfaH9oPMmSNeQfhGklSG2il1URoOIsA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "js-base64": "^3.7.5" + } + }, + "node_modules/@libsql/darwin-arm64": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/@libsql/darwin-arm64/-/darwin-arm64-0.5.22.tgz", + "integrity": "sha512-4B8ZlX3nIDPndfct7GNe0nI3Yw6ibocEicWdC4fvQbSs/jdq/RC2oCsoJxJ4NzXkvktX70C1J4FcmmoBy069UA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@libsql/darwin-x64": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/@libsql/darwin-x64/-/darwin-x64-0.5.22.tgz", + "integrity": "sha512-ny2HYWt6lFSIdNFzUFIJ04uiW6finXfMNJ7wypkAD8Pqdm6nAByO+Fdqu8t7sD0sqJGeUCiOg480icjyQ2/8VA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "peer": true + }, + "node_modules/@libsql/hrana-client": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@libsql/hrana-client/-/hrana-client-0.7.0.tgz", + "integrity": "sha512-OF8fFQSkbL7vJY9rfuegK1R7sPgQ6kFMkDamiEccNUvieQ+3urzfDFI616oPl8V7T9zRmnTkSjMOImYCAVRVuw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@libsql/isomorphic-fetch": "^0.3.1", + "@libsql/isomorphic-ws": "^0.1.5", + "js-base64": "^3.7.5", + "node-fetch": "^3.3.2" + } + }, + "node_modules/@libsql/isomorphic-fetch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@libsql/isomorphic-fetch/-/isomorphic-fetch-0.3.1.tgz", + "integrity": "sha512-6kK3SUK5Uu56zPq/Las620n5aS9xJq+jMBcNSOmjhNf/MUvdyji4vrMTqD7ptY7/4/CAVEAYDeotUz60LNQHtw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@libsql/isomorphic-ws": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@libsql/isomorphic-ws/-/isomorphic-ws-0.1.5.tgz", + "integrity": "sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@types/ws": "^8.5.4", + "ws": "^8.13.0" + } + }, + "node_modules/@libsql/linux-arm-gnueabihf": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm-gnueabihf/-/linux-arm-gnueabihf-0.5.22.tgz", + "integrity": "sha512-3Uo3SoDPJe/zBnyZKosziRGtszXaEtv57raWrZIahtQDsjxBVjuzYQinCm9LRCJCUT5t2r5Z5nLDPJi2CwZVoA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@libsql/linux-arm-musleabihf": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm-musleabihf/-/linux-arm-musleabihf-0.5.22.tgz", + "integrity": "sha512-LCsXh07jvSojTNJptT9CowOzwITznD+YFGGW+1XxUr7fS+7/ydUrpDfsMX7UqTqjm7xG17eq86VkWJgHJfvpNg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@libsql/linux-arm64-gnu": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-gnu/-/linux-arm64-gnu-0.5.22.tgz", + "integrity": "sha512-KSdnOMy88c9mpOFKUEzPskSaF3VLflfSUCBwas/pn1/sV3pEhtMF6H8VUCd2rsedwoukeeCSEONqX7LLnQwRMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@libsql/linux-arm64-musl": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-musl/-/linux-arm64-musl-0.5.22.tgz", + "integrity": "sha512-mCHSMAsDTLK5YH//lcV3eFEgiR23Ym0U9oEvgZA0667gqRZg/2px+7LshDvErEKv2XZ8ixzw3p1IrBzLQHGSsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@libsql/linux-x64-gnu": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-gnu/-/linux-x64-gnu-0.5.22.tgz", + "integrity": "sha512-kNBHaIkSg78Y4BqAdgjcR2mBilZXs4HYkAmi58J+4GRwDQZh5fIUWbnQvB9f95DkWUIGVeenqLRFY2pcTmlsew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@libsql/linux-x64-musl": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-musl/-/linux-x64-musl-0.5.22.tgz", + "integrity": "sha512-UZ4Xdxm4pu3pQXjvfJiyCzZop/9j/eA2JjmhMaAhe3EVLH2g11Fy4fwyUp9sT1QJYR1kpc2JLuybPM0kuXv/Tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true + }, + "node_modules/@libsql/win32-x64-msvc": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/@libsql/win32-x64-msvc/-/win32-x64-msvc-0.5.22.tgz", + "integrity": "sha512-Fj0j8RnBpo43tVZUVoNK6BV/9AtDUM5S7DF3LB4qTYg1LMSZqi3yeCneUTLJD6XomQJlZzbI4mst89yspVSAnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true + }, "node_modules/@napi-rs/cli": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-3.1.5.tgz", @@ -886,6 +1600,15 @@ "node": ">=14.0.0" } }, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, "node_modules/@octokit/auth-token": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", @@ -1198,6 +1921,18 @@ "undici-types": "~7.10.0" } }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@vitest/browser": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-3.2.4.tgz", @@ -1446,6 +2181,27 @@ "node": ">=12" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/before-after-hook": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", @@ -1453,14 +2209,74 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/better-sqlite3": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.2.0.tgz", + "integrity": "sha512-eGbYq2CT+tos1fBwLQ/tkBt9J5M3JEHjku4hbvQUePCckkvVf14xWj+1m7dGoK81M/fOjFT7yM9UMeKT/+vFLQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, - "license": "MIT", - "optional": true, - "peer": true + "license": "MIT" }, "node_modules/cac": { "version": "6.7.14", @@ -1506,6 +2322,13 @@ "node": ">= 16" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, "node_modules/cli-width": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", @@ -1568,6 +2391,18 @@ "optional": true, "peer": true }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -1586,6 +2421,22 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -1596,6 +2447,16 @@ "node": ">=6" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -1606,6 +2467,16 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", @@ -1613,6 +2484,148 @@ "dev": true, "license": "MIT" }, + "node_modules/drizzle-kit": { + "version": "0.31.4", + "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.4.tgz", + "integrity": "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@drizzle-team/brocli": "^0.10.2", + "@esbuild-kit/esm-loader": "^2.5.5", + "esbuild": "^0.25.4", + "esbuild-register": "^3.5.0" + }, + "bin": { + "drizzle-kit": "bin.cjs" + } + }, + "node_modules/drizzle-orm": { + "version": "0.44.5", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.44.5.tgz", + "integrity": "sha512-jBe37K7d8ZSKptdKfakQFdeljtu3P2Cbo7tJoJSVZADzIKOBo9IAJPOmMsH2bZl90bZgh8FQlD8BjxXA/zuBkQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=4", + "@electric-sql/pglite": ">=0.2.0", + "@libsql/client": ">=0.10.0", + "@libsql/client-wasm": ">=0.10.0", + "@neondatabase/serverless": ">=0.10.0", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1.13", + "@prisma/client": "*", + "@tidbcloud/serverless": "*", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/sql.js": "*", + "@upstash/redis": ">=1.34.7", + "@vercel/postgres": ">=0.8.0", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=14.0.0", + "gel": ">=2", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@libsql/client-wasm": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@prisma/client": { + "optional": true + }, + "@tidbcloud/serverless": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "gel": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "prisma": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, "node_modules/emnapi": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/emnapi/-/emnapi-1.4.5.tgz", @@ -1635,6 +2648,16 @@ "dev": true, "license": "MIT" }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/es-module-lexer": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", @@ -1695,6 +2718,386 @@ "@esbuild/win32-x64": "0.25.9" } }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/expect-type": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", @@ -1755,6 +3158,39 @@ } } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "license": "MIT" + }, "node_modules/find-up": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", @@ -1786,6 +3222,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1801,6 +3259,26 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT" + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -1814,6 +3292,41 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1824,6 +3337,15 @@ "node": ">=8" } }, + "node_modules/js-base64": { + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.8.tgz", + "integrity": "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "peer": true + }, "node_modules/js-tokens": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", @@ -1831,6 +3353,53 @@ "dev": true, "license": "MIT" }, + "node_modules/libsql": { + "version": "0.5.22", + "resolved": "https://registry.npmjs.org/libsql/-/libsql-0.5.22.tgz", + "integrity": "sha512-NscWthMQt7fpU8lqd7LXMvT9pi+KhhmTHAJWUB/Lj6MWa0MKFv0F2V4C6WKKpjCVZl0VwcDz4nOI3CyaT1DDiA==", + "cpu": [ + "x64", + "arm64", + "wasm32", + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin", + "linux", + "win32" + ], + "peer": true, + "dependencies": { + "@neon-rs/load": "^0.0.4", + "detect-libc": "2.0.2" + }, + "optionalDependencies": { + "@libsql/darwin-arm64": "0.5.22", + "@libsql/darwin-x64": "0.5.22", + "@libsql/linux-arm-gnueabihf": "0.5.22", + "@libsql/linux-arm-musleabihf": "0.5.22", + "@libsql/linux-arm64-gnu": "0.5.22", + "@libsql/linux-arm64-musl": "0.5.22", + "@libsql/linux-x64-gnu": "0.5.22", + "@libsql/linux-x64-musl": "0.5.22", + "@libsql/win32-x64-msvc": "0.5.22" + } + }, + "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==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, "node_modules/locate-path": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", @@ -1874,6 +3443,36 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -1920,6 +3519,80 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.77.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.77.0.tgz", + "integrity": "sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -2085,6 +3758,33 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", @@ -2100,6 +3800,42 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/promise-limit": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz", + "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -2107,6 +3843,31 @@ "dev": true, "license": "MIT" }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/rollup": { "version": "4.50.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz", @@ -2148,6 +3909,27 @@ "fsevents": "~2.3.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -2188,6 +3970,53 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/sirv": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", @@ -2209,8 +4038,6 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", - "optional": true, - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2231,8 +4058,6 @@ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -2252,6 +4077,16 @@ "dev": true, "license": "MIT" }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -2280,6 +4115,16 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-literal": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", @@ -2293,6 +4138,36 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/terser": { "version": "5.44.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", @@ -2404,6 +4279,19 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/typanion": { "version": "3.14.0", "resolved": "https://registry.npmjs.org/typanion/-/typanion-3.14.0.tgz", @@ -2442,6 +4330,13 @@ "dev": true, "license": "ISC" }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/vite": { "version": "7.1.5", "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", @@ -2613,6 +4508,18 @@ } } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -2630,6 +4537,13 @@ "node": ">=8" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", @@ -2724,6 +4638,9 @@ "devDependencies": { "@napi-rs/cli": "^3.1.5", "@types/node": "^24.3.1", + "better-sqlite3": "^12.2.0", + "drizzle-kit": "^0.31.4", + "drizzle-orm": "^0.44.5", "typescript": "^5.9.2", "vitest": "^3.2.4" } @@ -2773,6 +4690,9 @@ "name": "@tursodatabase/sync-common", "version": "0.2.0-pre.3", "license": "MIT", + "dependencies": { + "@tursodatabase/database-common": "^0.2.0-pre.3" + }, "devDependencies": { "typescript": "^5.9.2" } diff --git a/bindings/javascript/packages/browser/promise.test.ts b/bindings/javascript/packages/browser/promise.test.ts index 741e77276..0f9cebff8 100644 --- a/bindings/javascript/packages/browser/promise.test.ts +++ b/bindings/javascript/packages/browser/promise.test.ts @@ -52,6 +52,7 @@ test('on-disk db', async () => { db2.close(); }) +// attach is not supported in browser for now // test('attach', async () => { // const path1 = `test-${(Math.random() * 10000) | 0}.db`; // const path2 = `test-${(Math.random() * 10000) | 0}.db`; diff --git a/bindings/javascript/packages/common/async-lock.ts b/bindings/javascript/packages/common/async-lock.ts new file mode 100644 index 000000000..cf69a8f39 --- /dev/null +++ b/bindings/javascript/packages/common/async-lock.ts @@ -0,0 +1,29 @@ +export class AsyncLock { + locked: boolean; + queue: any[]; + constructor() { + this.locked = false; + this.queue = [] + } + async acquire() { + if (!this.locked) { + this.locked = true; + return Promise.resolve(); + } else { + const block = new Promise(resolve => { this.queue.push(resolve) }); + return block; + } + } + release() { + if (this.locked == false) { + throw new Error("invalid state: lock was already unlocked"); + } + const item = this.queue.shift(); + if (item != null) { + this.locked = true; + item(); + } else { + this.locked = false; + } + } +} \ No newline at end of file diff --git a/bindings/javascript/packages/common/index.ts b/bindings/javascript/packages/common/index.ts index 35e092d03..40a44b573 100644 --- a/bindings/javascript/packages/common/index.ts +++ b/bindings/javascript/packages/common/index.ts @@ -2,5 +2,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"; +import { AsyncLock } from "./async-lock.js"; -export { DatabaseCompat, StatementCompat, DatabasePromise, StatementPromise, NativeDatabase, NativeStatement, SqliteError, DatabaseOpts } +export { DatabaseCompat, StatementCompat, DatabasePromise, StatementPromise, NativeDatabase, NativeStatement, SqliteError, DatabaseOpts, AsyncLock } diff --git a/bindings/javascript/packages/common/promise.ts b/bindings/javascript/packages/common/promise.ts index f1a22260c..8dbcf5058 100644 --- a/bindings/javascript/packages/common/promise.ts +++ b/bindings/javascript/packages/common/promise.ts @@ -1,3 +1,4 @@ +import { AsyncLock } from "./async-lock.js"; import { bindParams } from "./bind.js"; import { SqliteError } from "./sqlite-error.js"; import { NativeDatabase, NativeStatement, STEP_IO, STEP_ROW, STEP_DONE, DatabaseOpts } from "./types.js"; @@ -32,6 +33,7 @@ class Database { db: NativeDatabase; memory: boolean; open: boolean; + execLock: AsyncLock; private _inTransaction: boolean = false; /** * Creates a new database connection. If the database file pointed to by `path` does not exists, it will be created. @@ -57,6 +59,7 @@ class Database { initialize(db: NativeDatabase, name, readonly) { this.db = db; this.memory = db.memory; + this.execLock = new AsyncLock(); Object.defineProperties(this, { inTransaction: { get: () => this._inTransaction, @@ -112,17 +115,22 @@ class Database { const db = this; const wrapTxn = (mode) => { return async (...bindParameters) => { - await db.exec("BEGIN " + mode); - db._inTransaction = true; + await this.execLock.acquire(); try { - const result = await fn(...bindParameters); - await db.exec("COMMIT"); - db._inTransaction = false; - return result; - } catch (err) { - await db.exec("ROLLBACK"); - db._inTransaction = false; - throw err; + await db.exec("BEGIN " + mode); + db._inTransaction = true; + try { + const result = await fn(...bindParameters); + await db.exec("COMMIT"); + db._inTransaction = false; + return result; + } catch (err) { + await db.exec("ROLLBACK"); + db._inTransaction = false; + throw err; + } + } finally { + this.execLock.release(); } }; }; @@ -195,6 +203,7 @@ class Database { throw new TypeError("The database connection is not open"); } + await this.execLock.acquire(); try { const stmt = this.prepare(sql); try { @@ -204,6 +213,8 @@ class Database { } } catch (err) { throw convertError(err); + } finally { + this.execLock.release(); } } @@ -302,25 +313,30 @@ class Statement { this.stmt.reset(); bindParams(this.stmt, bindParameters); - while (true) { - const stepResult = this.stmt.stepSync(); - if (stepResult === STEP_IO) { - await this.db.db.ioLoopAsync(); - continue; - } - if (stepResult === STEP_DONE) { - break; - } - if (stepResult === STEP_ROW) { - // For run(), we don't need the row data, just continue - continue; + await this.db.execLock.acquire(); + try { + while (true) { + const stepResult = await this.stmt.stepSync(); + if (stepResult === STEP_IO) { + await this.db.db.ioLoopAsync(); + continue; + } + if (stepResult === STEP_DONE) { + break; + } + if (stepResult === STEP_ROW) { + // For run(), we don't need the row data, just continue + continue; + } } + + const lastInsertRowid = this.db.db.lastInsertRowid(); + const changes = this.db.db.totalChanges() === totalChangesBefore ? 0 : this.db.db.changes(); + + return { changes, lastInsertRowid }; + } finally { + this.db.execLock.release(); } - - const lastInsertRowid = this.db.db.lastInsertRowid(); - const changes = this.db.db.totalChanges() === totalChangesBefore ? 0 : this.db.db.changes(); - - return { changes, lastInsertRowid }; } /** @@ -332,18 +348,23 @@ class Statement { this.stmt.reset(); bindParams(this.stmt, bindParameters); - while (true) { - const stepResult = this.stmt.stepSync(); - if (stepResult === STEP_IO) { - await this.db.db.ioLoopAsync(); - continue; - } - if (stepResult === STEP_DONE) { - return undefined; - } - if (stepResult === STEP_ROW) { - return this.stmt.row(); + await this.db.execLock.acquire(); + try { + while (true) { + const stepResult = await this.stmt.stepSync(); + if (stepResult === STEP_IO) { + await this.db.db.ioLoopAsync(); + continue; + } + if (stepResult === STEP_DONE) { + return undefined; + } + if (stepResult === STEP_ROW) { + return this.stmt.row(); + } } + } finally { + } } @@ -356,18 +377,23 @@ class Statement { this.stmt.reset(); bindParams(this.stmt, bindParameters); - while (true) { - const stepResult = this.stmt.stepSync(); - if (stepResult === STEP_IO) { - await this.db.db.ioLoopAsync(); - continue; - } - if (stepResult === STEP_DONE) { - break; - } - if (stepResult === STEP_ROW) { - yield this.stmt.row(); + await this.db.execLock.acquire(); + try { + while (true) { + const stepResult = await this.stmt.stepSync(); + if (stepResult === STEP_IO) { + await this.db.db.ioLoopAsync(); + continue; + } + if (stepResult === STEP_DONE) { + break; + } + if (stepResult === STEP_ROW) { + yield this.stmt.row(); + } } + } finally { + this.db.execLock.release(); } } @@ -381,20 +407,26 @@ class Statement { bindParams(this.stmt, bindParameters); const rows: any[] = []; - while (true) { - const stepResult = this.stmt.stepSync(); - if (stepResult === STEP_IO) { - await this.db.db.ioLoopAsync(); - continue; - } - if (stepResult === STEP_DONE) { - break; - } - if (stepResult === STEP_ROW) { - rows.push(this.stmt.row()); + await this.db.execLock.acquire(); + try { + while (true) { + const stepResult = await this.stmt.stepSync(); + if (stepResult === STEP_IO) { + await this.db.db.ioLoopAsync(); + continue; + } + if (stepResult === STEP_DONE) { + break; + } + if (stepResult === STEP_ROW) { + rows.push(this.stmt.row()); + } } + return rows; + } + finally { + this.db.execLock.release(); } - return rows; } /** diff --git a/bindings/javascript/packages/native/package.json b/bindings/javascript/packages/native/package.json index 81b8e092c..56888f616 100644 --- a/bindings/javascript/packages/native/package.json +++ b/bindings/javascript/packages/native/package.json @@ -22,6 +22,9 @@ "devDependencies": { "@napi-rs/cli": "^3.1.5", "@types/node": "^24.3.1", + "better-sqlite3": "^12.2.0", + "drizzle-kit": "^0.31.4", + "drizzle-orm": "^0.44.5", "typescript": "^5.9.2", "vitest": "^3.2.4" }, diff --git a/bindings/javascript/packages/native/promise.test.ts b/bindings/javascript/packages/native/promise.test.ts index d75e3728e..5819c8f39 100644 --- a/bindings/javascript/packages/native/promise.test.ts +++ b/bindings/javascript/packages/native/promise.test.ts @@ -1,6 +1,26 @@ import { unlinkSync } from "node:fs"; import { expect, test } from 'vitest' import { connect } from './promise.js' +import { sql } from 'drizzle-orm'; +import { drizzle } from 'drizzle-orm/better-sqlite3'; + +test('drizzle-orm', async () => { + const path = `test-${(Math.random() * 10000) | 0}.db`; + try { + const conn = await connect(path); + const db = drizzle(conn); + await db.run('CREATE TABLE t(x, y)'); + let tasks = []; + for (let i = 0; i < 1234; i++) { + tasks.push(db.run(sql`INSERT INTO t VALUES (${i}, randomblob(${i} * 5))`)) + } + await Promise.all(tasks); + expect(await db.all("SELECT COUNT(*) as cnt FROM t")).toEqual([{ cnt: 1234 }]) + } finally { + unlinkSync(path); + unlinkSync(`${path}-wal`); + } +}) test('in-memory db', async () => { const db = await connect(":memory:"); diff --git a/bindings/javascript/sync/Cargo.toml b/bindings/javascript/sync/Cargo.toml index 2f3f3d177..00749c5d3 100644 --- a/bindings/javascript/sync/Cargo.toml +++ b/bindings/javascript/sync/Cargo.toml @@ -18,9 +18,10 @@ turso_core = { workspace = true } turso_node = { workspace = true } genawaiter = { version = "0.99.1", default-features = false } tracing-subscriber = { workspace = true } +tracing.workspace = true [build-dependencies] napi-build = "2.2.3" [features] -browser = ["turso_node/browser"] \ No newline at end of file +browser = ["turso_node/browser"] diff --git a/bindings/javascript/sync/packages/browser/a b/bindings/javascript/sync/packages/browser/a new file mode 100755 index 0000000000000000000000000000000000000000..8f5630c076b124960766842e96d6e1c9609bce89 GIT binary patch literal 57344 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 X`wJoW7eekYgxp^UxxWxL?JxWX%{~pb literal 0 HcmV?d00001 diff --git a/bindings/javascript/sync/packages/browser/a-shm b/bindings/javascript/sync/packages/browser/a-shm new file mode 100755 index 0000000000000000000000000000000000000000..7c251555d016a9c5a07e151b6fff76239b1cde71 GIT binary patch literal 32768 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 N1Uv#Bf&VK6{s;Wz7XknP literal 0 HcmV?d00001 diff --git a/bindings/javascript/sync/packages/browser/package.json b/bindings/javascript/sync/packages/browser/package.json index a7d33c2ce..efddc0a70 100644 --- a/bindings/javascript/sync/packages/browser/package.json +++ b/bindings/javascript/sync/packages/browser/package.json @@ -42,7 +42,7 @@ "tsc-build": "npm exec tsc && cp sync.wasm32-wasi.wasm ./dist/sync.wasm32-wasi.wasm && WASM_FILE=sync.wasm32-wasi.wasm JS_FILE=./dist/wasm-inline.js node ../../../scripts/inline-wasm-base64.js && npm run bundle", "bundle": "vite build", "build": "npm run napi-build && npm run tsc-build", - "test": "VITE_TURSO_DB_URL=http://b--a--a.localhost:10000 CI=1 vitest --browser=chromium --run && VITE_TURSO_DB_URL=http://b--a--a.localhost:10000 CI=1 vitest --browser=firefox --run" + "test": "VITE_TURSO_DB_URL=http://c--a--a.localhost:10000 CI=1 vitest --testTimeout 30000 --browser=chromium --run && VITE_TURSO_DB_URL=http://c--a--a.localhost:10000 CI=1 vitest --testTimeout 30000 --browser=firefox --run" }, "napi": { "binaryName": "sync", diff --git a/bindings/javascript/sync/packages/browser/promise.test.ts b/bindings/javascript/sync/packages/browser/promise.test.ts index e30163af0..fb60ff61b 100644 --- a/bindings/javascript/sync/packages/browser/promise.test.ts +++ b/bindings/javascript/sync/packages/browser/promise.test.ts @@ -260,6 +260,105 @@ test('persistence-pull-push', async () => { expect(rows2.sort(localeCompare)).toEqual(expected.sort(localeCompare)) }) +test('pull-push-concurrent', async () => { + { + const db = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL, longPollTimeoutMs: 5000 }); + await db.exec("CREATE TABLE IF NOT EXISTS q(x TEXT PRIMARY KEY, y)"); + await db.exec("DELETE FROM q"); + await db.push(); + await db.close(); + } + let pullResolve = null; + const pullFinish = new Promise(resolve => pullResolve = resolve); + let pushResolve = null; + const pushFinish = new Promise(resolve => pushResolve = resolve); + let stopPull = false; + let stopPush = false; + const db = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL }); + let pull = async () => { + try { + await db.pull(); + } catch (e) { + console.error('pull', e); + } finally { + if (!stopPull) { + setTimeout(pull, 0); + } else { + pullResolve() + } + } + } + let push = async () => { + try { + if ((await db.stats()).operations > 0) { + await db.push(); + } + } catch (e) { + console.error('push', e); + } finally { + if (!stopPush) { + setTimeout(push, 0); + } else { + pushResolve(); + } + } + } + setTimeout(pull, 0); + setTimeout(push, 0); + for (let i = 0; i < 1000; i++) { + await db.exec(`INSERT INTO q VALUES ('k${i}', 'v${i}')`); + } + await new Promise(resolve => setTimeout(resolve, 1000)); + stopPush = true; + await pushFinish; + stopPull = true; + await pullFinish; + console.info(await db.stats()); +}) + +test('concurrent-updates', async () => { + { + const db = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL, longPollTimeoutMs: 5000 }); + await db.exec("CREATE TABLE IF NOT EXISTS q(x TEXT PRIMARY KEY, y)"); + await db.exec("DELETE FROM q"); + await db.push(); + await db.close(); + } + const db1 = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL }); + const db2 = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL }); + async function pull(db) { + try { + await db.pull(); + } catch (e) { + // ignore + } finally { + setTimeout(async () => await pull(db), 0); + } + } + async function push(db) { + try { + await db.push(); + } catch (e) { + // ignore + } finally { + setTimeout(async () => await push(db), 0); + } + } + setTimeout(async () => await pull(db1), 0) + setTimeout(async () => await pull(db2), 0) + setTimeout(async () => await push(db1), 0) + setTimeout(async () => await push(db2), 0) + for (let i = 0; i < 1000; i++) { + try { + await db1.exec(`INSERT INTO q VALUES ('1', 0) ON CONFLICT DO UPDATE SET y = randomblob(128)`); + await db2.exec(`INSERT INTO q VALUES ('2', 0) ON CONFLICT DO UPDATE SET y = randomblob(128)`); + } catch (e) { + // ignore + } + await new Promise(resolve => setTimeout(resolve, 1)); + } +}) + test('transform', async () => { { const db = await connect({ diff --git a/bindings/javascript/sync/packages/browser/promise.ts b/bindings/javascript/sync/packages/browser/promise.ts index 3f43b81b6..b12680598 100644 --- a/bindings/javascript/sync/packages/browser/promise.ts +++ b/bindings/javascript/sync/packages/browser/promise.ts @@ -1,6 +1,6 @@ import { registerFileAtWorker, unregisterFileAtWorker } from "@tursodatabase/database-browser-common" import { DatabasePromise, DatabaseOpts, NativeDatabase } from "@tursodatabase/database-common" -import { ProtocolIo, run, SyncOpts, RunOpts, memoryIO, SyncEngineStats } from "@tursodatabase/sync-common"; +import { ProtocolIo, run, SyncOpts, RunOpts, memoryIO, SyncEngineStats, SyncEngineGuards } from "@tursodatabase/sync-common"; let BrowserIo: ProtocolIo = { async read(path: string): Promise { @@ -24,6 +24,7 @@ class Database extends DatabasePromise { io: ProtocolIo; worker: Worker | null; fsPath: string | null; + guards: SyncEngineGuards; constructor(db: NativeDatabase, io: ProtocolIo, worker: Worker | null, runOpts: RunOpts, engine: any, fsPath: string | null, opts: DatabaseOpts = {}) { super(db, opts) this.io = io; @@ -31,18 +32,21 @@ class Database extends DatabasePromise { this.runOpts = runOpts; this.engine = engine; this.fsPath = fsPath; + this.guards = new SyncEngineGuards(); } async sync() { - await run(this.runOpts, this.io, this.engine, this.engine.sync()); + await this.push(); + await this.pull(); } async pull() { - await run(this.runOpts, this.io, this.engine, this.engine.pull()); + const changes = await this.guards.wait(async () => await run(this.runOpts, this.io, this.engine, this.engine.wait())); + await this.guards.apply(async () => await run(this.runOpts, this.io, this.engine, this.engine.apply(changes))); } async push() { - await run(this.runOpts, this.io, this.engine, this.engine.push()); + await this.guards.push(async () => await run(this.runOpts, this.io, this.engine, this.engine.push())); } async checkpoint() { - await run(this.runOpts, this.io, this.engine, this.engine.checkpoint()); + await this.guards.checkpoint(async () => await run(this.runOpts, this.io, this.engine, this.engine.checkpoint())); } async stats(): Promise { return (await run(this.runOpts, this.io, this.engine, this.engine.stats())); @@ -76,7 +80,8 @@ async function connect(opts: SyncOpts, connect: (any) => any, init: () => Promis tablesIgnore: opts.tablesIgnore, useTransform: opts.transform != null, tracing: opts.tracing, - protocolVersion: 1 + protocolVersion: 1, + longPollTimeoutMs: opts.longPollTimeoutMs }); const runOpts: RunOpts = { url: opts.url, diff --git a/bindings/javascript/sync/packages/common/index.ts b/bindings/javascript/sync/packages/common/index.ts index 822a8c24f..7e9af0bea 100644 --- a/bindings/javascript/sync/packages/common/index.ts +++ b/bindings/javascript/sync/packages/common/index.ts @@ -1,5 +1,5 @@ -import { run, memoryIO } from "./run.js" +import { run, memoryIO, SyncEngineGuards } from "./run.js" import { SyncOpts, ProtocolIo, RunOpts, DatabaseRowMutation, DatabaseRowStatement, DatabaseRowTransformResult, SyncEngineStats } from "./types.js" -export { run, memoryIO, } +export { run, memoryIO, SyncEngineGuards } export type { SyncOpts, ProtocolIo, RunOpts, DatabaseRowMutation, DatabaseRowStatement, DatabaseRowTransformResult, SyncEngineStats } \ No newline at end of file diff --git a/bindings/javascript/sync/packages/common/package.json b/bindings/javascript/sync/packages/common/package.json index 44e04bd38..dc962ad31 100644 --- a/bindings/javascript/sync/packages/common/package.json +++ b/bindings/javascript/sync/packages/common/package.json @@ -21,5 +21,8 @@ "tsc-build": "npm exec tsc", "build": "npm run tsc-build", "test": "echo 'no tests'" + }, + "dependencies": { + "@tursodatabase/database-common": "^0.2.0-pre.3" } } diff --git a/bindings/javascript/sync/packages/common/run.ts b/bindings/javascript/sync/packages/common/run.ts index f26333d4b..f80606db8 100644 --- a/bindings/javascript/sync/packages/common/run.ts +++ b/bindings/javascript/sync/packages/common/run.ts @@ -1,6 +1,7 @@ "use strict"; import { GeneratorResponse, ProtocolIo, RunOpts } from "./types.js"; +import { AsyncLock } from "@tursodatabase/database-common"; const GENERATOR_RESUME_IO = 0; const GENERATOR_RESUME_DONE = 1; @@ -114,6 +115,10 @@ export async function run(opts: RunOpts, io: ProtocolIo, engine: any, generator: if (type == 'SyncEngineStats') { return rest; } + if (type == 'SyncEngineChanges') { + //@ts-ignore + return rest.changes; + } for (let request = engine.protocolIo(); request != null; request = engine.protocolIo()) { tasks.push(trackPromise(process(opts, io, request))); } @@ -124,4 +129,67 @@ export async function run(opts: RunOpts, io: ProtocolIo, engine: any, generator: tasks = tasks.filter(t => !t.finished); } return generator.take(); +} + + + +export class SyncEngineGuards { + waitLock: AsyncLock; + pushLock: AsyncLock; + pullLock: AsyncLock; + checkpointLock: AsyncLock; + constructor() { + this.waitLock = new AsyncLock(); + this.pushLock = new AsyncLock(); + this.pullLock = new AsyncLock(); + this.checkpointLock = new AsyncLock(); + } + async wait(f: () => Promise): Promise { + try { + await this.waitLock.acquire(); + return await f(); + } finally { + this.waitLock.release(); + } + } + async push(f: () => Promise) { + try { + await this.pushLock.acquire(); + await this.pullLock.acquire(); + await this.checkpointLock.acquire(); + return await f(); + } finally { + this.pushLock.release(); + this.pullLock.release(); + this.checkpointLock.release(); + } + } + async apply(f: () => Promise) { + try { + await this.waitLock.acquire(); + await this.pushLock.acquire(); + await this.pullLock.acquire(); + await this.checkpointLock.acquire(); + return await f(); + } finally { + this.waitLock.release(); + this.pushLock.release(); + this.pullLock.release(); + this.checkpointLock.release(); + } + } + async checkpoint(f: () => Promise) { + try { + await this.waitLock.acquire(); + await this.pushLock.acquire(); + await this.pullLock.acquire(); + await this.checkpointLock.acquire(); + return await f(); + } finally { + this.waitLock.release(); + this.pushLock.release(); + this.pullLock.release(); + this.checkpointLock.release(); + } + } } \ No newline at end of file diff --git a/bindings/javascript/sync/packages/common/tsconfig.json b/bindings/javascript/sync/packages/common/tsconfig.json index 9bc14edd3..2e4046e8a 100644 --- a/bindings/javascript/sync/packages/common/tsconfig.json +++ b/bindings/javascript/sync/packages/common/tsconfig.json @@ -3,8 +3,9 @@ "skipLibCheck": true, "declaration": true, "declarationMap": true, - "module": "esnext", + "module": "nodenext", "target": "esnext", + "moduleResolution": "nodenext", "outDir": "dist/", "lib": [ "es2020", diff --git a/bindings/javascript/sync/packages/common/types.ts b/bindings/javascript/sync/packages/common/types.ts index 27006de8d..8391825cf 100644 --- a/bindings/javascript/sync/packages/common/types.ts +++ b/bindings/javascript/sync/packages/common/types.ts @@ -54,4 +54,4 @@ export interface SyncEngineStats { revision: string | null; } -export type GeneratorResponse = { type: 'IO' } | { type: 'Done' } | ({ type: 'SyncEngineStats' } & SyncEngineStats) \ No newline at end of file +export type GeneratorResponse = { type: 'IO' } | { type: 'Done' } | ({ type: 'SyncEngineStats' } & SyncEngineStats) | { type: 'SyncEngineChanges', changes: any } \ No newline at end of file diff --git a/bindings/javascript/sync/packages/native/index.d.ts b/bindings/javascript/sync/packages/native/index.d.ts index 02ff2c385..4d1b45fa9 100644 --- a/bindings/javascript/sync/packages/native/index.d.ts +++ b/bindings/javascript/sync/packages/native/index.d.ts @@ -174,15 +174,19 @@ export declare class SyncEngine { /** Runs the I/O loop asynchronously, returning a Promise. */ ioLoopAsync(): Promise protocolIo(): JsProtocolRequestBytes | null - sync(): GeneratorHolder push(): GeneratorHolder stats(): GeneratorHolder - pull(): GeneratorHolder + wait(): GeneratorHolder + apply(changes: SyncEngineChanges): GeneratorHolder checkpoint(): GeneratorHolder open(): Database close(): void } +export declare class SyncEngineChanges { + +} + export declare const enum DatabaseChangeTypeJs { Insert = 0, Update = 1, @@ -217,6 +221,7 @@ export type GeneratorResponse = | { type: 'IO' } | { type: 'Done' } | { type: 'SyncEngineStats', operations: number, mainWal: number, revertWal: number, lastPullUnixTime: number, lastPushUnixTime?: number, revision?: string } + | { type: 'SyncEngineChanges', changes: SyncEngineChanges } export type JsProtocolRequest = | { type: 'Http', method: string, path: string, body?: Array, headers: Array<[string, string]> } diff --git a/bindings/javascript/sync/packages/native/index.js b/bindings/javascript/sync/packages/native/index.js index 53bff489f..12e351d61 100644 --- a/bindings/javascript/sync/packages/native/index.js +++ b/bindings/javascript/sync/packages/native/index.js @@ -508,7 +508,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { Database, Opfs, OpfsFile, Statement, initThreadPool, GeneratorHolder, JsDataCompletion, JsProtocolIo, JsProtocolRequestBytes, SyncEngine, DatabaseChangeTypeJs, SyncEngineProtocolVersion } = nativeBinding +const { Database, Statement, GeneratorHolder, JsDataCompletion, JsProtocolIo, JsProtocolRequestBytes, SyncEngine, SyncEngineChanges, DatabaseChangeTypeJs, SyncEngineProtocolVersion } = nativeBinding export { Database } export { Opfs } export { OpfsFile } @@ -519,5 +519,6 @@ export { JsDataCompletion } export { JsProtocolIo } export { JsProtocolRequestBytes } export { SyncEngine } +export { SyncEngineChanges } export { DatabaseChangeTypeJs } export { SyncEngineProtocolVersion } diff --git a/bindings/javascript/sync/packages/native/log b/bindings/javascript/sync/packages/native/log new file mode 100644 index 000000000..b6989da2e --- /dev/null +++ b/bindings/javascript/sync/packages/native/log @@ -0,0 +1,14 @@ + +> @tursodatabase/sync@0.2.0-pre.3 test +> VITE_TURSO_DB_URL=http://c--a--a.localhost:10000 vitest --run -t update + + + RUN v3.2.4 /home/sivukhin/turso/limbo/bindings/javascript/sync/packages/native + + ✓ promise.test.ts (14 tests | 13 skipped) 109ms + + Test Files 1 passed (1) + Tests 1 passed | 13 skipped (14) + Start at 16:40:50 + Duration 436ms (transform 99ms, setup 0ms, collect 118ms, tests 109ms, environment 0ms, prepare 57ms) + diff --git a/bindings/javascript/sync/packages/native/package.json b/bindings/javascript/sync/packages/native/package.json index dcdc39fac..c0c01081c 100644 --- a/bindings/javascript/sync/packages/native/package.json +++ b/bindings/javascript/sync/packages/native/package.json @@ -31,7 +31,7 @@ "napi-artifacts": "napi artifacts --output-dir .", "tsc-build": "npm exec tsc", "build": "npm run napi-build && npm run tsc-build", - "test": "VITE_TURSO_DB_URL=http://b--a--a.localhost:10000 vitest --run", + "test": "VITE_TURSO_DB_URL=http://c--a--a.localhost:10000 vitest --run", "prepublishOnly": "npm run napi-dirs && npm run napi-artifacts && napi prepublish -t npm" }, "napi": { diff --git a/bindings/javascript/sync/packages/native/promise.test.ts b/bindings/javascript/sync/packages/native/promise.test.ts index cae58db11..fea253fef 100644 --- a/bindings/javascript/sync/packages/native/promise.test.ts +++ b/bindings/javascript/sync/packages/native/promise.test.ts @@ -160,7 +160,7 @@ test('checkpoint', async () => { await db1.checkpoint(); expect((await db1.stats()).mainWal).toBe(0); let revertWal = (await db1.stats()).revertWal; - expect(revertWal).toBeLessThan(4096 * 1000 / 100); + expect(revertWal).toBeLessThan(4096 * 1000 / 50); for (let i = 0; i < 1000; i++) { await db1.exec(`UPDATE q SET y = 'u${i}' WHERE x = 'k${i}'`); @@ -284,6 +284,119 @@ test('persistence-pull-push', async () => { } }) +test('update', async () => { + { + const db = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL, longPollTimeoutMs: 5000 }); + await db.exec("CREATE TABLE IF NOT EXISTS q(x TEXT PRIMARY KEY, y)"); + await db.exec("DELETE FROM q"); + await db.push(); + await db.close(); + } + const db = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL }); + await db.exec("INSERT INTO q VALUES ('1', '2')") + await db.push(); + await db.exec("INSERT INTO q VALUES ('1', '2') ON CONFLICT DO UPDATE SET y = '3'") + await db.push(); +}) + +test('concurrent-updates', async () => { + { + const db = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL, longPollTimeoutMs: 5000 }); + await db.exec("CREATE TABLE IF NOT EXISTS q(x TEXT PRIMARY KEY, y)"); + await db.exec("DELETE FROM q"); + await db.push(); + await db.close(); + } + const db1 = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL }); + async function pull(db) { + try { + await db.pull(); + } catch (e) { + // ignore + } finally { + setTimeout(async () => await pull(db), 0); + } + } + async function push(db) { + try { + await db.push(); + } catch (e) { + // ignore + } finally { + setTimeout(async () => await push(db), 0); + } + } + setTimeout(async () => await pull(db1), 0) + setTimeout(async () => await push(db1), 0) + for (let i = 0; i < 1000; i++) { + try { + await Promise.all([ + db1.exec(`INSERT INTO q VALUES ('1', 0) ON CONFLICT DO UPDATE SET y = ${i + 1}`), + db1.exec(`INSERT INTO q VALUES ('2', 0) ON CONFLICT DO UPDATE SET y = ${i + 1}`) + ]); + } catch (e) { + // ignore + } + await new Promise(resolve => setTimeout(resolve, 1)); + } +}) + +test('pull-push-concurrent', async () => { + { + const db = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL, longPollTimeoutMs: 5000 }); + await db.exec("CREATE TABLE IF NOT EXISTS q(x TEXT PRIMARY KEY, y)"); + await db.exec("DELETE FROM q"); + await db.push(); + await db.close(); + } + let pullResolve = null; + const pullFinish = new Promise(resolve => pullResolve = resolve); + let pushResolve = null; + const pushFinish = new Promise(resolve => pushResolve = resolve); + let stopPull = false; + let stopPush = false; + const db = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL }); + let pull = async () => { + try { + await db.pull(); + } catch (e) { + console.error('pull', e); + } finally { + if (!stopPull) { + setTimeout(pull, 0); + } else { + pullResolve() + } + } + } + let push = async () => { + try { + if ((await db.stats()).operations > 0) { + await db.push(); + } + } catch (e) { + console.error('push', e); + } finally { + if (!stopPush) { + setTimeout(push, 0); + } else { + pushResolve(); + } + } + } + setTimeout(pull, 0); + setTimeout(push, 0); + for (let i = 0; i < 1000; i++) { + await db.exec(`INSERT INTO q VALUES ('k${i}', 'v${i}')`); + } + await new Promise(resolve => setTimeout(resolve, 1000)); + stopPush = true; + await pushFinish; + stopPull = true; + await pullFinish; + console.info(await db.stats()); +}) + test('transform', async () => { { const db = await connect({ diff --git a/bindings/javascript/sync/packages/native/promise.ts b/bindings/javascript/sync/packages/native/promise.ts index 2bff31d29..f00656b03 100644 --- a/bindings/javascript/sync/packages/native/promise.ts +++ b/bindings/javascript/sync/packages/native/promise.ts @@ -1,5 +1,5 @@ import { DatabasePromise, DatabaseOpts, NativeDatabase } from "@tursodatabase/database-common" -import { ProtocolIo, run, SyncOpts, RunOpts, DatabaseRowMutation, DatabaseRowStatement, DatabaseRowTransformResult, SyncEngineStats } from "@tursodatabase/sync-common"; +import { ProtocolIo, run, SyncOpts, RunOpts, DatabaseRowMutation, DatabaseRowStatement, DatabaseRowTransformResult, SyncEngineStats, SyncEngineGuards } from "@tursodatabase/sync-common"; import { Database as NativeDB, SyncEngine } from "#index"; import { promises } from "node:fs"; @@ -43,23 +43,27 @@ class Database extends DatabasePromise { runOpts: RunOpts; engine: any; io: ProtocolIo; + guards: SyncEngineGuards constructor(db: NativeDatabase, io: ProtocolIo, runOpts: RunOpts, engine: any, opts: DatabaseOpts = {}) { super(db, opts) this.runOpts = runOpts; this.engine = engine; this.io = io; + this.guards = new SyncEngineGuards(); } async sync() { - await run(this.runOpts, this.io, this.engine, this.engine.sync()); + await this.push(); + await this.pull(); } async pull() { - await run(this.runOpts, this.io, this.engine, this.engine.pull()); + const changes = await this.guards.wait(async () => await run(this.runOpts, this.io, this.engine, this.engine.wait())); + await this.guards.apply(async () => await run(this.runOpts, this.io, this.engine, this.engine.apply(changes))); } async push() { - await run(this.runOpts, this.io, this.engine, this.engine.push()); + await this.guards.push(async () => await run(this.runOpts, this.io, this.engine, this.engine.push())); } async checkpoint() { - await run(this.runOpts, this.io, this.engine, this.engine.checkpoint()); + await this.guards.checkpoint(async () => await run(this.runOpts, this.io, this.engine, this.engine.checkpoint())); } async stats(): Promise { return (await run(this.runOpts, this.io, this.engine, this.engine.stats())); @@ -83,8 +87,8 @@ async function connect(opts: SyncOpts): Promise { tablesIgnore: opts.tablesIgnore, useTransform: opts.transform != null, tracing: opts.tracing, - longPollTimeoutMs: opts.longPollTimeoutMs, protocolVersion: 1, + longPollTimeoutMs: opts.longPollTimeoutMs, }); const runOpts: RunOpts = { url: opts.url, diff --git a/bindings/javascript/sync/src/generator.rs b/bindings/javascript/sync/src/generator.rs index 2aae4f373..00ae9661e 100644 --- a/bindings/javascript/sync/src/generator.rs +++ b/bindings/javascript/sync/src/generator.rs @@ -5,7 +5,7 @@ use std::{ sync::{Arc, Mutex}, }; -use turso_sync_engine::types::ProtocolCommand; +use turso_sync_engine::types::{DbChangesStatus, ProtocolCommand}; pub const GENERATOR_RESUME_IO: u32 = 0; pub const GENERATOR_RESUME_DONE: u32 = 1; @@ -35,7 +35,12 @@ impl>> Generator } } -#[napi(discriminant = "type")] +#[napi] +pub struct SyncEngineChanges { + pub(crate) status: Box>, +} + +#[napi(discriminant = "type", object_from_js = false)] pub enum GeneratorResponse { IO, Done, @@ -47,6 +52,9 @@ pub enum GeneratorResponse { last_push_unix_time: Option, revision: Option, }, + SyncEngineChanges { + changes: SyncEngineChanges, + }, } #[napi] diff --git a/bindings/javascript/sync/src/lib.rs b/bindings/javascript/sync/src/lib.rs index c70932081..13427f501 100644 --- a/bindings/javascript/sync/src/lib.rs +++ b/bindings/javascript/sync/src/lib.rs @@ -19,7 +19,7 @@ use turso_sync_engine::{ }; use crate::{ - generator::{GeneratorHolder, GeneratorResponse}, + generator::{GeneratorHolder, GeneratorResponse, SyncEngineChanges}, js_protocol_io::{JsProtocolIo, JsProtocolRequestBytes}, }; @@ -28,38 +28,6 @@ pub struct DatabaseOpts { pub path: String, } -pub struct SyncEngineGuard { - inner: Arc>>>, - wait_lock: Mutex<()>, - push_lock: Mutex<()>, - pull_lock: Mutex<()>, - checkpoint_lock: Mutex<()>, -} - -impl SyncEngineGuard { - fn checkpoint_lock(&self) -> (MutexGuard<'_, ()>, MutexGuard<'_, ()>, MutexGuard<'_, ()>) { - let push = self.push_lock.lock().unwrap(); - let pull = self.pull_lock.lock().unwrap(); - let checkpoint = self.checkpoint_lock.lock().unwrap(); - (push, pull, checkpoint) - } - fn pull_lock(&self) -> (MutexGuard<'_, ()>, MutexGuard<'_, ()>, MutexGuard<'_, ()>) { - let wait = self.wait_lock.lock().unwrap(); - let push = self.push_lock.lock().unwrap(); - let pull = self.pull_lock.lock().unwrap(); - (wait, push, pull) - } - fn push_lock(&self) -> MutexGuard<'_, ()> { - let push = self.push_lock.lock().unwrap(); - push - } - fn wait_lock(&self) -> (MutexGuard<'_, ()>, MutexGuard<'_, ()>) { - let wait = self.wait_lock.lock().unwrap(); - let pull = self.pull_lock.lock().unwrap(); - (wait, pull) - } -} - #[napi] pub struct SyncEngine { path: String, @@ -71,7 +39,7 @@ pub struct SyncEngine { use_transform: bool, io: Option>, protocol: Option>, - sync_engine: Arc, + sync_engine: Arc>>>, opened: Arc>>, } @@ -214,13 +182,7 @@ impl SyncEngine { tables_ignore: opts.tables_ignore.unwrap_or_default(), use_transform: opts.use_transform, #[allow(clippy::arc_with_non_send_sync)] - sync_engine: Arc::new(SyncEngineGuard { - inner: Arc::new(RwLock::new(None)), - wait_lock: Mutex::new(()), - push_lock: Mutex::new(()), - pull_lock: Mutex::new(()), - checkpoint_lock: Mutex::new(()), - }), + sync_engine: Arc::new(RwLock::new(None)), io: Some(io), protocol: Some(Arc::new(JsProtocolIo::default())), #[allow(clippy::arc_with_non_send_sync)] @@ -257,7 +219,7 @@ impl SyncEngine { let connection = initialized.connect_rw(&coro).await?; let db = turso_node::Database::create(None, io.clone(), connection, path); - *sync_engine.inner.write().unwrap() = Some(initialized); + *sync_engine.write().unwrap() = Some(initialized); *opened.lock().unwrap() = Some(db); Ok(()) }); @@ -288,22 +250,10 @@ impl SyncEngine { Ok(self.protocol()?.take_request()) } - #[napi] - pub fn sync(&self) -> GeneratorHolder { - self.run(async move |coro, guard| { - let _lock = guard.pull_lock(); - let sync_engine = try_read(&guard.inner)?; - let sync_engine = try_unwrap(&sync_engine)?; - sync_engine.sync(coro).await?; - Ok(None) - }) - } - #[napi] pub fn push(&self) -> GeneratorHolder { self.run(async move |coro, guard| { - let _lock = guard.push_lock(); - let sync_engine = try_read(&guard.inner)?; + let sync_engine = try_read(&guard)?; let sync_engine = try_unwrap(&sync_engine)?; sync_engine.push_changes_to_remote(coro).await?; Ok(None) @@ -313,7 +263,7 @@ impl SyncEngine { #[napi] pub fn stats(&self) -> GeneratorHolder { self.run(async move |coro, guard| { - let sync_engine = try_read(&guard.inner)?; + let sync_engine = try_read(&guard)?; let sync_engine = try_unwrap(&sync_engine)?; let stats = sync_engine.stats(coro).await?; Ok(Some(GeneratorResponse::SyncEngineStats { @@ -328,16 +278,25 @@ impl SyncEngine { } #[napi] - pub fn pull(&self) -> GeneratorHolder { + pub fn wait(&self) -> GeneratorHolder { self.run(async move |coro, guard| { - let sync_engine = try_read(&guard.inner)?; + let sync_engine = try_read(&guard)?; let sync_engine = try_unwrap(&sync_engine)?; - let changes = { - let _lock = guard.wait_lock(); - sync_engine.wait_changes_from_remote(coro).await? - }; - let _lock = guard.pull_lock(); - sync_engine.apply_changes_from_remote(coro, changes).await?; + Ok(Some(GeneratorResponse::SyncEngineChanges { + changes: SyncEngineChanges { + status: Box::new(Some(sync_engine.wait_changes_from_remote(coro).await?)), + }, + })) + }) + } + + #[napi] + pub fn apply(&self, changes: &mut SyncEngineChanges) -> GeneratorHolder { + let status = changes.status.take().unwrap(); + self.run(async move |coro, guard| { + let sync_engine = try_read(&guard)?; + let sync_engine = try_unwrap(&sync_engine)?; + sync_engine.apply_changes_from_remote(coro, status).await?; Ok(None) }) } @@ -345,8 +304,7 @@ impl SyncEngine { #[napi] pub fn checkpoint(&self) -> GeneratorHolder { self.run(async move |coro, guard| { - let _lock = guard.checkpoint_lock(); - let sync_engine = try_read(&guard.inner)?; + let sync_engine = try_read(&guard)?; let sync_engine = try_unwrap(&sync_engine)?; sync_engine.checkpoint(coro).await?; Ok(None) @@ -367,7 +325,7 @@ impl SyncEngine { #[napi] pub fn close(&mut self) { - let _ = self.sync_engine.inner.write().unwrap().take(); + let _ = self.sync_engine.write().unwrap().take(); let _ = self.opened.lock().unwrap().take().unwrap(); let _ = self.io.take(); let _ = self.protocol.take(); @@ -396,7 +354,7 @@ impl SyncEngine { &self, f: impl AsyncFnOnce( &Coro<()>, - &Arc, + &Arc>>>, ) -> turso_sync_engine::Result> + 'static, ) -> GeneratorHolder { diff --git a/bindings/javascript/yarn.lock b/bindings/javascript/yarn.lock index 5dc592472..f79e485bd 100644 --- a/bindings/javascript/yarn.lock +++ b/bindings/javascript/yarn.lock @@ -30,6 +30,13 @@ __metadata: languageName: node linkType: hard +"@drizzle-team/brocli@npm:^0.10.2": + version: 0.10.2 + resolution: "@drizzle-team/brocli@npm:0.10.2" + checksum: 10c0/3d8b99d680f0b14fea32b45c59b938b6665e0840cc67f04801b1aa3c6747da3c7d01c00e321645034fa100abdba7e0c20ce07cf46fc2ca769ee4cafd97562484 + languageName: node + linkType: hard + "@emnapi/core@npm:^1.4.5": version: 1.4.5 resolution: "@emnapi/core@npm:1.4.5" @@ -58,6 +65,26 @@ __metadata: languageName: node linkType: hard +"@esbuild-kit/core-utils@npm:^3.3.2": + version: 3.3.2 + resolution: "@esbuild-kit/core-utils@npm:3.3.2" + dependencies: + esbuild: "npm:~0.18.20" + source-map-support: "npm:^0.5.21" + checksum: 10c0/d856f5bd720814593f911d781ed7558a3f8ec1a39802f3831d0eea0d1306e0e2dc11b7b2443af621c413ec6557f1f3034a9a4f1472a4cb40e52cd6e3b356aa05 + languageName: node + linkType: hard + +"@esbuild-kit/esm-loader@npm:^2.5.5": + version: 2.6.5 + resolution: "@esbuild-kit/esm-loader@npm:2.6.5" + dependencies: + "@esbuild-kit/core-utils": "npm:^3.3.2" + get-tsconfig: "npm:^4.7.0" + checksum: 10c0/6894b29176eda62bdce0d458d57f32daed5cb8fcff14cb3ddfbc995cfe3e2fa8599f3b0b1af66db446903b30167f57069f27e9cf79a69cf9b41f557115811cde + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/aix-ppc64@npm:0.25.9" @@ -65,6 +92,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm64@npm:0.18.20" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/android-arm64@npm:0.25.9" @@ -72,6 +106,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-arm@npm:0.18.20" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/android-arm@npm:0.25.9" @@ -79,6 +120,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/android-x64@npm:0.18.20" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/android-x64@npm:0.25.9" @@ -86,6 +134,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-arm64@npm:0.18.20" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/darwin-arm64@npm:0.25.9" @@ -93,6 +148,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/darwin-x64@npm:0.18.20" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/darwin-x64@npm:0.25.9" @@ -100,6 +162,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-arm64@npm:0.18.20" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/freebsd-arm64@npm:0.25.9" @@ -107,6 +176,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/freebsd-x64@npm:0.18.20" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/freebsd-x64@npm:0.25.9" @@ -114,6 +190,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm64@npm:0.18.20" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/linux-arm64@npm:0.25.9" @@ -121,6 +204,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-arm@npm:0.18.20" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/linux-arm@npm:0.25.9" @@ -128,6 +218,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ia32@npm:0.18.20" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/linux-ia32@npm:0.25.9" @@ -135,6 +232,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-loong64@npm:0.18.20" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/linux-loong64@npm:0.25.9" @@ -142,6 +246,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-mips64el@npm:0.18.20" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/linux-mips64el@npm:0.25.9" @@ -149,6 +260,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-ppc64@npm:0.18.20" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/linux-ppc64@npm:0.25.9" @@ -156,6 +274,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-riscv64@npm:0.18.20" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/linux-riscv64@npm:0.25.9" @@ -163,6 +288,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-s390x@npm:0.18.20" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/linux-s390x@npm:0.25.9" @@ -170,6 +302,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/linux-x64@npm:0.18.20" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/linux-x64@npm:0.25.9" @@ -184,6 +323,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/netbsd-x64@npm:0.18.20" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/netbsd-x64@npm:0.25.9" @@ -198,6 +344,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/openbsd-x64@npm:0.18.20" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/openbsd-x64@npm:0.25.9" @@ -212,6 +365,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/sunos-x64@npm:0.18.20" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/sunos-x64@npm:0.25.9" @@ -219,6 +379,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-arm64@npm:0.18.20" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/win32-arm64@npm:0.25.9" @@ -226,6 +393,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-ia32@npm:0.18.20" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/win32-ia32@npm:0.25.9" @@ -233,6 +407,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.18.20": + version: 0.18.20 + resolution: "@esbuild/win32-x64@npm:0.18.20" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.25.9": version: 0.25.9 resolution: "@esbuild/win32-x64@npm:0.25.9" @@ -1433,6 +1614,9 @@ __metadata: "@napi-rs/cli": "npm:^3.1.5" "@tursodatabase/database-common": "npm:^0.2.0-pre.3" "@types/node": "npm:^24.3.1" + better-sqlite3: "npm:^12.2.0" + drizzle-kit: "npm:^0.31.4" + drizzle-orm: "npm:^0.44.5" typescript: "npm:^5.9.2" vitest: "npm:^3.2.4" languageName: unknown @@ -1458,6 +1642,7 @@ __metadata: version: 0.0.0-use.local resolution: "@tursodatabase/sync-common@workspace:sync/packages/common" dependencies: + "@tursodatabase/database-common": "npm:^0.2.0-pre.3" typescript: "npm:^5.9.2" languageName: unknown linkType: soft @@ -1680,16 +1865,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^4.0.0": - version: 4.3.0 - resolution: "ansi-styles@npm:4.3.0" - dependencies: - color-convert: "npm:^2.0.1" - checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 - languageName: node - linkType: hard - -"ansi-styles@npm:^5.0.0": +"ansi-styles@npm:^4.0.0, ansi-styles@npm:^5.0.0": version: 5.2.0 resolution: "ansi-styles@npm:5.2.0" checksum: 10c0/9c4ca80eb3c2fb7b33841c210d2f20807f40865d27008d7c3f707b7f95cab7d67462a565e2388ac3285b71cb3d9bb2173de8da37c57692a362885ec34d6e27df @@ -1733,6 +1909,13 @@ __metadata: languageName: node linkType: hard +"base64-js@npm:^1.3.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf + languageName: node + linkType: hard + "before-after-hook@npm:^4.0.0": version: 4.0.0 resolution: "before-after-hook@npm:4.0.0" @@ -1740,6 +1923,37 @@ __metadata: languageName: node linkType: hard +"better-sqlite3@npm:^12.2.0": + version: 12.2.0 + resolution: "better-sqlite3@npm:12.2.0" + dependencies: + bindings: "npm:^1.5.0" + node-gyp: "npm:latest" + prebuild-install: "npm:^7.1.1" + checksum: 10c0/842247e9bbb775f366ac91f604117112c312497e643bac21648d8b69f479763de0ac049b14b609d6d5ecaee50debcc09a854f682d3dc099a1d933fea92ce68d0 + languageName: node + linkType: hard + +"bindings@npm:^1.5.0": + version: 1.5.0 + resolution: "bindings@npm:1.5.0" + dependencies: + file-uri-to-path: "npm:1.0.0" + checksum: 10c0/3dab2491b4bb24124252a91e656803eac24292473e56554e35bbfe3cc1875332cfa77600c3bac7564049dc95075bf6fcc63a4609920ff2d64d0fe405fcf0d4ba + languageName: node + linkType: hard + +"bl@npm:^4.0.3": + version: 4.1.0 + resolution: "bl@npm:4.1.0" + dependencies: + buffer: "npm:^5.5.0" + inherits: "npm:^2.0.4" + readable-stream: "npm:^3.4.0" + checksum: 10c0/02847e1d2cb089c9dc6958add42e3cdeaf07d13f575973963335ac0fdece563a50ac770ac4c8fa06492d2dd276f6cc3b7f08c7cd9c7a7ad0f8d388b2a28def5f + languageName: node + linkType: hard + "brace-expansion@npm:^2.0.1": version: 2.0.2 resolution: "brace-expansion@npm:2.0.2" @@ -1749,6 +1963,23 @@ __metadata: languageName: node linkType: hard +"buffer-from@npm:^1.0.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 10c0/124fff9d66d691a86d3b062eff4663fe437a9d9ee4b47b1b9e97f5a5d14f6d5399345db80f796827be7c95e70a8e765dd404b7c3ff3b3324f98e9b0c8826cc34 + languageName: node + linkType: hard + +"buffer@npm:^5.5.0": + version: 5.7.1 + resolution: "buffer@npm:5.7.1" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.1.13" + checksum: 10c0/27cac81cff434ed2876058d72e7c4789d11ff1120ef32c9de48f59eab58179b66710c488987d295ae89a228f835fc66d088652dffeb8e3ba8659f80eb091d55e + languageName: node + linkType: hard + "cac@npm:^6.7.14": version: 6.7.14 resolution: "cac@npm:6.7.14" @@ -1803,6 +2034,13 @@ __metadata: languageName: node linkType: hard +"chownr@npm:^1.1.1": + version: 1.1.4 + resolution: "chownr@npm:1.1.4" + checksum: 10c0/ed57952a84cc0c802af900cf7136de643d3aba2eecb59d29344bc2f3f9bf703a301b9d84cdc71f82c3ffc9ccde831b0d92f5b45f91727d6c9da62f23aef9d9db + languageName: node + linkType: hard + "chownr@npm:^3.0.0": version: 3.0.0 resolution: "chownr@npm:3.0.0" @@ -1828,22 +2066,6 @@ __metadata: languageName: node linkType: hard -"color-convert@npm:^2.0.1": - version: 2.0.1 - resolution: "color-convert@npm:2.0.1" - dependencies: - color-name: "npm:~1.1.4" - checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 - languageName: node - linkType: hard - -"color-name@npm:~1.1.4": - version: 1.1.4 - resolution: "color-name@npm:1.1.4" - checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 - languageName: node - linkType: hard - "colorette@npm:^2.0.20": version: 2.0.20 resolution: "colorette@npm:2.0.20" @@ -1862,7 +2084,19 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.3.4, debug@npm:^4.4.0, debug@npm:^4.4.1": +"debug@npm:4": + version: 4.4.3 + resolution: "debug@npm:4.4.3" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 + languageName: node + linkType: hard + +"debug@npm:^4.3.4, debug@npm:^4.4.0, debug@npm:^4.4.1": version: 4.4.1 resolution: "debug@npm:4.4.1" dependencies: @@ -1874,6 +2108,15 @@ __metadata: languageName: node linkType: hard +"decompress-response@npm:^6.0.0": + version: 6.0.0 + resolution: "decompress-response@npm:6.0.0" + dependencies: + mimic-response: "npm:^3.1.0" + checksum: 10c0/bd89d23141b96d80577e70c54fb226b2f40e74a6817652b80a116d7befb8758261ad073a8895648a29cc0a5947021ab66705cb542fa9c143c82022b27c5b175e + languageName: node + linkType: hard + "deep-eql@npm:^5.0.1": version: 5.0.2 resolution: "deep-eql@npm:5.0.2" @@ -1881,6 +2124,13 @@ __metadata: languageName: node linkType: hard +"deep-extend@npm:^0.6.0": + version: 0.6.0 + resolution: "deep-extend@npm:0.6.0" + checksum: 10c0/1c6b0abcdb901e13a44c7d699116d3d4279fdb261983122a3783e7273844d5f2537dc2e1c454a23fcf645917f93fbf8d07101c1d03c015a87faa662755212566 + languageName: node + linkType: hard + "dequal@npm:^2.0.3": version: 2.0.3 resolution: "dequal@npm:2.0.3" @@ -1888,6 +2138,13 @@ __metadata: languageName: node linkType: hard +"detect-libc@npm:^2.0.0": + version: 2.1.0 + resolution: "detect-libc@npm:2.1.0" + checksum: 10c0/4d0d36c77fdcb1d3221779d8dfc7d5808dd52530d49db67193fb3cd8149e2d499a1eeb87bb830ad7c442294929992c12e971f88ae492965549f8f83e5336eba6 + languageName: node + linkType: hard + "dom-accessibility-api@npm:^0.5.9": version: 0.5.16 resolution: "dom-accessibility-api@npm:0.5.16" @@ -1895,6 +2152,115 @@ __metadata: languageName: node linkType: hard +"drizzle-kit@npm:^0.31.4": + version: 0.31.4 + resolution: "drizzle-kit@npm:0.31.4" + dependencies: + "@drizzle-team/brocli": "npm:^0.10.2" + "@esbuild-kit/esm-loader": "npm:^2.5.5" + esbuild: "npm:^0.25.4" + esbuild-register: "npm:^3.5.0" + bin: + drizzle-kit: bin.cjs + checksum: 10c0/5e345cb28b4b8f329ce5f851e47418ac2ee8189aecec85f566f7a6c309f3392613519a39c559618599bd1e63fb99f114b9d9d82fb9e411f1702425678f34d2c2 + languageName: node + linkType: hard + +"drizzle-orm@npm:^0.44.5": + version: 0.44.5 + resolution: "drizzle-orm@npm:0.44.5" + peerDependencies: + "@aws-sdk/client-rds-data": ">=3" + "@cloudflare/workers-types": ">=4" + "@electric-sql/pglite": ">=0.2.0" + "@libsql/client": ">=0.10.0" + "@libsql/client-wasm": ">=0.10.0" + "@neondatabase/serverless": ">=0.10.0" + "@op-engineering/op-sqlite": ">=2" + "@opentelemetry/api": ^1.4.1 + "@planetscale/database": ">=1.13" + "@prisma/client": "*" + "@tidbcloud/serverless": "*" + "@types/better-sqlite3": "*" + "@types/pg": "*" + "@types/sql.js": "*" + "@upstash/redis": ">=1.34.7" + "@vercel/postgres": ">=0.8.0" + "@xata.io/client": "*" + better-sqlite3: ">=7" + bun-types: "*" + expo-sqlite: ">=14.0.0" + gel: ">=2" + knex: "*" + kysely: "*" + mysql2: ">=2" + pg: ">=8" + postgres: ">=3" + sql.js: ">=1" + sqlite3: ">=5" + peerDependenciesMeta: + "@aws-sdk/client-rds-data": + optional: true + "@cloudflare/workers-types": + optional: true + "@electric-sql/pglite": + optional: true + "@libsql/client": + optional: true + "@libsql/client-wasm": + optional: true + "@neondatabase/serverless": + optional: true + "@op-engineering/op-sqlite": + optional: true + "@opentelemetry/api": + optional: true + "@planetscale/database": + optional: true + "@prisma/client": + optional: true + "@tidbcloud/serverless": + optional: true + "@types/better-sqlite3": + optional: true + "@types/pg": + optional: true + "@types/sql.js": + optional: true + "@upstash/redis": + optional: true + "@vercel/postgres": + optional: true + "@xata.io/client": + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + checksum: 10c0/2f9bd8cc7395b3254574eb9e9c344b7cebd507ac61f1ee8783648ad3bb8a7983875f44c0eabedfd871496d7eae646dbc75111fa21de2c64d0c899fcea091e303 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -1937,6 +2303,15 @@ __metadata: languageName: node linkType: hard +"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": + version: 1.4.5 + resolution: "end-of-stream@npm:1.4.5" + dependencies: + once: "npm:^1.4.0" + checksum: 10c0/b0701c92a10b89afb1cb45bf54a5292c6f008d744eb4382fa559d54775ff31617d1d7bc3ef617575f552e24fad2c7c1a1835948c66b3f3a4be0a6c1f35c883d8 + languageName: node + linkType: hard + "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -1970,7 +2345,18 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.25.0": +"esbuild-register@npm:^3.5.0": + version: 3.6.0 + resolution: "esbuild-register@npm:3.6.0" + dependencies: + debug: "npm:^4.3.4" + peerDependencies: + esbuild: ">=0.12 <1" + checksum: 10c0/77193b7ca32ba9f81b35ddf3d3d0138efb0b1429d71b39480cfee932e1189dd2e492bd32bf04a4d0bc3adfbc7ec7381ceb5ffd06efe35f3e70904f1f686566d5 + languageName: node + linkType: hard + +"esbuild@npm:^0.25.0, esbuild@npm:^0.25.4": version: 0.25.9 resolution: "esbuild@npm:0.25.9" dependencies: @@ -2059,6 +2445,83 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:~0.18.20": + version: 0.18.20 + resolution: "esbuild@npm:0.18.20" + dependencies: + "@esbuild/android-arm": "npm:0.18.20" + "@esbuild/android-arm64": "npm:0.18.20" + "@esbuild/android-x64": "npm:0.18.20" + "@esbuild/darwin-arm64": "npm:0.18.20" + "@esbuild/darwin-x64": "npm:0.18.20" + "@esbuild/freebsd-arm64": "npm:0.18.20" + "@esbuild/freebsd-x64": "npm:0.18.20" + "@esbuild/linux-arm": "npm:0.18.20" + "@esbuild/linux-arm64": "npm:0.18.20" + "@esbuild/linux-ia32": "npm:0.18.20" + "@esbuild/linux-loong64": "npm:0.18.20" + "@esbuild/linux-mips64el": "npm:0.18.20" + "@esbuild/linux-ppc64": "npm:0.18.20" + "@esbuild/linux-riscv64": "npm:0.18.20" + "@esbuild/linux-s390x": "npm:0.18.20" + "@esbuild/linux-x64": "npm:0.18.20" + "@esbuild/netbsd-x64": "npm:0.18.20" + "@esbuild/openbsd-x64": "npm:0.18.20" + "@esbuild/sunos-x64": "npm:0.18.20" + "@esbuild/win32-arm64": "npm:0.18.20" + "@esbuild/win32-ia32": "npm:0.18.20" + "@esbuild/win32-x64": "npm:0.18.20" + dependenciesMeta: + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 10c0/473b1d92842f50a303cf948a11ebd5f69581cd254d599dd9d62f9989858e0533f64e83b723b5e1398a5b488c0f5fd088795b4235f65ecaf4f007d4b79f04bc88 + languageName: node + linkType: hard + "estree-walker@npm:^3.0.3": version: 3.0.3 resolution: "estree-walker@npm:3.0.3" @@ -2068,6 +2531,13 @@ __metadata: languageName: node linkType: hard +"expand-template@npm:^2.0.3": + version: 2.0.3 + resolution: "expand-template@npm:2.0.3" + checksum: 10c0/1c9e7afe9acadf9d373301d27f6a47b34e89b3391b1ef38b7471d381812537ef2457e620ae7f819d2642ce9c43b189b3583813ec395e2938319abe356a9b2f51 + languageName: node + linkType: hard + "expect-type@npm:^1.2.1": version: 1.2.2 resolution: "expect-type@npm:1.2.2" @@ -2112,6 +2582,13 @@ __metadata: languageName: node linkType: hard +"file-uri-to-path@npm:1.0.0": + version: 1.0.0 + resolution: "file-uri-to-path@npm:1.0.0" + checksum: 10c0/3b545e3a341d322d368e880e1c204ef55f1d45cdea65f7efc6c6ce9e0c4d22d802d5629320eb779d006fe59624ac17b0e848d83cc5af7cd101f206cb704f5519 + languageName: node + linkType: hard + "find-up@npm:^7.0.0": version: 7.0.0 resolution: "find-up@npm:7.0.0" @@ -2133,6 +2610,13 @@ __metadata: languageName: node linkType: hard +"fs-constants@npm:^1.0.0": + version: 1.0.0 + resolution: "fs-constants@npm:1.0.0" + checksum: 10c0/a0cde99085f0872f4d244e83e03a46aa387b74f5a5af750896c6b05e9077fac00e9932fdf5aef84f2f16634cd473c63037d7a512576da7d5c2b9163d1909f3a8 + languageName: node + linkType: hard + "fs-minipass@npm:^3.0.0": version: 3.0.3 resolution: "fs-minipass@npm:3.0.3" @@ -2180,6 +2664,22 @@ __metadata: languageName: node linkType: hard +"get-tsconfig@npm:^4.7.0": + version: 4.10.1 + resolution: "get-tsconfig@npm:4.10.1" + dependencies: + resolve-pkg-maps: "npm:^1.0.0" + checksum: 10c0/7f8e3dabc6a49b747920a800fb88e1952fef871cdf51b79e98db48275a5de6cdaf499c55ee67df5fa6fe7ce65f0063e26de0f2e53049b408c585aa74d39ffa21 + languageName: node + linkType: hard + +"github-from-package@npm:0.0.0": + version: 0.0.0 + resolution: "github-from-package@npm:0.0.0" + checksum: 10c0/737ee3f52d0a27e26332cde85b533c21fcdc0b09fb716c3f8e522cfaa9c600d4a631dec9fcde179ec9d47cca89017b7848ed4d6ae6b6b78f936c06825b1fcc12 + languageName: node + linkType: hard + "glob@npm:^10.2.2": version: 10.4.5 resolution: "glob@npm:10.4.5" @@ -2248,6 +2748,13 @@ __metadata: languageName: node linkType: hard +"ieee754@npm:^1.1.13": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb + languageName: node + linkType: hard + "imurmurhash@npm:^0.1.4": version: 0.1.4 resolution: "imurmurhash@npm:0.1.4" @@ -2255,6 +2762,20 @@ __metadata: languageName: node linkType: hard +"inherits@npm:^2.0.3, inherits@npm:^2.0.4": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 + languageName: node + linkType: hard + +"ini@npm:~1.3.0": + version: 1.3.8 + resolution: "ini@npm:1.3.8" + checksum: 10c0/ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a + languageName: node + linkType: hard + "ip-address@npm:^10.0.1": version: 10.0.1 resolution: "ip-address@npm:10.0.1" @@ -2381,6 +2902,13 @@ __metadata: languageName: node linkType: hard +"mimic-response@npm:^3.1.0": + version: 3.1.0 + resolution: "mimic-response@npm:3.1.0" + checksum: 10c0/0d6f07ce6e03e9e4445bee655202153bdb8a98d67ee8dc965ac140900d7a2688343e6b4c9a72cfc9ef2f7944dfd76eef4ab2482eb7b293a68b84916bac735362 + languageName: node + linkType: hard + "minimatch@npm:^9.0.4": version: 9.0.5 resolution: "minimatch@npm:9.0.5" @@ -2390,6 +2918,13 @@ __metadata: languageName: node linkType: hard +"minimist@npm:^1.2.0, minimist@npm:^1.2.3": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 + languageName: node + linkType: hard + "minipass-collect@npm:^2.0.1": version: 2.0.1 resolution: "minipass-collect@npm:2.0.1" @@ -2466,6 +3001,13 @@ __metadata: languageName: node linkType: hard +"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3": + version: 0.5.3 + resolution: "mkdirp-classic@npm:0.5.3" + checksum: 10c0/95371d831d196960ddc3833cc6907e6b8f67ac5501a6582f47dfae5eb0f092e9f8ce88e0d83afcae95d6e2b61a01741ba03714eeafb6f7a6e9dcc158ac85b168 + languageName: node + linkType: hard + "mkdirp@npm:^3.0.1": version: 3.0.1 resolution: "mkdirp@npm:3.0.1" @@ -2505,6 +3047,13 @@ __metadata: languageName: node linkType: hard +"napi-build-utils@npm:^2.0.0": + version: 2.0.0 + resolution: "napi-build-utils@npm:2.0.0" + checksum: 10c0/5833aaeb5cc5c173da47a102efa4680a95842c13e0d9cc70428bd3ee8d96bb2172f8860d2811799b5daa5cbeda779933601492a2028a6a5351c6d0fcf6de83db + languageName: node + linkType: hard + "negotiator@npm:^1.0.0": version: 1.0.0 resolution: "negotiator@npm:1.0.0" @@ -2512,6 +3061,15 @@ __metadata: languageName: node linkType: hard +"node-abi@npm:^3.3.0": + version: 3.77.0 + resolution: "node-abi@npm:3.77.0" + dependencies: + semver: "npm:^7.3.5" + checksum: 10c0/3354289ccca052538f653968ead73d00785e5ab159ce3a575dbff465724dac749821e7c327ae6c4774f29994f94c402fbafc8799b172aabf4aa8a082a070b00a + languageName: node + linkType: hard + "node-gyp@npm:latest": version: 11.4.2 resolution: "node-gyp@npm:11.4.2" @@ -2543,6 +3101,15 @@ __metadata: languageName: node linkType: hard +"once@npm:^1.3.1, once@npm:^1.4.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: "npm:1" + checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0 + languageName: node + linkType: hard + "os-tmpdir@npm:~1.0.2": version: 1.0.2 resolution: "os-tmpdir@npm:1.0.2" @@ -2669,6 +3236,28 @@ __metadata: languageName: node linkType: hard +"prebuild-install@npm:^7.1.1": + version: 7.1.3 + resolution: "prebuild-install@npm:7.1.3" + dependencies: + detect-libc: "npm:^2.0.0" + expand-template: "npm:^2.0.3" + github-from-package: "npm:0.0.0" + minimist: "npm:^1.2.3" + mkdirp-classic: "npm:^0.5.3" + napi-build-utils: "npm:^2.0.0" + node-abi: "npm:^3.3.0" + pump: "npm:^3.0.0" + rc: "npm:^1.2.7" + simple-get: "npm:^4.0.0" + tar-fs: "npm:^2.0.0" + tunnel-agent: "npm:^0.6.0" + bin: + prebuild-install: bin.js + checksum: 10c0/25919a42b52734606a4036ab492d37cfe8b601273d8dfb1fa3c84e141a0a475e7bad3ab848c741d2f810cef892fcf6059b8c7fe5b29f98d30e0c29ad009bedff + languageName: node + linkType: hard + "pretty-format@npm:^27.0.2": version: 27.5.1 resolution: "pretty-format@npm:27.5.1" @@ -2697,6 +3286,30 @@ __metadata: languageName: node linkType: hard +"pump@npm:^3.0.0": + version: 3.0.3 + resolution: "pump@npm:3.0.3" + dependencies: + end-of-stream: "npm:^1.1.0" + once: "npm:^1.3.1" + checksum: 10c0/ada5cdf1d813065bbc99aa2c393b8f6beee73b5de2890a8754c9f488d7323ffd2ca5f5a0943b48934e3fcbd97637d0337369c3c631aeb9614915db629f1c75c9 + languageName: node + linkType: hard + +"rc@npm:^1.2.7": + version: 1.2.8 + resolution: "rc@npm:1.2.8" + dependencies: + deep-extend: "npm:^0.6.0" + ini: "npm:~1.3.0" + minimist: "npm:^1.2.0" + strip-json-comments: "npm:~2.0.1" + bin: + rc: ./cli.js + checksum: 10c0/24a07653150f0d9ac7168e52943cc3cb4b7a22c0e43c7dff3219977c2fdca5a2760a304a029c20811a0e79d351f57d46c9bde216193a0f73978496afc2b85b15 + languageName: node + linkType: hard + "react-is@npm:^17.0.1": version: 17.0.2 resolution: "react-is@npm:17.0.2" @@ -2704,6 +3317,24 @@ __metadata: languageName: node linkType: hard +"readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0": + version: 3.6.2 + resolution: "readable-stream@npm:3.6.2" + dependencies: + inherits: "npm:^2.0.3" + string_decoder: "npm:^1.1.1" + util-deprecate: "npm:^1.0.1" + checksum: 10c0/e37be5c79c376fdd088a45fa31ea2e423e5d48854be7a22a58869b4e84d25047b193f6acb54f1012331e1bcd667ffb569c01b99d36b0bd59658fb33f513511b7 + languageName: node + linkType: hard + +"resolve-pkg-maps@npm:^1.0.0": + version: 1.0.0 + resolution: "resolve-pkg-maps@npm:1.0.0" + checksum: 10c0/fb8f7bbe2ca281a73b7ef423a1cbc786fb244bd7a95cbe5c3fba25b27d327150beca8ba02f622baea65919a57e061eb5005204daa5f93ed590d9b77463a567ab + languageName: node + linkType: hard + "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -2795,6 +3426,13 @@ __metadata: languageName: unknown linkType: soft +"safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 + languageName: node + linkType: hard + "safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -2841,6 +3479,24 @@ __metadata: languageName: node linkType: hard +"simple-concat@npm:^1.0.0": + version: 1.0.1 + resolution: "simple-concat@npm:1.0.1" + checksum: 10c0/62f7508e674414008910b5397c1811941d457dfa0db4fd5aa7fa0409eb02c3609608dfcd7508cace75b3a0bf67a2a77990711e32cd213d2c76f4fd12ee86d776 + languageName: node + linkType: hard + +"simple-get@npm:^4.0.0": + version: 4.0.1 + resolution: "simple-get@npm:4.0.1" + dependencies: + decompress-response: "npm:^6.0.0" + once: "npm:^1.3.1" + simple-concat: "npm:^1.0.0" + checksum: 10c0/b0649a581dbca741babb960423248899203165769747142033479a7dc5e77d7b0fced0253c731cd57cf21e31e4d77c9157c3069f4448d558ebc96cf9e1eebcf0 + languageName: node + linkType: hard + "sirv@npm:^3.0.1": version: 3.0.2 resolution: "sirv@npm:3.0.2" @@ -2887,6 +3543,23 @@ __metadata: languageName: node linkType: hard +"source-map-support@npm:^0.5.21": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 10c0/9ee09942f415e0f721d6daad3917ec1516af746a8120bba7bb56278707a37f1eb8642bde456e98454b8a885023af81a16e646869975f06afc1a711fb90484e7d + languageName: node + linkType: hard + +"source-map@npm:^0.6.0": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + "ssri@npm:^12.0.0": version: 12.0.0 resolution: "ssri@npm:12.0.0" @@ -2932,6 +3605,15 @@ __metadata: languageName: node linkType: hard +"string_decoder@npm:^1.1.1": + version: 1.3.0 + resolution: "string_decoder@npm:1.3.0" + dependencies: + safe-buffer: "npm:~5.2.0" + checksum: 10c0/810614ddb030e271cd591935dcd5956b2410dd079d64ff92a1844d6b7588bf992b3e1b69b0f4d34a3e06e0bd73046ac646b5264c1987b20d0601f81ef35d731d + languageName: node + linkType: hard + "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -2950,6 +3632,13 @@ __metadata: languageName: node linkType: hard +"strip-json-comments@npm:~2.0.1": + version: 2.0.1 + resolution: "strip-json-comments@npm:2.0.1" + checksum: 10c0/b509231cbdee45064ff4f9fd73609e2bcc4e84a4d508e9dd0f31f70356473fde18abfb5838c17d56fb236f5a06b102ef115438de0600b749e818a35fbbc48c43 + languageName: node + linkType: hard + "strip-literal@npm:^3.0.0": version: 3.0.0 resolution: "strip-literal@npm:3.0.0" @@ -2959,6 +3648,31 @@ __metadata: languageName: node linkType: hard +"tar-fs@npm:^2.0.0": + version: 2.1.4 + resolution: "tar-fs@npm:2.1.4" + dependencies: + chownr: "npm:^1.1.1" + mkdirp-classic: "npm:^0.5.2" + pump: "npm:^3.0.0" + tar-stream: "npm:^2.1.4" + checksum: 10c0/decb25acdc6839182c06ec83cba6136205bda1db984e120c8ffd0d80182bc5baa1d916f9b6c5c663ea3f9975b4dd49e3c6bb7b1707cbcdaba4e76042f43ec84c + languageName: node + linkType: hard + +"tar-stream@npm:^2.1.4": + version: 2.2.0 + resolution: "tar-stream@npm:2.2.0" + dependencies: + bl: "npm:^4.0.3" + end-of-stream: "npm:^1.4.1" + fs-constants: "npm:^1.0.0" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.1.1" + checksum: 10c0/2f4c910b3ee7196502e1ff015a7ba321ec6ea837667220d7bcb8d0852d51cb04b87f7ae471008a6fb8f5b1a1b5078f62f3a82d30c706f20ada1238ac797e7692 + languageName: node + linkType: hard + "tar@npm:^7.4.3": version: 7.4.3 resolution: "tar@npm:7.4.3" @@ -3041,6 +3755,15 @@ __metadata: languageName: node linkType: hard +"tunnel-agent@npm:^0.6.0": + version: 0.6.0 + resolution: "tunnel-agent@npm:0.6.0" + dependencies: + safe-buffer: "npm:^5.0.1" + checksum: 10c0/4c7a1b813e7beae66fdbf567a65ec6d46313643753d0beefb3c7973d66fcec3a1e7f39759f0a0b4465883499c6dc8b0750ab8b287399af2e583823e40410a17a + languageName: node + linkType: hard + "typanion@npm:^3.14.0, typanion@npm:^3.8.0": version: 3.14.0 resolution: "typanion@npm:3.14.0" @@ -3114,6 +3837,13 @@ __metadata: languageName: node linkType: hard +"util-deprecate@npm:^1.0.1": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 + languageName: node + linkType: hard + "vite-node@npm:3.2.4": version: 3.2.4 resolution: "vite-node@npm:3.2.4" @@ -3307,6 +4037,13 @@ __metadata: languageName: node linkType: hard +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0 + languageName: node + linkType: hard + "ws@npm:^8.18.2": version: 8.18.3 resolution: "ws@npm:8.18.3" From 1d3c823c7b83443144c5234751119aee005b65dc Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Thu, 18 Sep 2025 01:35:09 +0400 Subject: [PATCH 09/14] wip --- .../javascript/packages/common/promise.ts | 38 ++++------- .../packages/native/promise.test.ts | 2 +- bindings/javascript/src/lib.rs | 13 +++- bindings/javascript/sync/packages/browser/a | Bin 57344 -> 0 bytes .../javascript/sync/packages/browser/a-shm | Bin 32768 -> 0 bytes .../sync/packages/browser/package.json | 2 +- .../sync/packages/browser/promise.test.ts | 61 ++++++++++++------ .../sync/packages/native/index.d.ts | 16 ----- .../javascript/sync/packages/native/index.js | 3 - .../sync/packages/native/package.json | 2 +- bindings/javascript/sync/src/lib.rs | 4 +- 11 files changed, 72 insertions(+), 69 deletions(-) delete mode 100755 bindings/javascript/sync/packages/browser/a delete mode 100755 bindings/javascript/sync/packages/browser/a-shm diff --git a/bindings/javascript/packages/common/promise.ts b/bindings/javascript/packages/common/promise.ts index 8dbcf5058..88ccb802c 100644 --- a/bindings/javascript/packages/common/promise.ts +++ b/bindings/javascript/packages/common/promise.ts @@ -115,22 +115,17 @@ class Database { const db = this; const wrapTxn = (mode) => { return async (...bindParameters) => { - await this.execLock.acquire(); + await db.exec("BEGIN " + mode); + db._inTransaction = true; try { - await db.exec("BEGIN " + mode); - db._inTransaction = true; - try { - const result = await fn(...bindParameters); - await db.exec("COMMIT"); - db._inTransaction = false; - return result; - } catch (err) { - await db.exec("ROLLBACK"); - db._inTransaction = false; - throw err; - } - } finally { - this.execLock.release(); + const result = await fn(...bindParameters); + await db.exec("COMMIT"); + db._inTransaction = false; + return result; + } catch (err) { + await db.exec("ROLLBACK"); + db._inTransaction = false; + throw err; } }; }; @@ -203,18 +198,11 @@ class Database { throw new TypeError("The database connection is not open"); } - await this.execLock.acquire(); + const stmt = this.prepare(sql); try { - const stmt = this.prepare(sql); - try { - await stmt.run(); - } finally { - stmt.close(); - } - } catch (err) { - throw convertError(err); + await stmt.run(); } finally { - this.execLock.release(); + stmt.close(); } } diff --git a/bindings/javascript/packages/native/promise.test.ts b/bindings/javascript/packages/native/promise.test.ts index 5819c8f39..a554154cd 100644 --- a/bindings/javascript/packages/native/promise.test.ts +++ b/bindings/javascript/packages/native/promise.test.ts @@ -22,7 +22,7 @@ test('drizzle-orm', async () => { } }) -test('in-memory db', async () => { +test('in-memory-db-async', async () => { const db = await connect(":memory:"); await db.exec("CREATE TABLE t(x)"); await db.exec("INSERT INTO t VALUES (1), (2), (3)"); diff --git a/bindings/javascript/src/lib.rs b/bindings/javascript/src/lib.rs index 3a9970680..ff910b7f2 100644 --- a/bindings/javascript/src/lib.rs +++ b/bindings/javascript/src/lib.rs @@ -10,9 +10,9 @@ //! - Iterating through query results //! - Managing the I/O event loop -// #[cfg(feature = "browser")] +#[cfg(feature = "browser")] pub mod browser; -// #[cfg(feature = "browser")] +#[cfg(feature = "browser")] use crate::browser::opfs; use napi::bindgen_prelude::*; @@ -62,6 +62,8 @@ pub(crate) fn init_tracing(level_filter: Option) { return; }; let level_filter = match level_filter.as_ref() { + "error" => LevelFilter::ERROR, + "warn" => LevelFilter::WARN, "info" => LevelFilter::INFO, "debug" => LevelFilter::DEBUG, "trace" => LevelFilter::TRACE, @@ -596,7 +598,12 @@ impl Statement { /// Finalizes the statement. #[napi] pub fn finalize(&self) -> Result<()> { - self.stmt.borrow_mut().take(); + match self.stmt.try_borrow_mut() { + Ok(mut stmt) => { + stmt.take(); + } + Err(err) => tracing::error!("borrow error: {:?}", err), + } Ok(()) } } diff --git a/bindings/javascript/sync/packages/browser/a b/bindings/javascript/sync/packages/browser/a deleted file mode 100755 index 8f5630c076b124960766842e96d6e1c9609bce89..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57344 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 X`wJoW7eekYgxp^UxxWxL?JxWX%{~pb diff --git a/bindings/javascript/sync/packages/browser/a-shm b/bindings/javascript/sync/packages/browser/a-shm deleted file mode 100755 index 7c251555d016a9c5a07e151b6fff76239b1cde71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 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 N1Uv#Bf&VK6{s;Wz7XknP diff --git a/bindings/javascript/sync/packages/browser/package.json b/bindings/javascript/sync/packages/browser/package.json index efddc0a70..e09740154 100644 --- a/bindings/javascript/sync/packages/browser/package.json +++ b/bindings/javascript/sync/packages/browser/package.json @@ -42,7 +42,7 @@ "tsc-build": "npm exec tsc && cp sync.wasm32-wasi.wasm ./dist/sync.wasm32-wasi.wasm && WASM_FILE=sync.wasm32-wasi.wasm JS_FILE=./dist/wasm-inline.js node ../../../scripts/inline-wasm-base64.js && npm run bundle", "bundle": "vite build", "build": "npm run napi-build && npm run tsc-build", - "test": "VITE_TURSO_DB_URL=http://c--a--a.localhost:10000 CI=1 vitest --testTimeout 30000 --browser=chromium --run && VITE_TURSO_DB_URL=http://c--a--a.localhost:10000 CI=1 vitest --testTimeout 30000 --browser=firefox --run" + "test": "VITE_TURSO_DB_URL=http://f--a--a.localhost:10000 CI=1 vitest --testTimeout 30000 --browser=chromium --run && VITE_TURSO_DB_URL=http://f--a--a.localhost:10000 CI=1 vitest --testTimeout 30000 --browser=firefox --run" }, "napi": { "binaryName": "sync", diff --git a/bindings/javascript/sync/packages/browser/promise.test.ts b/bindings/javascript/sync/packages/browser/promise.test.ts index fb60ff61b..ff271d0e6 100644 --- a/bindings/javascript/sync/packages/browser/promise.test.ts +++ b/bindings/javascript/sync/packages/browser/promise.test.ts @@ -316,47 +316,72 @@ test('pull-push-concurrent', async () => { console.info(await db.stats()); }) -test('concurrent-updates', async () => { +test('concurrent-updates', { timeout: 60000 }, async () => { { - const db = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL, longPollTimeoutMs: 5000 }); - await db.exec("CREATE TABLE IF NOT EXISTS q(x TEXT PRIMARY KEY, y)"); - await db.exec("DELETE FROM q"); + const db = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL, longPollTimeoutMs: 10 }); + await db.exec("CREATE TABLE IF NOT EXISTS three(x TEXT PRIMARY KEY, y, z)"); + await db.exec("DELETE FROM three"); await db.push(); await db.close(); } - const db1 = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL }); - const db2 = await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL }); - async function pull(db) { + let stop = false; + const dbs = []; + for (let i = 0; i < 8; i++) { + dbs.push(await connect({ path: ':memory:', url: process.env.VITE_TURSO_DB_URL })); + } + async function pull(db, i) { try { + console.info('pull', i); await db.pull(); } catch (e) { - // ignore + console.error('pull', i, e); } finally { - setTimeout(async () => await pull(db), 0); + if (!stop) { + setTimeout(async () => await pull(db, i), 0); + } } } - async function push(db) { + async function push(db, i) { try { + console.info('push', i); await db.push(); } catch (e) { - // ignore + console.error('push', i, e); } finally { - setTimeout(async () => await push(db), 0); + if (!stop) { + setTimeout(async () => await push(db, i), 0); + } } } - setTimeout(async () => await pull(db1), 0) - setTimeout(async () => await pull(db2), 0) - setTimeout(async () => await push(db1), 0) - setTimeout(async () => await push(db2), 0) + for (let i = 0; i < dbs.length; i++) { + setTimeout(async () => await pull(dbs[i], i), 0) + setTimeout(async () => await push(dbs[i], i), 0) + } for (let i = 0; i < 1000; i++) { try { - await db1.exec(`INSERT INTO q VALUES ('1', 0) ON CONFLICT DO UPDATE SET y = randomblob(128)`); - await db2.exec(`INSERT INTO q VALUES ('2', 0) ON CONFLICT DO UPDATE SET y = randomblob(128)`); + const tasks = []; + for (let s = 0; s < dbs.length; s++) { + tasks.push(dbs[s].exec(`INSERT INTO three VALUES ('${s}', 0, randomblob(128)) ON CONFLICT DO UPDATE SET y = y + 1, z = randomblob(128)`)); + } + await Promise.all(tasks); } catch (e) { // ignore } await new Promise(resolve => setTimeout(resolve, 1)); } + stop = true; + await Promise.all(dbs.map(db => db.push())); + await Promise.all(dbs.map(db => db.pull())); + let results = []; + for (let i = 0; i < dbs.length; i++) { + results.push(await dbs[i].prepare('SELECT x, y FROM three').all()); + } + for (let i = 0; i < dbs.length; i++) { + expect(results[i]).toEqual(results[0]); + for (let s = 0; s < dbs.length; s++) { + expect(results[i][s].y).toBeGreaterThan(500); + } + } }) test('transform', async () => { diff --git a/bindings/javascript/sync/packages/native/index.d.ts b/bindings/javascript/sync/packages/native/index.d.ts index 4d1b45fa9..af73101f3 100644 --- a/bindings/javascript/sync/packages/native/index.d.ts +++ b/bindings/javascript/sync/packages/native/index.d.ts @@ -73,16 +73,6 @@ export declare class Database { ioLoopAsync(): Promise } -export declare class Opfs { - constructor() - connectDb(path: string, opts?: DatabaseOpts | undefined | null): Promise - complete(completionNo: number, result: number): void -} - -export declare class OpfsFile { - -} - /** A prepared statement. */ export declare class Statement { reset(): void @@ -139,12 +129,6 @@ export declare class Statement { export interface DatabaseOpts { tracing?: string } - -/** - * turso-db in the the browser requires explicit thread pool initialization - * so, we just put no-op task on the thread pool and force emnapi to allocate web worker - */ -export declare function initThreadPool(): Promise export declare class GeneratorHolder { resumeSync(error?: string | undefined | null): GeneratorResponse resumeAsync(error?: string | undefined | null): Promise diff --git a/bindings/javascript/sync/packages/native/index.js b/bindings/javascript/sync/packages/native/index.js index 12e351d61..cd543a959 100644 --- a/bindings/javascript/sync/packages/native/index.js +++ b/bindings/javascript/sync/packages/native/index.js @@ -510,10 +510,7 @@ if (!nativeBinding) { const { Database, Statement, GeneratorHolder, JsDataCompletion, JsProtocolIo, JsProtocolRequestBytes, SyncEngine, SyncEngineChanges, DatabaseChangeTypeJs, SyncEngineProtocolVersion } = nativeBinding export { Database } -export { Opfs } -export { OpfsFile } export { Statement } -export { initThreadPool } export { GeneratorHolder } export { JsDataCompletion } export { JsProtocolIo } diff --git a/bindings/javascript/sync/packages/native/package.json b/bindings/javascript/sync/packages/native/package.json index c0c01081c..c7c52414f 100644 --- a/bindings/javascript/sync/packages/native/package.json +++ b/bindings/javascript/sync/packages/native/package.json @@ -31,7 +31,7 @@ "napi-artifacts": "napi artifacts --output-dir .", "tsc-build": "npm exec tsc", "build": "npm run napi-build && npm run tsc-build", - "test": "VITE_TURSO_DB_URL=http://c--a--a.localhost:10000 vitest --run", + "test": "VITE_TURSO_DB_URL=http://d--a--a.localhost:10000 vitest --run", "prepublishOnly": "npm run napi-dirs && npm run napi-artifacts && napi prepublish -t npm" }, "napi": { diff --git a/bindings/javascript/sync/src/lib.rs b/bindings/javascript/sync/src/lib.rs index 13427f501..3223b0795 100644 --- a/bindings/javascript/sync/src/lib.rs +++ b/bindings/javascript/sync/src/lib.rs @@ -6,7 +6,7 @@ pub mod js_protocol_io; use std::{ collections::HashMap, - sync::{Arc, Mutex, MutexGuard, OnceLock, RwLock, RwLockReadGuard}, + sync::{Arc, Mutex, OnceLock, RwLock, RwLockReadGuard}, }; use napi::bindgen_prelude::{AsyncTask, Either5, Null}; @@ -149,6 +149,8 @@ impl SyncEngine { pub fn new(opts: SyncEngineOpts) -> napi::Result { // helpful for local debugging match opts.tracing.as_deref() { + Some("error") => init_tracing(LevelFilter::ERROR), + Some("warn") => init_tracing(LevelFilter::WARN), Some("info") => init_tracing(LevelFilter::INFO), Some("debug") => init_tracing(LevelFilter::DEBUG), Some("trace") => init_tracing(LevelFilter::TRACE), From b1062207435dcc67e2cfd6b0c90cc796b67fe30d Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Thu, 18 Sep 2025 01:35:22 +0400 Subject: [PATCH 10/14] main thread in browser can't execute parking - so we use parking lot in spin-lock style for that target --- core/storage/wal.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/core/storage/wal.rs b/core/storage/wal.rs index 3edf3f6d5..d6b111172 100644 --- a/core/storage/wal.rs +++ b/core/storage/wal.rs @@ -1639,11 +1639,41 @@ impl WalFile { } fn get_shared_mut(&self) -> parking_lot::RwLockWriteGuard<'_, WalFileShared> { - self.shared.write() + // WASM in browser main thread doesn't have a way to "park" a thread + // so, we spin way here instead of calling blocking lock + #[cfg(target_family = "wasm")] + { + loop { + let Some(lock) = self.shared.try_write() else { + std::hint::spin_loop(); + continue; + }; + return lock; + } + } + #[cfg(not(target_family = "wasm"))] + { + self.shared.write() + } } fn get_shared(&self) -> parking_lot::RwLockReadGuard<'_, WalFileShared> { - self.shared.read() + // WASM in browser main thread doesn't have a way to "park" a thread + // so, we spin way here instead of calling blocking lock + #[cfg(target_family = "wasm")] + { + loop { + let Some(lock) = self.shared.try_read() else { + std::hint::spin_loop(); + continue; + }; + return lock; + } + } + #[cfg(not(target_family = "wasm"))] + { + self.shared.read() + } } fn complete_append_frame(&mut self, page_id: u64, frame_id: u64, checksums: (u32, u32)) { From bc2dbe902514a5e6467af4f587520ddbae9febb8 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Fri, 19 Sep 2025 12:56:45 +0400 Subject: [PATCH 11/14] fix bug --- bindings/javascript/packages/common/promise.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/javascript/packages/common/promise.ts b/bindings/javascript/packages/common/promise.ts index 88ccb802c..3009b2fcb 100644 --- a/bindings/javascript/packages/common/promise.ts +++ b/bindings/javascript/packages/common/promise.ts @@ -352,7 +352,7 @@ class Statement { } } } finally { - + this.db.execLock.release(); } } From 7049f3ddae4005e528811444aa6765a535dce23d Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Fri, 19 Sep 2025 13:17:23 +0400 Subject: [PATCH 12/14] fix clock implementation for OPFS IO --- Cargo.lock | 2 +- Cargo.toml | 6 ++---- bindings/javascript/Cargo.toml | 1 + bindings/javascript/src/browser.rs | 6 +++++- bindings/javascript/src/lib.rs | 2 -- bindings/javascript/sync/Cargo.toml | 1 - 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f42165992..79c477a01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4220,6 +4220,7 @@ dependencies = [ name = "turso_node" version = "0.2.0-pre.3" dependencies = [ + "chrono", "napi", "napi-build", "napi-derive", @@ -4322,7 +4323,6 @@ name = "turso_sync_js" version = "0.2.0-pre.3" dependencies = [ "genawaiter", - "http", "napi", "napi-build", "napi-derive", diff --git a/Cargo.toml b/Cargo.toml index 2771c2a31..33352e546 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,9 +33,7 @@ members = [ "perf/throughput/turso", "perf/throughput/rusqlite", ] -exclude = [ - "perf/latency/limbo", -] +exclude = ["perf/latency/limbo"] [workspace.package] version = "0.2.0-pre.3" @@ -60,7 +58,7 @@ limbo_percentile = { path = "extensions/percentile", version = "0.2.0-pre.3" } limbo_regexp = { path = "extensions/regexp", version = "0.2.0-pre.3" } turso_sqlite3_parser = { path = "vendored/sqlite3-parser", version = "0.2.0-pre.3" } limbo_uuid = { path = "extensions/uuid", version = "0.2.0-pre.3" } -turso_parser = { path = "parser", version = "0.2.0-pre.3" } +turso_parser = { path = "parser", version = "0.2.0-pre.3" } sql_generation = { path = "sql_generation" } strum = { version = "0.26", features = ["derive"] } strum_macros = "0.26" diff --git a/bindings/javascript/Cargo.toml b/bindings/javascript/Cargo.toml index 1b5001839..dcd2ba441 100644 --- a/bindings/javascript/Cargo.toml +++ b/bindings/javascript/Cargo.toml @@ -16,6 +16,7 @@ napi = { version = "3.1.3", default-features = false, features = ["napi6"] } napi-derive = { version = "3.1.1", default-features = true } tracing-subscriber = { workspace = true, features = ["env-filter"] } tracing.workspace = true +chrono = { workspace = true, default-features = false, features = ["clock"] } [features] encryption = ["turso_core/encryption"] diff --git a/bindings/javascript/src/browser.rs b/bindings/javascript/src/browser.rs index c59d86e7f..4ebae0a59 100644 --- a/bindings/javascript/src/browser.rs +++ b/bindings/javascript/src/browser.rs @@ -140,7 +140,11 @@ impl Opfs { impl Clock for Opfs { fn now(&self) -> Instant { - Instant { secs: 0, micros: 0 } // TODO + let now = chrono::Local::now(); + Instant { + secs: now.timestamp(), + micros: now.timestamp_subsec_micros(), + } } } diff --git a/bindings/javascript/src/lib.rs b/bindings/javascript/src/lib.rs index ff910b7f2..df4724e6e 100644 --- a/bindings/javascript/src/lib.rs +++ b/bindings/javascript/src/lib.rs @@ -12,8 +12,6 @@ #[cfg(feature = "browser")] pub mod browser; -#[cfg(feature = "browser")] -use crate::browser::opfs; use napi::bindgen_prelude::*; use napi::{Env, Task}; diff --git a/bindings/javascript/sync/Cargo.toml b/bindings/javascript/sync/Cargo.toml index 00749c5d3..1057c4e8f 100644 --- a/bindings/javascript/sync/Cargo.toml +++ b/bindings/javascript/sync/Cargo.toml @@ -10,7 +10,6 @@ repository.workspace = true crate-type = ["cdylib"] [dependencies] -http = "1.3.1" napi = { version = "3.1.3", default-features = false, features = ["napi6"] } napi-derive = { version = "3.1.1", default-features = true } turso_sync_engine = { workspace = true } From cfc8728774bf914e1156b6907b9bff996eacfa73 Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Fri, 19 Sep 2025 13:25:52 +0400 Subject: [PATCH 13/14] fix clippy --- bindings/javascript/src/browser.rs | 26 +++++++++++++------------- bindings/javascript/sync/src/lib.rs | 10 +++++----- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/bindings/javascript/src/browser.rs b/bindings/javascript/src/browser.rs index 4ebae0a59..16b071ec8 100644 --- a/bindings/javascript/src/browser.rs +++ b/bindings/javascript/src/browser.rs @@ -64,7 +64,7 @@ pub struct OpfsInner { } thread_local! { - static OPFS: Arc = Arc::new(Opfs::new()); + static OPFS: Arc = Arc::new(Opfs::default()); } #[napi] @@ -74,9 +74,6 @@ struct OpfsFile { opfs: Opfs, } -// unsafe impl Send for OpfsFile {} -// unsafe impl Sync for OpfsFile {} - unsafe impl Send for Opfs {} unsafe impl Sync for Opfs {} @@ -107,15 +104,6 @@ pub fn opfs() -> Arc { } impl Opfs { - pub fn new() -> Self { - Self { - inner: Arc::new(OpfsInner { - completion_no: RefCell::new(0), - completions: RefCell::new(HashMap::new()), - }), - } - } - pub fn complete(&self, completion_no: u32, result: i32) { let completion = { let mut completions = self.inner.completions.borrow_mut(); @@ -148,6 +136,18 @@ impl Clock for Opfs { } } +impl Default for Opfs { + fn default() -> Self { + Self { + #[allow(clippy::arc_with_non_send_sync)] + inner: Arc::new(OpfsInner { + completion_no: RefCell::new(0), + completions: RefCell::new(HashMap::new()), + }), + } + } +} + #[link(wasm_import_module = "env")] extern "C" { fn lookup_file(path: *const u8, path_len: usize) -> i32; diff --git a/bindings/javascript/sync/src/lib.rs b/bindings/javascript/sync/src/lib.rs index 3223b0795..f851af991 100644 --- a/bindings/javascript/sync/src/lib.rs +++ b/bindings/javascript/sync/src/lib.rs @@ -255,7 +255,7 @@ impl SyncEngine { #[napi] pub fn push(&self) -> GeneratorHolder { self.run(async move |coro, guard| { - let sync_engine = try_read(&guard)?; + let sync_engine = try_read(guard)?; let sync_engine = try_unwrap(&sync_engine)?; sync_engine.push_changes_to_remote(coro).await?; Ok(None) @@ -265,7 +265,7 @@ impl SyncEngine { #[napi] pub fn stats(&self) -> GeneratorHolder { self.run(async move |coro, guard| { - let sync_engine = try_read(&guard)?; + let sync_engine = try_read(guard)?; let sync_engine = try_unwrap(&sync_engine)?; let stats = sync_engine.stats(coro).await?; Ok(Some(GeneratorResponse::SyncEngineStats { @@ -282,7 +282,7 @@ impl SyncEngine { #[napi] pub fn wait(&self) -> GeneratorHolder { self.run(async move |coro, guard| { - let sync_engine = try_read(&guard)?; + let sync_engine = try_read(guard)?; let sync_engine = try_unwrap(&sync_engine)?; Ok(Some(GeneratorResponse::SyncEngineChanges { changes: SyncEngineChanges { @@ -296,7 +296,7 @@ impl SyncEngine { pub fn apply(&self, changes: &mut SyncEngineChanges) -> GeneratorHolder { let status = changes.status.take().unwrap(); self.run(async move |coro, guard| { - let sync_engine = try_read(&guard)?; + let sync_engine = try_read(guard)?; let sync_engine = try_unwrap(&sync_engine)?; sync_engine.apply_changes_from_remote(coro, status).await?; Ok(None) @@ -306,7 +306,7 @@ impl SyncEngine { #[napi] pub fn checkpoint(&self) -> GeneratorHolder { self.run(async move |coro, guard| { - let sync_engine = try_read(&guard)?; + let sync_engine = try_read(guard)?; let sync_engine = try_unwrap(&sync_engine)?; sync_engine.checkpoint(coro).await?; Ok(None) From c24e5219d2665c3875f273e48621b093b2be1a8a Mon Sep 17 00:00:00 2001 From: Nikita Sivukhin Date: Fri, 19 Sep 2025 14:24:42 +0400 Subject: [PATCH 14/14] remove log file --- bindings/javascript/sync/packages/native/log | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 bindings/javascript/sync/packages/native/log diff --git a/bindings/javascript/sync/packages/native/log b/bindings/javascript/sync/packages/native/log deleted file mode 100644 index b6989da2e..000000000 --- a/bindings/javascript/sync/packages/native/log +++ /dev/null @@ -1,14 +0,0 @@ - -> @tursodatabase/sync@0.2.0-pre.3 test -> VITE_TURSO_DB_URL=http://c--a--a.localhost:10000 vitest --run -t update - - - RUN v3.2.4 /home/sivukhin/turso/limbo/bindings/javascript/sync/packages/native - - ✓ promise.test.ts (14 tests | 13 skipped) 109ms - - Test Files 1 passed (1) - Tests 1 passed | 13 skipped (14) - Start at 16:40:50 - Duration 436ms (transform 99ms, setup 0ms, collect 118ms, tests 109ms, environment 0ms, prepare 57ms) -