@loro-dev/unisqlite
Advanced tools
+1
-1
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, i as QueryResult, l as UniStoreConnection, n as ConnectionType, o as SQLiteParams, s as SQLiteValue, u as UniStoreOptions } from "./types.mjs"; | ||
| import { c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, i as ConnectionType, l as SQLiteValue, o as QueryResult, s as RunResult } from "./types.mjs"; | ||
@@ -3,0 +3,0 @@ //#region src/adapters/base.d.ts |
+1
-1
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, i as QueryResult, l as UniStoreConnection, n as ConnectionType, o as SQLiteParams, s as SQLiteValue, u as UniStoreOptions } from "./types.js"; | ||
| import { c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, i as ConnectionType, l as SQLiteValue, o as QueryResult, s as RunResult } from "./types.js"; | ||
@@ -3,0 +3,0 @@ //#region src/adapters/base.d.ts |
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, c as SQLiteWasmConfig, i as QueryResult, l as UniStoreConnection, o as SQLiteParams, s as SQLiteValue, t as ChangeEvent, u as UniStoreOptions } from "./types.mjs"; | ||
| import { c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, l as SQLiteValue, o as QueryResult, s as RunResult, t as ChangeEvent, u as SQLiteWasmConfig } from "./types.mjs"; | ||
| import { n as BrowserSQLiteStatement, t as BrowserSQLiteDatabase } from "./platform-types.mjs"; | ||
@@ -3,0 +3,0 @@ |
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, c as SQLiteWasmConfig, i as QueryResult, l as UniStoreConnection, o as SQLiteParams, s as SQLiteValue, t as ChangeEvent, u as UniStoreOptions } from "./types.js"; | ||
| import { c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, l as SQLiteValue, o as QueryResult, s as RunResult, t as ChangeEvent, u as SQLiteWasmConfig } from "./types.js"; | ||
| import { n as BrowserSQLiteStatement, t as BrowserSQLiteDatabase } from "./platform-types.js"; | ||
@@ -3,0 +3,0 @@ |
@@ -1,8 +0,8 @@ | ||
| const e=require(`./chunk.cjs`),t=require(`./base.cjs`);let n=require(`debug`);n=e.t(n);const r=`unisqlite`,i=`0.4.0`;function a(e){let t=(0,n.default)(`${r}:${e}`),i=(0,n.default)(`${r}:${e}:warn`),a=(0,n.default)(`${r}:${e}:error`);return a.enabled=!0,{log:t,warn:i,error:a}}const o=a(`host`),s=a(`txn`),c=a(`adapter`),l=a(`wasm`),u=a(`visibility`);var d=class e{constructor(e){this.options=e,this.abortController=null,this._isHost=!1,this._isStarted=!1,this.releaseResolver=null,this.hostReadyResolver=null,this.hostReadyPromise=null,this.lockName=`sqlite:host:${e.dbName}`,this.txnLockName=`sqlite:txn:${e.dbName}`}static isSupported(){return typeof navigator<`u`&&navigator.locks!==void 0}get isHost(){return this._isHost}async start(){if(!this._isStarted){if(!e.isSupported()){o.warn(`Web Locks API not supported. Running in local-only mode.`),this._isStarted=!0,this._isHost=!0,this.options.onBecomeHost().catch(e=>{o.error(`Error in onBecomeHost callback:`,e),this._isHost=!1});return}this.options.checkVisibility&&!this.options.checkVisibility()||(this._isStarted=!0,this.hostReadyPromise=new Promise(e=>{this.hostReadyResolver=e}),this.tryAcquireHost().catch(e=>{o.error(`Error in host election:`,e)}))}}async waitForHost(){if(!this._isHost&&(this.hostReadyPromise||(await this.start(),this.hostReadyPromise)))return this.hostReadyPromise}async tryAcquireHost(){this.abortController=new AbortController;try{await navigator.locks.request(this.lockName,{mode:`exclusive`,signal:this.abortController.signal},async e=>{if(e){this._isHost=!0,o.log(`Tab ${this.options.tabId} became Host for ${this.options.dbName}`);try{await this.options.onBecomeHost()}catch(e){throw o.error(`Error in onBecomeHost callback:`,e),this._isHost=!1,e}this.hostReadyResolver&&(this.hostReadyResolver(),this.hostReadyResolver=null,this.hostReadyPromise=null),await new Promise(e=>{this.releaseResolver=e,this.abortController.signal.addEventListener(`abort`,()=>{e()})}),this._isHost=!1,this.releaseResolver=null,o.log(`Tab ${this.options.tabId} lost Host role for ${this.options.dbName}`);try{this.options.onLoseHost()}catch(e){o.error(`Error in onLoseHost callback:`,e)}}})}catch(e){if(e.name===`AbortError`)return;throw o.error(`Error acquiring Host lock:`,e),e}finally{this._isStarted=!1,this.abortController=null}}releaseHost(){this.releaseResolver&&=(this.releaseResolver(),null),this.abortController&&=(this.abortController.abort(),null)}async acquireTransactionLock(){if(!e.isSupported())return()=>{};let t=new AbortController,n=null,r=null,i=new Promise(e=>{r=e});return navigator.locks.request(this.txnLockName,{mode:`exclusive`,signal:t.signal},async e=>{e&&(r?.(),await new Promise(e=>{n=e}))}).catch(e=>{e.name!==`AbortError`&&o.error(`Error acquiring transaction lock:`,e),r?.()}),await i,()=>{n?n():t.abort()}}async hasTransactionLock(){if(!e.isSupported())return!1;try{return(await navigator.locks.query()).held?.some(e=>e.name===this.txnLockName)??!1}catch{return!1}}async close(){this.releaseHost()}},f=class{constructor(){this.listeners=new Set,this.boundHandler=this.handleVisibilityChange.bind(this),typeof document<`u`&&document.addEventListener(`visibilitychange`,this.boundHandler)}handleVisibilityChange(){let e=this.isVisible();this.listeners.forEach(t=>{try{t(e)}catch(e){u.error(`Error in visibility change listener:`,e)}})}isVisible(){return typeof document>`u`?!0:document.visibilityState===`visible`}onVisibilityChange(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}destroy(){typeof document<`u`&&document.removeEventListener(`visibilitychange`,this.boundHandler),this.listeners.clear()}};function p(e){return e instanceof Error?{name:e.name,message:e.message,stack:e.stack}:{name:`Error`,message:String(e)}}function m(e){let t=Error(e.message);return t.name=e.name,e.stack&&(t.stack=e.stack),t}function h(e){return e.t===`req`||e.t===`txn_begin`||e.t===`txn_exec`||e.t===`txn_commit`||e.t===`txn_rollback`||e.t===`txn_heartbeat`}function g(e){return e.t===`res`||e.t===`txn_ack`||e.t===`txn_result`||e.t===`txn_done`||e.t===`txn_error`}function _(){return crypto.randomUUID()}function v(){return crypto.randomUUID()}function y(){return crypto.randomUUID()}var b=class{constructor(e,t,n=3e4){this.dbName=e,this.tabId=t,this.defaultTimeoutMs=n,this.pending=new Map,this.closed=!1,this.channel=new BroadcastChannel(`sqlite:rpc:${e}`),this.channel.onmessage=this.handleMessage.bind(this)}handleMessage(e){if(this.closed)return;let t=e.data;!t||typeof t.t!=`string`||(g(t)?this.handleResponse(t):h(t)&&this.requestHandler&&`from`in t&&t.from!==this.tabId&&this.handleRequest(t))}handleResponse(e){if(`to`in e&&e.to!==this.tabId||!(`id`in e))return;let t=this.pending.get(e.id);if(t)switch(clearTimeout(t.timer),this.pending.delete(e.id),e.t){case`res`:e.ok?t.resolve(e.result):t.reject(m(e.error));break;case`txn_ack`:case`txn_done`:t.resolve(void 0);break;case`txn_result`:e.ok?t.resolve(e.result):t.reject(m(e.error));break;case`txn_error`:t.reject(m(e.error));break}}async handleRequest(e){if(this.requestHandler)try{let t=await this.requestHandler(e);this.sendResponse(e,t)}catch(t){this.sendErrorResponse(e,t)}}sendResponse(e,t){if(this.closed)return;let n=`from`in e?e.from:void 0,r=`id`in e?e.id:void 0;if(!(!n||!r))switch(e.t){case`req`:this.channel.postMessage({t:`res`,to:n,id:r,ok:!0,result:t});break;case`txn_begin`:this.channel.postMessage({t:`txn_ack`,to:n,id:r,txnId:e.txnId});break;case`txn_exec`:this.channel.postMessage({t:`txn_result`,to:n,id:r,txnId:e.txnId,ok:!0,result:t});break;case`txn_commit`:case`txn_rollback`:this.channel.postMessage({t:`txn_done`,to:n,id:r,txnId:e.txnId});break;case`txn_heartbeat`:break}}sendErrorResponse(e,t){if(this.closed)return;let n=`from`in e?e.from:void 0,r=`id`in e?e.id:void 0;if(!n||!r)return;let i=p(t);switch(e.t){case`req`:this.channel.postMessage({t:`res`,to:n,id:r,ok:!1,error:i});break;case`txn_begin`:case`txn_exec`:case`txn_commit`:case`txn_rollback`:this.channel.postMessage({t:`txn_error`,to:n,id:r,txnId:e.txnId,error:i});break}}setRequestHandler(e){this.requestHandler=e}async request(e,t,n){let r=_(),i=n??this.defaultTimeoutMs;return new Promise((n,a)=>{let o=setTimeout(()=>{this.pending.delete(r),a(Error(`RPC timeout after ${i}ms for ${e}`))},i);this.pending.set(r,{resolve:n,reject:a,timer:o}),this.channel.postMessage({t:`req`,from:this.tabId,id:r,op:e,payload:t})})}async txnBegin(e,t=`immediate`,n){let r=_(),i=n??this.defaultTimeoutMs;return new Promise((n,a)=>{let o=setTimeout(()=>{this.pending.delete(r),a(Error(`Transaction begin timeout after ${i}ms`))},i);this.pending.set(r,{resolve:n,reject:a,timer:o}),this.channel.postMessage({t:`txn_begin`,from:this.tabId,id:r,txnId:e,mode:t})})}async txnExec(e,t,n,r){let i=_(),a=r??1e4;return new Promise((r,o)=>{let s=setTimeout(()=>{this.pending.delete(i),o(Error(`Transaction SQL timeout after ${a}ms`))},a);this.pending.set(i,{resolve:r,reject:o,timer:s}),this.channel.postMessage({t:`txn_exec`,from:this.tabId,id:i,txnId:e,op:t,payload:n})})}async txnCommit(e,t){let n=_(),r=t??1e4;return new Promise((t,i)=>{let a=setTimeout(()=>{this.pending.delete(n),i(Error(`Transaction commit timeout after ${r}ms`))},r);this.pending.set(n,{resolve:t,reject:i,timer:a}),this.channel.postMessage({t:`txn_commit`,from:this.tabId,id:n,txnId:e})})}async txnRollback(e,t){let n=_(),r=t??1e4;return new Promise((t,i)=>{let a=setTimeout(()=>{this.pending.delete(n),i(Error(`Transaction rollback timeout after ${r}ms`))},r);this.pending.set(n,{resolve:t,reject:i,timer:a}),this.channel.postMessage({t:`txn_rollback`,from:this.tabId,id:n,txnId:e})})}txnHeartbeat(e){this.closed||this.channel.postMessage({t:`txn_heartbeat`,from:this.tabId,txnId:e})}close(){this.closed=!0,this.channel.close();for(let e of this.pending.values())clearTimeout(e.timer),e.reject(Error(`RPC channel closed`));this.pending.clear()}};const x=new Map,S=new Map;var C=class e{constructor(e,t){this.dbName=e,this.config=t,this.workerQueue=Promise.resolve(),this.activeStorageBackend=`memory`,this.closed=!1}static async create(t){let n=new e(t.dbName,t.config);return await n.initialize(),n}async initialize(){l.log(`UniSQLite v0.4.0 - Loading SQLite WASM using strategy: ${this.config.loadStrategy}`);let e=await this.loadSQLiteWasm(),t={print:(...e)=>l.log(...e),printErr:(...e)=>l.error(...e)};if(this.config.locateFile)t.locateFile=this.config.locateFile;else if(typeof window<`u`){let e=window;e.sqlite3InitModuleState?.locateFile&&(t.locateFile=e.sqlite3InitModuleState.locateFile)}if(l.log(`Initializing SQLite WASM module...`),this.sqlite3=await e(t),!this.sqlite3)throw Error(`Failed to initialize SQLite WASM module`);let n=this.sqlite3;l.log(`SQLite WASM module initialized successfully`);let r=this.config.storageBackend??`auto`,i=this.dbName===`:memory:`?`:memory:`:`/unisqlite/${this.dbName}`;r===`memory`||this.dbName===`:memory:`?(l.log(`Using in-memory database`),this.db=new n.oo1.DB(`:memory:`),this.activeStorageBackend=`memory`):await this.tryCreatePersistentDatabase(n,i,r)||(l.log(`All persistent storage backends failed, using in-memory database`),this.db=new n.oo1.DB(`:memory:`),this.activeStorageBackend=`memory`);try{await this.execInternal(`PRAGMA journal_mode=WAL`),l.log(`Enabled WAL mode`)}catch(e){l.warn(`Could not enable WAL mode:`,e)}}async query(e,t){return(await this.executeSql(e,t)).rows}async queryRaw(e,t){return await this.executeSql(e,t)}async run(e,t){let n=await this.executeSql(e,t);return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){await this.execInternal(e)}async execRaw(e){await this.execInternal(e,{bypassQueue:!0})}async executeSqlRaw(e,t){return await this.executeSql(e,t,{bypassQueue:!0})}getActiveStorageBackend(){return this.activeStorageBackend}getActiveOpfsVfs(){return this.activeOpfsVfs}isWorkerDbActive(){return!!this.workerPromiser&&this.workerDbId!==void 0}async close(){if(!this.closed){if(this.closed=!0,this.workerPromiser){let e=this.workerPromiser,t=this.workerDbId;try{t!==void 0&&await this.enqueueWorker(async()=>{await e(`close`,{dbId:t})})}catch(e){l.warn(`Failed to close SQLite worker database:`,e)}finally{this.workerDbId=void 0;let t=e.worker;t?.terminate&&t.terminate(),this.workerPromiser=void 0}}this.db&&=(this.db.close(),void 0)}}enqueueWorker(e){let t=async()=>e(),n=this.workerQueue.then(t,t);return this.workerQueue=n.then(()=>void 0,()=>void 0),n}normalizeParams(e){if(!e)return;if(Array.isArray(e))return e;let t=Object.keys(e),n=e;return t.map(e=>n[e])}async execInternal(e,t){if(this.isWorkerDbActive()){let n=async()=>{if(!this.workerPromiser||this.workerDbId===void 0)throw Error(`Database not initialized`);await this.workerPromiser(`exec`,{dbId:this.workerDbId,sql:e})};t?.bypassQueue?await n():await this.enqueueWorker(n);return}if(!this.db)throw Error(`Database not initialized`);this.db.exec(e)}async executeSql(e,t,n){try{if(this.isWorkerDbActive()){let r=async()=>{if(!this.workerPromiser||this.workerDbId===void 0)throw Error(`Database not initialized`);let n=this.normalizeParams(t),r={dbId:this.workerDbId,sql:e,rowMode:`object`,resultRows:[],columnNames:[],countChanges:!0};n&&(r.bind=n);let i=await this.workerPromiser(`exec`,r),a=i.result??i,o=Array.isArray(a.resultRows)?a.resultRows:[],s=Array.isArray(a.columnNames)?a.columnNames:[],c=typeof a.changeCount==`number`?a.changeCount:0,l={dbId:this.workerDbId,sql:`SELECT last_insert_rowid() AS lastInsertRowId`,rowMode:`object`,resultRows:[],columnNames:[]},u=await this.workerPromiser(`exec`,l),d=u.result??u,f=Array.isArray(d.resultRows)?d.resultRows:[];return{rows:o,columns:s,rowsAffected:c,lastInsertRowId:Number(f[0]?.lastInsertRowId??0)}};return n?.bypassQueue?await r():await this.enqueueWorker(r)}if(!this.db||!this.sqlite3)throw Error(`Database not initialized`);let r=this.normalizeParams(t),i=[],a=[],o=this.db.prepare(e);try{let e=o;r&&o.bind(r);let t=typeof e.getColumnCount==`function`?e.getColumnCount():typeof e.columnCount==`number`?e.columnCount:0;if(t>0)if(typeof e.getColumnNames==`function`)a=e.getColumnNames();else for(let e=0;e<t;e++)a.push(o.getColumnName(e));for(;o.step();){let e={};for(let n=0;n<t;n++)e[a[n]]=o.get(n);i.push(e)}return{rows:i,columns:a,rowsAffected:this.db.changes(),lastInsertRowId:Number(this.sqlite3.capi.sqlite3_last_insert_rowid(this.db.pointer)||0)}}finally{o.finalize()}}catch(e){let t=e instanceof Error?e.message:String(e),n=Error(`SQLite error: ${t}`);throw e instanceof Error&&(n.cause=e),n}}async loadSQLiteWasm(){let e=`${this.config.loadStrategy}-${this.config.wasmUrl||this.config.cdnBaseUrl}-${this.config.version}`;if(x.has(e))return x.get(e);let t=this.loadSQLiteWasmInternal();return x.set(e,t),t}async loadSQLiteWasmInternal(){let{loadStrategy:e,wasmUrl:t,cdnBaseUrl:n,version:r,bundlerFriendly:i}=this.config;switch(e){case`npm`:return await this.loadFromNpm();case`cdn`:return await this.loadFromCdn(n,r,i);case`url`:if(!t)throw Error(`wasmUrl must be provided when using 'url' loading strategy`);return await this.loadFromUrl(t);case`module`:return await this.loadAsModule();case`global`:default:return this.loadFromGlobal()}}async loadFromNpm(){try{let e=await import(`@sqlite.org/sqlite-wasm`);return e.default||e}catch(e){return l.warn(`Failed to load SQLite WASM from npm, falling back to CDN:`,e),this.loadFromCdn(this.config.cdnBaseUrl,this.config.version,this.config.bundlerFriendly)}}async loadFromCdn(e,t,n){let r=n?`sqlite3-bundler-friendly.mjs`:`index.mjs`,i=[`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm@${t}/${r}`,`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm@${t}/dist/${r}`],a;for(let e of i)try{l.log(`Trying to load SQLite WASM from: ${e}`);let t=await import(e);return l.log(`Successfully loaded SQLite WASM from: ${e}`),t.default||t}catch(t){a=t,l.warn(`Failed to load SQLite WASM from ${e}:`,t)}let o=a instanceof Error?a.message:String(a);throw Error(`Failed to load SQLite WASM from any CDN source. Last error: ${o}`)}async loadFromUrl(e){let t=await import(e);return t.default||t.sqlite3InitModule}async loadAsModule(){if(globalThis.importScripts!==void 0)throw Error(`ES6 module loading not supported in Web Worker context`);for(let e of[`./sqlite3.mjs`,`./sqlite3-bundler-friendly.mjs`,`/sqlite3.mjs`])try{let t=await import(e);return t.default||t.sqlite3InitModule}catch(t){l.warn(`Failed to load SQLite WASM from ${e}:`,t)}throw Error(`Could not load SQLite WASM as ES6 module from any known path`)}loadFromGlobal(){if(typeof window<`u`){let e=window;if(e.sqlite3InitModule)return e.sqlite3InitModule}throw Error(`SQLite WASM module not found globally. Please: | ||
| const e=require(`./base.cjs`),t=`unisqlite`,n=`0.4.0`;let r=``,i=[];function a(e){try{return typeof localStorage<`u`?localStorage.getItem(e):null}catch{return null}}function o(e){return e.split(/[\s,]+/g).map(e=>e.trim()).filter(Boolean).map(e=>{let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`).replace(/\\\*/g,`.*`);return RegExp(`^${t}$`)})}function s(e){return i.some(t=>t.test(e))}function c(e){return`[${e}]`}function l(e,t){if(t===`error`)return(...t)=>console.error(c(e),...t);let n=t===`warn`?console.warn.bind(console):console.log.bind(console);return(...t)=>{s(e)&&n(c(e),...t)}}function u(e){let n=`${t}:${e}`,r=`${t}:${e}:warn`,i=`${t}:${e}:error`;return{log:l(n,`log`),warn:l(r,`warn`),error:l(i,`error`)}}const d=u(`host`),f=u(`txn`),p=u(`adapter`),m=u(`wasm`),h=u(`visibility`);function g(){let e=a(`debug`)??``;e!==r&&(r=e,i=o(e))}g();var _=class e{constructor(e){this.options=e,this.abortController=null,this._isHost=!1,this._isStarted=!1,this.releaseResolver=null,this.hostReadyResolver=null,this.hostReadyPromise=null,this.lockName=`sqlite:host:${e.dbName}`,this.txnLockName=`sqlite:txn:${e.dbName}`}static isSupported(){return typeof navigator<`u`&&navigator.locks!==void 0}get isHost(){return this._isHost}async start(){if(!this._isStarted){if(!e.isSupported()){d.warn(`Web Locks API not supported. Running in local-only mode.`),this._isStarted=!0,this._isHost=!0,this.options.onBecomeHost().catch(e=>{d.error(`Error in onBecomeHost callback:`,e),this._isHost=!1});return}this.options.checkVisibility&&!this.options.checkVisibility()||(this._isStarted=!0,this.hostReadyPromise=new Promise(e=>{this.hostReadyResolver=e}),this.tryAcquireHost().catch(e=>{d.error(`Error in host election:`,e)}))}}async waitForHost(){if(!this._isHost&&(this.hostReadyPromise||(await this.start(),this.hostReadyPromise)))return this.hostReadyPromise}async tryAcquireHost(){this.abortController=new AbortController;try{await navigator.locks.request(this.lockName,{mode:`exclusive`,signal:this.abortController.signal},async e=>{if(e){this._isHost=!0,d.log(`Tab ${this.options.tabId} became Host for ${this.options.dbName}`);try{await this.options.onBecomeHost()}catch(e){throw d.error(`Error in onBecomeHost callback:`,e),this._isHost=!1,e}this.hostReadyResolver&&(this.hostReadyResolver(),this.hostReadyResolver=null,this.hostReadyPromise=null),await new Promise(e=>{this.releaseResolver=e,this.abortController.signal.addEventListener(`abort`,()=>{e()})}),this._isHost=!1,this.releaseResolver=null,d.log(`Tab ${this.options.tabId} lost Host role for ${this.options.dbName}`);try{this.options.onLoseHost()}catch(e){d.error(`Error in onLoseHost callback:`,e)}}})}catch(e){if(e.name===`AbortError`)return;throw d.error(`Error acquiring Host lock:`,e),e}finally{this._isStarted=!1,this.abortController=null}}releaseHost(){this.releaseResolver&&=(this.releaseResolver(),null),this.abortController&&=(this.abortController.abort(),null)}async acquireTransactionLock(){if(!e.isSupported())return()=>{};let t=new AbortController,n=null,r=null,i=new Promise(e=>{r=e});return navigator.locks.request(this.txnLockName,{mode:`exclusive`,signal:t.signal},async e=>{e&&(r?.(),await new Promise(e=>{n=e}))}).catch(e=>{e.name!==`AbortError`&&d.error(`Error acquiring transaction lock:`,e),r?.()}),await i,()=>{n?n():t.abort()}}async hasTransactionLock(){if(!e.isSupported())return!1;try{return(await navigator.locks.query()).held?.some(e=>e.name===this.txnLockName)??!1}catch{return!1}}async close(){this.releaseHost()}},v=class{constructor(){this.listeners=new Set,this.boundHandler=this.handleVisibilityChange.bind(this),typeof document<`u`&&document.addEventListener(`visibilitychange`,this.boundHandler)}handleVisibilityChange(){let e=this.isVisible();this.listeners.forEach(t=>{try{t(e)}catch(e){h.error(`Error in visibility change listener:`,e)}})}isVisible(){return typeof document>`u`?!0:document.visibilityState===`visible`}onVisibilityChange(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}destroy(){typeof document<`u`&&document.removeEventListener(`visibilitychange`,this.boundHandler),this.listeners.clear()}};function y(e){return e instanceof Error?{name:e.name,message:e.message,stack:e.stack}:{name:`Error`,message:String(e)}}function b(e){let t=Error(e.message);return t.name=e.name,e.stack&&(t.stack=e.stack),t}function x(e){return e.t===`req`||e.t===`txn_begin`||e.t===`txn_exec`||e.t===`txn_commit`||e.t===`txn_rollback`||e.t===`txn_heartbeat`}function S(e){return e.t===`res`||e.t===`txn_ack`||e.t===`txn_result`||e.t===`txn_done`||e.t===`txn_error`}function C(){return crypto.randomUUID()}function w(){return crypto.randomUUID()}function T(){return crypto.randomUUID()}var E=class{constructor(e,t,n=3e4){this.dbName=e,this.tabId=t,this.defaultTimeoutMs=n,this.pending=new Map,this.closed=!1,this.channel=new BroadcastChannel(`sqlite:rpc:${e}`),this.channel.onmessage=this.handleMessage.bind(this)}handleMessage(e){if(this.closed)return;let t=e.data;!t||typeof t.t!=`string`||(S(t)?this.handleResponse(t):x(t)&&this.requestHandler&&`from`in t&&t.from!==this.tabId&&this.handleRequest(t))}handleResponse(e){if(`to`in e&&e.to!==this.tabId||!(`id`in e))return;let t=this.pending.get(e.id);if(t)switch(clearTimeout(t.timer),this.pending.delete(e.id),e.t){case`res`:e.ok?t.resolve(e.result):t.reject(b(e.error));break;case`txn_ack`:case`txn_done`:t.resolve(void 0);break;case`txn_result`:e.ok?t.resolve(e.result):t.reject(b(e.error));break;case`txn_error`:t.reject(b(e.error));break}}async handleRequest(e){if(this.requestHandler)try{let t=await this.requestHandler(e);this.sendResponse(e,t)}catch(t){this.sendErrorResponse(e,t)}}sendResponse(e,t){if(this.closed)return;let n=`from`in e?e.from:void 0,r=`id`in e?e.id:void 0;if(!(!n||!r))switch(e.t){case`req`:this.channel.postMessage({t:`res`,to:n,id:r,ok:!0,result:t});break;case`txn_begin`:this.channel.postMessage({t:`txn_ack`,to:n,id:r,txnId:e.txnId});break;case`txn_exec`:this.channel.postMessage({t:`txn_result`,to:n,id:r,txnId:e.txnId,ok:!0,result:t});break;case`txn_commit`:case`txn_rollback`:this.channel.postMessage({t:`txn_done`,to:n,id:r,txnId:e.txnId});break;case`txn_heartbeat`:break}}sendErrorResponse(e,t){if(this.closed)return;let n=`from`in e?e.from:void 0,r=`id`in e?e.id:void 0;if(!n||!r)return;let i=y(t);switch(e.t){case`req`:this.channel.postMessage({t:`res`,to:n,id:r,ok:!1,error:i});break;case`txn_begin`:case`txn_exec`:case`txn_commit`:case`txn_rollback`:this.channel.postMessage({t:`txn_error`,to:n,id:r,txnId:e.txnId,error:i});break}}setRequestHandler(e){this.requestHandler=e}async request(e,t,n){let r=C(),i=n??this.defaultTimeoutMs;return new Promise((n,a)=>{let o=setTimeout(()=>{this.pending.delete(r),a(Error(`RPC timeout after ${i}ms for ${e}`))},i);this.pending.set(r,{resolve:n,reject:a,timer:o}),this.channel.postMessage({t:`req`,from:this.tabId,id:r,op:e,payload:t})})}async txnBegin(e,t=`immediate`,n){let r=C(),i=n??this.defaultTimeoutMs;return new Promise((n,a)=>{let o=setTimeout(()=>{this.pending.delete(r),a(Error(`Transaction begin timeout after ${i}ms`))},i);this.pending.set(r,{resolve:n,reject:a,timer:o}),this.channel.postMessage({t:`txn_begin`,from:this.tabId,id:r,txnId:e,mode:t})})}async txnExec(e,t,n,r){let i=C(),a=r??1e4;return new Promise((r,o)=>{let s=setTimeout(()=>{this.pending.delete(i),o(Error(`Transaction SQL timeout after ${a}ms`))},a);this.pending.set(i,{resolve:r,reject:o,timer:s}),this.channel.postMessage({t:`txn_exec`,from:this.tabId,id:i,txnId:e,op:t,payload:n})})}async txnCommit(e,t){let n=C(),r=t??1e4;return new Promise((t,i)=>{let a=setTimeout(()=>{this.pending.delete(n),i(Error(`Transaction commit timeout after ${r}ms`))},r);this.pending.set(n,{resolve:t,reject:i,timer:a}),this.channel.postMessage({t:`txn_commit`,from:this.tabId,id:n,txnId:e})})}async txnRollback(e,t){let n=C(),r=t??1e4;return new Promise((t,i)=>{let a=setTimeout(()=>{this.pending.delete(n),i(Error(`Transaction rollback timeout after ${r}ms`))},r);this.pending.set(n,{resolve:t,reject:i,timer:a}),this.channel.postMessage({t:`txn_rollback`,from:this.tabId,id:n,txnId:e})})}txnHeartbeat(e){this.closed||this.channel.postMessage({t:`txn_heartbeat`,from:this.tabId,txnId:e})}close(){this.closed=!0,this.channel.close();for(let e of this.pending.values())clearTimeout(e.timer),e.reject(Error(`RPC channel closed`));this.pending.clear()}};const D=new Map,O=new Map;function k(e){if(e instanceof Error)return e.message;if(typeof e==`string`)return e;if(e&&typeof e==`object`){let t=e;if(typeof t.message==`string`)return t.message;if(t.error&&typeof t.error==`object`){let e=t.error;if(typeof e.message==`string`)return e.message}if(t.result&&typeof t.result==`object`){let e=t.result;if(typeof e.message==`string`)return e.message}try{let t=JSON.stringify(e);if(t&&t!==`{}`)return t}catch{}}return String(e)}var A=class e{constructor(e,t){this.dbName=e,this.config=t,this.workerQueue=Promise.resolve(),this.activeStorageBackend=`memory`,this.closed=!1}static async create(t){let n=new e(t.dbName,t.config);return await n.initialize(),n}async initialize(){m.log(`UniSQLite v0.4.0 - Loading SQLite WASM using strategy: ${this.config.loadStrategy}`);let e=await this.loadSQLiteWasm(),t={print:(...e)=>m.log(...e),printErr:(...e)=>m.error(...e)};if(this.config.locateFile)t.locateFile=this.config.locateFile;else if(typeof window<`u`){let e=window;e.sqlite3InitModuleState?.locateFile&&(t.locateFile=e.sqlite3InitModuleState.locateFile)}if(m.log(`Initializing SQLite WASM module...`),this.sqlite3=await e(t),!this.sqlite3)throw Error(`Failed to initialize SQLite WASM module`);let n=this.sqlite3;m.log(`SQLite WASM module initialized successfully`);let r=this.config.storageBackend??`auto`,i=this.dbName===`:memory:`?`:memory:`:`/unisqlite/${this.dbName}`;r===`memory`||this.dbName===`:memory:`?(m.log(`Using in-memory database`),this.db=new n.oo1.DB(`:memory:`),this.activeStorageBackend=`memory`):await this.tryCreatePersistentDatabase(n,i,r)||(m.log(`All persistent storage backends failed, using in-memory database`),this.db=new n.oo1.DB(`:memory:`),this.activeStorageBackend=`memory`);try{await this.execInternal(`PRAGMA journal_mode=WAL`),m.log(`Enabled WAL mode`)}catch(e){m.warn(`Could not enable WAL mode:`,e)}}async query(e,t){return(await this.executeSql(e,t)).rows}async queryRaw(e,t){return await this.executeSql(e,t)}async run(e,t){let n=await this.executeSql(e,t);return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){await this.execInternal(e)}async execRaw(e){await this.execInternal(e,{bypassQueue:!0})}async executeSqlRaw(e,t){return await this.executeSql(e,t,{bypassQueue:!0})}getActiveStorageBackend(){return this.activeStorageBackend}getActiveOpfsVfs(){return this.activeOpfsVfs}isWorkerDbActive(){return!!this.workerPromiser&&this.workerDbId!==void 0}async close(){if(!this.closed){if(this.closed=!0,this.workerPromiser){let e=this.workerPromiser,t=this.workerDbId;try{t!==void 0&&await this.enqueueWorker(async()=>{await e(`close`,{dbId:t})})}catch(e){m.warn(`Failed to close SQLite worker database:`,e)}finally{this.workerDbId=void 0;let t=e.worker;t?.terminate&&t.terminate(),this.workerPromiser=void 0}}this.db&&=(this.db.close(),void 0)}}enqueueWorker(e){let t=async()=>e(),n=this.workerQueue.then(t,t);return this.workerQueue=n.then(()=>void 0,()=>void 0),n}normalizeParams(e){if(!e)return;if(Array.isArray(e))return e;let t=Object.keys(e),n=e;return t.map(e=>n[e])}async execInternal(e,t){if(this.isWorkerDbActive()){let n=async()=>{if(!this.workerPromiser||this.workerDbId===void 0)throw Error(`Database not initialized`);await this.workerPromiser(`exec`,{dbId:this.workerDbId,sql:e})};t?.bypassQueue?await n():await this.enqueueWorker(n);return}if(!this.db)throw Error(`Database not initialized`);this.db.exec(e)}async executeSql(e,t,n){try{if(this.isWorkerDbActive()){let r=async()=>{if(!this.workerPromiser||this.workerDbId===void 0)throw Error(`Database not initialized`);let n=this.normalizeParams(t),r={dbId:this.workerDbId,sql:e,rowMode:`object`,resultRows:[],columnNames:[],countChanges:!0};n&&(r.bind=n);let i=await this.workerPromiser(`exec`,r),a=i.result??i,o=Array.isArray(a.resultRows)?a.resultRows:[],s=Array.isArray(a.columnNames)?a.columnNames:[],c=typeof a.changeCount==`number`?a.changeCount:0,l={dbId:this.workerDbId,sql:`SELECT last_insert_rowid() AS lastInsertRowId`,rowMode:`object`,resultRows:[],columnNames:[]},u=await this.workerPromiser(`exec`,l),d=u.result??u,f=Array.isArray(d.resultRows)?d.resultRows:[];return{rows:o,columns:s,rowsAffected:c,lastInsertRowId:Number(f[0]?.lastInsertRowId??0)}};return n?.bypassQueue?await r():await this.enqueueWorker(r)}if(!this.db||!this.sqlite3)throw Error(`Database not initialized`);let r=this.normalizeParams(t),i=[],a=[],o=this.db.prepare(e);try{let e=o;r&&o.bind(r);let t=typeof e.getColumnCount==`function`?e.getColumnCount():typeof e.columnCount==`number`?e.columnCount:0;if(t>0)if(typeof e.getColumnNames==`function`)a=e.getColumnNames();else for(let e=0;e<t;e++)a.push(o.getColumnName(e));for(;o.step();){let e={};for(let n=0;n<t;n++)e[a[n]]=o.get(n);i.push(e)}return{rows:i,columns:a,rowsAffected:this.db.changes(),lastInsertRowId:Number(this.sqlite3.capi.sqlite3_last_insert_rowid(this.db.pointer)||0)}}finally{o.finalize()}}catch(e){let t=k(e),n=Error(`SQLite error: ${t}`);throw e instanceof Error&&(n.cause=e),n}}async loadSQLiteWasm(){let e=`${this.config.loadStrategy}-${this.config.wasmUrl||this.config.cdnBaseUrl}-${this.config.version}`;if(D.has(e))return D.get(e);let t=this.loadSQLiteWasmInternal();return D.set(e,t),t}async loadSQLiteWasmInternal(){let{loadStrategy:e,wasmUrl:t,cdnBaseUrl:n,version:r,bundlerFriendly:i}=this.config;switch(e){case`npm`:return await this.loadFromNpm();case`cdn`:return await this.loadFromCdn(n,r,i);case`url`:if(!t)throw Error(`wasmUrl must be provided when using 'url' loading strategy`);return await this.loadFromUrl(t);case`module`:return await this.loadAsModule();case`global`:default:return this.loadFromGlobal()}}async loadFromNpm(){try{let e=await import(`@sqlite.org/sqlite-wasm`);return e.default||e}catch(e){return m.warn(`Failed to load SQLite WASM from npm, falling back to CDN:`,e),this.loadFromCdn(this.config.cdnBaseUrl,this.config.version,this.config.bundlerFriendly)}}async loadFromCdn(e,t,n){let r=n?`sqlite3-bundler-friendly.mjs`:`index.mjs`,i=[`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm@${t}/${r}`,`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm@${t}/dist/${r}`],a;for(let e of i)try{m.log(`Trying to load SQLite WASM from: ${e}`);let t=await import(e);return m.log(`Successfully loaded SQLite WASM from: ${e}`),t.default||t}catch(t){a=t,m.warn(`Failed to load SQLite WASM from ${e}:`,t)}let o=a instanceof Error?a.message:String(a);throw Error(`Failed to load SQLite WASM from any CDN source. Last error: ${o}`)}async loadFromUrl(e){let t=await import(e);return t.default||t.sqlite3InitModule}async loadAsModule(){if(globalThis.importScripts!==void 0)throw Error(`ES6 module loading not supported in Web Worker context`);for(let e of[`./sqlite3.mjs`,`./sqlite3-bundler-friendly.mjs`,`/sqlite3.mjs`])try{let t=await import(e);return t.default||t.sqlite3InitModule}catch(t){m.warn(`Failed to load SQLite WASM from ${e}:`,t)}throw Error(`Could not load SQLite WASM as ES6 module from any known path`)}loadFromGlobal(){if(typeof window<`u`){let e=window;if(e.sqlite3InitModule)return e.sqlite3InitModule}throw Error(`SQLite WASM module not found globally. Please: | ||
| 1. Install via npm: npm install @sqlite.org/sqlite-wasm | ||
| 2. Or set loadStrategy to 'cdn' for automatic CDN loading | ||
| 3. Or include SQLite WASM script in your HTML before using UniSqlite`)}async loadSQLiteWorkerPromiserFactory(){let e=`worker-${this.config.loadStrategy}-${this.config.wasmUrl||this.config.cdnBaseUrl}-${this.config.version}`;if(S.has(e))return S.get(e);let t=this.loadSQLiteWorkerPromiserFactoryInternal();return S.set(e,t),t}async loadSQLiteWorkerPromiserFactoryInternal(){let{loadStrategy:e,wasmUrl:t,cdnBaseUrl:n,version:r,bundlerFriendly:i}=this.config;switch(e){case`npm`:return await this.loadWorkerPromiserFromNpm();case`cdn`:return await this.loadWorkerPromiserFromCdn(n,r,i);case`url`:if(!t)throw Error(`wasmUrl must be provided when using 'url' loading strategy`);return await this.loadWorkerPromiserFromUrl(t);case`module`:return await this.loadWorkerPromiserAsModule();case`global`:default:return this.loadWorkerPromiserFromGlobal()}}async loadWorkerPromiserFromNpm(){try{let e=(await import(`@sqlite.org/sqlite-wasm`)).sqlite3Worker1Promiser;if(typeof e==`function`)return e;throw Error(`sqlite3Worker1Promiser export not found`)}catch(e){return l.warn(`Failed to load SQLite WASM worker promiser from npm, falling back to CDN:`,e),this.loadWorkerPromiserFromCdn(this.config.cdnBaseUrl,this.config.version,this.config.bundlerFriendly)}}async loadWorkerPromiserFromCdn(e,t,n){let r=n?`sqlite3-bundler-friendly.mjs`:`index.mjs`,i=[`${e}@${t}/${r}`,`${e}@${t}/dist/${r}`],a;for(let e of i)try{l.log(`Trying to load SQLite WASM worker promiser from: ${e}`);let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t==`function`)return l.log(`Successfully loaded SQLite WASM worker promiser from: ${e}`),t;throw Error(`sqlite3Worker1Promiser export not found`)}catch(t){a=t,l.warn(`Failed to load SQLite WASM worker promiser from ${e}:`,t)}let o=a instanceof Error?a.message:String(a);throw Error(`Failed to load sqlite3Worker1Promiser from any CDN source. Last error: ${o}`)}async loadWorkerPromiserFromUrl(e){let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t!=`function`)throw Error(`sqlite3Worker1Promiser export not found in module loaded from ${e}`);return t}async loadWorkerPromiserAsModule(){if(globalThis.importScripts!==void 0)throw Error(`ES6 module loading not supported in Web Worker context`);for(let e of[`./sqlite3.mjs`,`./sqlite3-bundler-friendly.mjs`,`/sqlite3.mjs`])try{let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t==`function`)return t}catch(t){l.warn(`Failed to load SQLite WASM worker promiser from ${e}:`,t)}throw Error(`Could not load sqlite3Worker1Promiser as ES6 module from any known path`)}loadWorkerPromiserFromGlobal(){let e=globalThis;if(typeof e.sqlite3Worker1Promiser==`function`)return e.sqlite3Worker1Promiser;throw Error(`SQLite WASM worker promiser not found globally. Please: | ||
| 3. Or include SQLite WASM script in your HTML before using UniSqlite`)}async loadSQLiteWorkerPromiserFactory(){let e=`worker-${this.config.loadStrategy}-${this.config.wasmUrl||this.config.cdnBaseUrl}-${this.config.version}`;if(O.has(e))return O.get(e);let t=this.loadSQLiteWorkerPromiserFactoryInternal();return O.set(e,t),t}async loadSQLiteWorkerPromiserFactoryInternal(){let{loadStrategy:e,wasmUrl:t,cdnBaseUrl:n,version:r,bundlerFriendly:i}=this.config;switch(e){case`npm`:return await this.loadWorkerPromiserFromNpm();case`cdn`:return await this.loadWorkerPromiserFromCdn(n,r,i);case`url`:if(!t)throw Error(`wasmUrl must be provided when using 'url' loading strategy`);return await this.loadWorkerPromiserFromUrl(t);case`module`:return await this.loadWorkerPromiserAsModule();case`global`:default:return this.loadWorkerPromiserFromGlobal()}}async loadWorkerPromiserFromNpm(){try{let e=(await import(`@sqlite.org/sqlite-wasm`)).sqlite3Worker1Promiser;if(typeof e==`function`)return e;throw Error(`sqlite3Worker1Promiser export not found`)}catch(e){return m.warn(`Failed to load SQLite WASM worker promiser from npm, falling back to CDN:`,e),this.loadWorkerPromiserFromCdn(this.config.cdnBaseUrl,this.config.version,this.config.bundlerFriendly)}}async loadWorkerPromiserFromCdn(e,t,n){let r=n?`sqlite3-bundler-friendly.mjs`:`index.mjs`,i=[`${e}@${t}/${r}`,`${e}@${t}/dist/${r}`],a;for(let e of i)try{m.log(`Trying to load SQLite WASM worker promiser from: ${e}`);let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t==`function`)return m.log(`Successfully loaded SQLite WASM worker promiser from: ${e}`),t;throw Error(`sqlite3Worker1Promiser export not found`)}catch(t){a=t,m.warn(`Failed to load SQLite WASM worker promiser from ${e}:`,t)}let o=a instanceof Error?a.message:String(a);throw Error(`Failed to load sqlite3Worker1Promiser from any CDN source. Last error: ${o}`)}async loadWorkerPromiserFromUrl(e){let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t!=`function`)throw Error(`sqlite3Worker1Promiser export not found in module loaded from ${e}`);return t}async loadWorkerPromiserAsModule(){if(globalThis.importScripts!==void 0)throw Error(`ES6 module loading not supported in Web Worker context`);for(let e of[`./sqlite3.mjs`,`./sqlite3-bundler-friendly.mjs`,`/sqlite3.mjs`])try{let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t==`function`)return t}catch(t){m.warn(`Failed to load SQLite WASM worker promiser from ${e}:`,t)}throw Error(`Could not load sqlite3Worker1Promiser as ES6 module from any known path`)}loadWorkerPromiserFromGlobal(){let e=globalThis;if(typeof e.sqlite3Worker1Promiser==`function`)return e.sqlite3Worker1Promiser;throw Error(`SQLite WASM worker promiser not found globally. Please: | ||
| 1. Install via npm: npm install @sqlite.org/sqlite-wasm and set loadStrategy to 'npm' | ||
| 2. Or set loadStrategy to 'cdn' for automatic CDN loading | ||
| 3. Or include SQLite WASM worker promiser script in your HTML before using UniSqlite`)}async tryCreatePersistentDatabase(e,t,n){if(n===`opfs`||n===`auto`){let r=this.config.opfsVfsType??`auto`,i=r===`auto`?e.oo1.OpfsDb?[`opfs`]:[`opfs-sahpool`,`opfs`]:r===`sahpool`?[`opfs-sahpool`]:[`opfs`];for(let n of i)if(await this.tryCreateOpfsDatabase(e,t,n))return!0;if(n===`opfs`)throw Error(this.getOpfsUnavailableError())}if(n===`localStorage`||n===`auto`){if(e.oo1.JsStorageDb)if(this.dbName===`local`||this.dbName===`session`)try{return l.log(`Creating localStorage-backed database:`,this.dbName),this.db=new e.oo1.JsStorageDb(this.dbName),this.activeStorageBackend=`localStorage`,l.log(`Successfully created localStorage-backed database`),!0}catch(e){if(l.warn(`localStorage database creation failed:`,e),n===`localStorage`)throw Error(`localStorage database creation failed: ${e instanceof Error?e.message:String(e)}`)}else if(n===`localStorage`)throw Error(`localStorage storage backend requires path to be 'local' or 'session', got '${this.dbName}'. Use storageBackend: 'opfs' for custom database names with persistence.`);else l.log(`Skipping localStorage: path '${this.dbName}' is not 'local' or 'session'`);else if(n===`localStorage`)throw Error(`JsStorageDb is not available in this environment.`)}return!1}getOpfsUnavailableError(){return`OPFS is not available. | ||
| 3. Or include SQLite WASM worker promiser script in your HTML before using UniSqlite`)}async tryCreatePersistentDatabase(e,t,n){if(n===`opfs`||n===`auto`){let r=this.config.opfsVfsType??`auto`,i=r===`auto`?e.oo1.OpfsDb?[`opfs`]:[`opfs-sahpool`,`opfs`]:r===`sahpool`?[`opfs-sahpool`]:[`opfs`];for(let n of i)if(await this.tryCreateOpfsDatabase(e,t,n))return!0;if(n===`opfs`)throw Error(this.getOpfsUnavailableError())}if(n===`localStorage`||n===`auto`){if(e.oo1.JsStorageDb)if(this.dbName===`local`||this.dbName===`session`)try{return m.log(`Creating localStorage-backed database:`,this.dbName),this.db=new e.oo1.JsStorageDb(this.dbName),this.activeStorageBackend=`localStorage`,m.log(`Successfully created localStorage-backed database`),!0}catch(e){if(m.warn(`localStorage database creation failed:`,e),n===`localStorage`){let t=`localStorage database creation failed: ${e instanceof Error?e.message:String(e)}`,n=Error(t);throw e instanceof Error&&(n.cause=e),n}}else if(n===`localStorage`)throw Error(`localStorage storage backend requires path to be 'local' or 'session', got '${this.dbName}'. Use storageBackend: 'opfs' for custom database names with persistence.`);else m.log(`Skipping localStorage: path '${this.dbName}' is not 'local' or 'session'`);else if(n===`localStorage`)throw Error(`JsStorageDb is not available in this environment.`)}return!1}getOpfsUnavailableError(){return`OPFS is not available. | ||
@@ -14,5 +14,5 @@ SQLite WASM OPFS backends only work in Worker contexts. When running on the main thread, UniSQLite will try to use SQLite WASM's wrapped-worker API (sqlite3Worker1Promiser) under the hood. | ||
| - For VFS 'opfs': COOP/COEP headers are set and SharedArrayBuffer is available | ||
| - Your sqlite load strategy can load sqlite3Worker1Promiser (use sqlite.loadStrategy = 'npm' or 'cdn', or provide it globally)`}async tryCreateOpfsDatabase(e,t,n){if(n===`opfs`){if(e.oo1.OpfsDb)try{return l.log(`Creating OPFS-backed database (OpfsDb):`,t),this.db=new e.oo1.OpfsDb(t),this.workerDbId=void 0,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=`opfs`,l.log(`Successfully created OPFS-backed database (OpfsDb)`),!0}catch(e){l.warn(`OPFS database creation via OpfsDb failed:`,e)}return await this.tryCreateWrappedWorkerOpfsDatabase(t,n)}if(await this.ensureSahpoolVfs(e))try{return l.log(`Creating SAHPool OPFS-backed database:`,t),this.db=new e.oo1.DB(t,`c`,`opfs-sahpool`),this.workerDbId=void 0,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=`opfs-sahpool`,l.log(`Successfully created SAHPool OPFS-backed database`),!0}catch(e){l.warn(`SAHPool OPFS database creation failed:`,e)}return await this.tryCreateWrappedWorkerOpfsDatabase(t,n)}async ensureSahpoolVfs(e){if(globalThis.importScripts===void 0)return!1;let t=`opfs-sahpool`;try{if(e.capi.sqlite3_vfs_find?.(t))return!0}catch(e){l.warn(`SAHPool VFS lookup failed:`,e)}if(typeof e.installOpfsSAHPoolVfs!=`function`)return!1;try{await e.installOpfsSAHPoolVfs({name:t,directory:`/unisqlite-sahpool`})}catch(e){return l.warn(`SAHPool VFS installation failed:`,e),!1}try{return!!e.capi.sqlite3_vfs_find?.(t)}catch{return!0}}createWorkerFromConfig(){if(!this.config.workerUrl||typeof Worker>`u`)return;let e=typeof this.config.workerUrl==`string`?this.config.workerUrl:this.config.workerUrl.href;try{return new Worker(e,{type:`module`})}catch(e){l.warn(`Failed to create Worker from custom workerUrl:`,e);return}}createWorkerFromCdn(e){if(typeof Worker>`u`||typeof Blob>`u`||typeof URL>`u`)return;let t=this.config.cdnBaseUrl,n=this.config.version;if(!t||!n)return;let r=`${`${t}@${n}/sqlite-wasm/jswasm`}/sqlite3-bundler-friendly.mjs`,i=e.installSahpool?[` if (typeof sqlite3.installOpfsSAHPoolVfs === "function") {`,` await sqlite3.installOpfsSAHPoolVfs({ name: "opfs-sahpool", directory: "/unisqlite-sahpool" });`,` }`].join(` | ||
| - Your sqlite load strategy can load sqlite3Worker1Promiser (use sqlite.loadStrategy = 'npm' or 'cdn', or provide it globally)`}async tryCreateOpfsDatabase(e,t,n){if(n===`opfs`){if(e.oo1.OpfsDb)try{return m.log(`Creating OPFS-backed database (OpfsDb):`,t),this.db=new e.oo1.OpfsDb(t),this.workerDbId=void 0,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=`opfs`,m.log(`Successfully created OPFS-backed database (OpfsDb)`),!0}catch(e){m.warn(`OPFS database creation via OpfsDb failed:`,e)}return await this.tryCreateWrappedWorkerOpfsDatabase(t,n)}if(await this.ensureSahpoolVfs(e))try{return m.log(`Creating SAHPool OPFS-backed database:`,t),this.db=new e.oo1.DB(t,`c`,`opfs-sahpool`),this.workerDbId=void 0,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=`opfs-sahpool`,m.log(`Successfully created SAHPool OPFS-backed database`),!0}catch(e){m.warn(`SAHPool OPFS database creation failed:`,e)}return await this.tryCreateWrappedWorkerOpfsDatabase(t,n)}async ensureSahpoolVfs(e){if(globalThis.importScripts===void 0)return!1;let t=`opfs-sahpool`;try{if(e.capi.sqlite3_vfs_find?.(t))return!0}catch(e){m.warn(`SAHPool VFS lookup failed:`,e)}if(typeof e.installOpfsSAHPoolVfs!=`function`)return!1;try{await e.installOpfsSAHPoolVfs({name:t,directory:`/unisqlite-sahpool`})}catch(e){return m.warn(`SAHPool VFS installation failed:`,e),!1}try{return!!e.capi.sqlite3_vfs_find?.(t)}catch{return!0}}createWorkerFromConfig(){if(!this.config.workerUrl||typeof Worker>`u`)return;let e=typeof this.config.workerUrl==`string`?this.config.workerUrl:this.config.workerUrl.href;try{return new Worker(e,{type:`module`})}catch(e){m.warn(`Failed to create Worker from custom workerUrl:`,e);return}}createWorkerFromCdn(e){if(typeof Worker>`u`||typeof Blob>`u`||typeof URL>`u`)return;let t=this.config.cdnBaseUrl,n=this.config.version;if(!t||!n)return;let r=`${`${t}@${n}/sqlite-wasm/jswasm`}/sqlite3-bundler-friendly.mjs`,i=e.installSahpool?[` if (typeof sqlite3.installOpfsSAHPoolVfs === "function") {`,` await sqlite3.installOpfsSAHPoolVfs({ name: "opfs-sahpool", directory: "/unisqlite-sahpool" });`,` }`].join(` | ||
| `):``,a=[`import sqlite3InitModule from ${JSON.stringify(r)};`,`sqlite3InitModule().then(async (sqlite3) => {`,` try {`,i||` // no-op`,` } catch (e) {`,` console.warn("SAHPool VFS installation failed:", e);`,` }`,` sqlite3.initWorker1API();`,`});`,``].join(` | ||
| `),o=new Blob([a],{type:`text/javascript`}),s=URL.createObjectURL(o),c=new Worker(s,{type:`module`});return c.addEventListener(`message`,()=>{URL.revokeObjectURL(s)},{once:!0}),c.addEventListener(`error`,()=>{URL.revokeObjectURL(s)},{once:!0}),c}async getOrCreateWorkerPromiser(e){if(this.workerPromiser)return this.workerPromiser;if(this.workerPromiserInitPromise)return await this.workerPromiserInitPromise;let t=await this.loadSQLiteWorkerPromiserFactory(),n=this.config.storageBackend===`opfs`||this.config.opfsVfsType===`opfs`||this.config.opfsVfsType===`sahpool`,r=this.config.loadStrategy===`global`&&!n?5e3:2e4;this.workerPromiserInitPromise=new Promise((n,i)=>{let a=!1,o=setTimeout(()=>{a||(a=!0,i(Error(`SQLite worker initialization timed out after ${r}ms`)))},r),s=(e,t)=>{a||(a=!0,clearTimeout(o),e(t))},c=e=>{s(i,e instanceof Error?e:Error(String(e)))},l;try{l=t({...e?.worker?{worker:e.worker}:{},onready:e=>{if(typeof e==`function`){s(n,e);return}if(typeof l==`function`){s(n,l);return}if(l&&typeof l.then==`function`){l.then(e=>s(n,e),e=>c(e));return}c(Error(`sqlite3Worker1Promiser did not provide a usable promiser`))},onerror:e=>{let t=e instanceof Error?e.message:String(e);c(Error(`SQLite worker error: ${t}`))}}),l&&typeof l.then==`function`&&l.then(e=>s(n,e),e=>c(e))}catch(e){c(e)}});let i=await this.workerPromiserInitPromise;return this.workerPromiser=i,i}async tryCreateWrappedWorkerOpfsDatabase(e,t){try{let n=!this.workerPromiser&&!this.workerPromiserInitPromise?(()=>{let e=this.createWorkerFromConfig()??(t===`opfs-sahpool`?this.createWorkerFromCdn({installSahpool:!0}):this.createWorkerFromCdn({installSahpool:!1}));return e?{worker:e}:void 0})():void 0,r=await(await this.getOrCreateWorkerPromiser(n))(`open`,{filename:`file:${e}`,vfs:t}),i=r.dbId??r.result?.dbId;if(i===void 0)throw Error(`SQLite worker did not return a dbId`);if(typeof i!=`number`&&typeof i!=`string`)throw Error(`SQLite worker returned an invalid dbId (type=${typeof i})`);return this.db=void 0,this.workerDbId=i,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=t,l.log(`Successfully created OPFS-backed database via wrapped worker (${t}):`,e),!0}catch(e){return l.warn(`Wrapped-worker OPFS database creation failed (vfs=${t}):`,e),!1}}},w=class{constructor(e,t,n){this.dbHost=e,this.hostElection=t,this.activeTxn=null,this.txnQueue=[],this.heartbeatTimers=new Map,this.txnPending=!1,this.rpcQueue=[],this.txnTimeoutMs=n?.txnTimeoutMs??3e4,this.opTimeoutMs=n?.opTimeoutMs??1e4,this.maxQueueSize=n?.maxQueueSize??10,this.heartbeatTimeoutMs=n?.heartbeatTimeoutMs??15e3}startWatchdog(){this.watchdogTimer||=setInterval(()=>{if(this.activeTxn){let e=Date.now()-this.activeTxn.lastActivityAt;e>this.txnTimeoutMs&&(s.warn(`Transaction ${this.activeTxn.txnId} timed out after ${e}ms, rolling back`),this.forceRollback())}let e=Date.now(),t=this.txnQueue.filter(t=>e-t.enqueuedAt>this.txnTimeoutMs);for(let e of t){let t=this.txnQueue.indexOf(e);t!==-1&&(this.txnQueue.splice(t,1),e.reject(Error(`Transaction queue wait timeout`)))}},5e3)}stopWatchdog(){this.watchdogTimer&&=(clearInterval(this.watchdogTimer),void 0)}getActiveTransaction(){return this.activeTxn}hasActiveTransaction(){return this.activeTxn!==null}isTransactionBlocking(){return this.activeTxn!==null||this.txnPending}async waitForTransactionSlot(){if(this.isTransactionBlocking())return new Promise((e,t)=>{this.rpcQueue.push({resolve:e,reject:t})})}async beginTransaction(e,t,n=`immediate`){if(this.activeTxn||this.txnPending){if(this.txnQueue.length>=this.maxQueueSize)throw Error(`Transaction queue full`);await new Promise((r,i)=>{this.txnQueue.push({txnId:e,originTabId:t,mode:n,resolve:r,reject:i,enqueuedAt:Date.now()})})}this.txnPending=!0;try{this.txnLockRelease=await this.hostElection.acquireTransactionLock();let r=n===`deferred`?`BEGIN`:n===`exclusive`?`BEGIN EXCLUSIVE`:`BEGIN IMMEDIATE`;await this.dbHost.execRaw(r),this.activeTxn={txnId:e,originTabId:t,startedAt:Date.now(),lastActivityAt:Date.now(),mode:n},this.resetHeartbeatTimer(e)}catch(e){throw this.txnLockRelease?.(),this.txnLockRelease=void 0,this.txnPending=!1,this.processQueue(),this.processRpcQueue(),e}finally{this.txnPending=!1}}async execInTransaction(e,t){return this.validateActiveTxn(e),this.activeTxn.lastActivityAt=Date.now(),this.resetHeartbeatTimer(e),await t()}recordHeartbeat(e){this.activeTxn?.txnId===e&&(this.activeTxn.lastActivityAt=Date.now(),this.resetHeartbeatTimer(e))}async commitTransaction(e){this.validateActiveTxn(e);try{await this.dbHost.execRaw(`COMMIT`)}finally{this.endTransaction()}}async rollbackTransaction(e){this.validateActiveTxn(e);try{await this.dbHost.execRaw(`ROLLBACK`)}finally{this.endTransaction()}}forceRollback(){if(this.activeTxn){try{this.dbHost.execRaw(`ROLLBACK`).catch(e=>{s.error(`Force rollback failed:`,e)})}catch(e){s.error(`Force rollback failed:`,e)}this.endTransaction()}}endTransaction(){if(this.activeTxn){let e=this.heartbeatTimers.get(this.activeTxn.txnId);e&&(clearTimeout(e),this.heartbeatTimers.delete(this.activeTxn.txnId)),this.activeTxn=null}this.txnLockRelease&&=(this.txnLockRelease(),void 0),this.processQueue(),this.processRpcQueue()}processQueue(){this.txnQueue.length>0&&!this.activeTxn&&!this.txnPending&&this.txnQueue.shift().resolve()}processRpcQueue(){if(this.activeTxn||this.txnPending)return;let e=this.rpcQueue;this.rpcQueue=[];for(let{resolve:t}of e)t()}resetHeartbeatTimer(e){let t=this.heartbeatTimers.get(e);t&&clearTimeout(t);let n=setTimeout(()=>{this.activeTxn?.txnId===e&&(s.warn(`No heartbeat for transaction ${e} in ${this.heartbeatTimeoutMs}ms, rolling back`),this.forceRollback())},this.heartbeatTimeoutMs);this.heartbeatTimers.set(e,n)}validateActiveTxn(e){if(!this.activeTxn)throw Error(`No active transaction (expected ${e})`);if(this.activeTxn.txnId!==e)throw Error(`Transaction ID mismatch: expected ${this.activeTxn.txnId}, got ${e}`);let t=Date.now()-this.activeTxn.lastActivityAt;if(t>this.txnTimeoutMs)throw this.forceRollback(),Error(`Transaction ${e} timed out after ${t}ms`)}async close(){this.stopWatchdog(),this.activeTxn&&this.forceRollback();for(let e of this.txnQueue)e.reject(Error(`Transaction session manager closed`));this.txnQueue=[];for(let e of this.rpcQueue)e.reject(Error(`Transaction session manager closed`));this.rpcQueue=[];for(let e of this.heartbeatTimers.values())clearTimeout(e);this.heartbeatTimers.clear()}},T=class{constructor(e,t,n=5e3){this.rpcChannel=e,this.txnId=t,this.heartbeatIntervalMs=n,this.committed=!1,this.rolledBack=!1,this.startHeartbeat()}startHeartbeat(){this.heartbeatInterval=setInterval(()=>{!this.committed&&!this.rolledBack&&this.rpcChannel.txnHeartbeat(this.txnId)},this.heartbeatIntervalMs)}stopHeartbeat(){this.heartbeatInterval&&=(clearInterval(this.heartbeatInterval),void 0)}ensureActive(){if(this.committed)throw Error(`Transaction already committed`);if(this.rolledBack)throw Error(`Transaction already rolled back`)}async query(e,t){return this.ensureActive(),(await this.rpcChannel.txnExec(this.txnId,`query`,{sql:e,params:t})).rows}async queryRaw(e,t){return this.ensureActive(),await this.rpcChannel.txnExec(this.txnId,`queryRaw`,{sql:e,params:t})}async run(e,t){this.ensureActive();let n=await this.rpcChannel.txnExec(this.txnId,`run`,{sql:e,params:t});return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){this.ensureActive(),await this.rpcChannel.txnExec(this.txnId,`exec`,{sql:e})}async transaction(e){this.ensureActive();let t=e(this);return t instanceof Promise?await t:t}async asyncTransaction(e,t){return this.ensureActive(),await e(this)}getConnectionType(){return`asyncTxn`}async close(){}async _commit(){this.ensureActive(),this.stopHeartbeat();try{await this.rpcChannel.txnCommit(this.txnId),this.committed=!0}catch(e){throw this.rolledBack=!0,e}}async _rollback(){if(!(this.committed||this.rolledBack)){this.stopHeartbeat();try{await this.rpcChannel.txnRollback(this.txnId)}finally{this.rolledBack=!0}}}isCommitted(){return this.committed}isRolledBack(){return this.rolledBack}},E=class{constructor(e,t){this.dbHost=e,this.isAsync=t}async query(e,t){return(await this.dbHost.executeSqlRaw(e,t)).rows}async queryRaw(e,t){return await this.dbHost.executeSqlRaw(e,t)}async run(e,t){let n=await this.dbHost.executeSqlRaw(e,t);return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){await this.dbHost.execRaw(e)}async transaction(e){let t=e(this);return t instanceof Promise?await t:t}async asyncTransaction(e){return await e(this)}getConnectionType(){return this.isAsync?`asyncTxn`:`syncTxn`}async close(){}},D=class e extends t.t{constructor(e){let t=e.path??e.name??`default`;super({...e,path:t}),this.dbHost=null,this.txnSessionManager=null,this.initialized=!1,this.closed=!1,this.options={...e,path:t},this.tabId=v(),this.sqliteConfig={loadStrategy:`global`,storageBackend:`auto`,opfsVfsType:`auto`,cdnBaseUrl:`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm`,version:`3.50.1-build1`,bundlerFriendly:!1,...e.sqlite},d.isSupported()||(c.warn(`Web Locks API not supported. Falling back to memory-only storage.`),this.sqliteConfig.storageBackend=`memory`),this.visibilityManager=new f,this.rpcChannel=new b(t,this.tabId),this.hostElection=new d({dbName:t,tabId:this.tabId,onBecomeHost:()=>this.becomeHost(),onLoseHost:()=>this.loseHost(),checkVisibility:()=>this.visibilityManager.isVisible()}),this.visibilityUnsubscribe=this.visibilityManager.onVisibilityChange(e=>{this.handleVisibilityChange(e)})}static async create(t){let n=new e(t);return await n.initialize(),n}getTabId(){return this.tabId}get isHost(){return this.hostElection.isHost}async initialize(){if(!this.initialized){if(this.initPromise)return this.initPromise;this.initPromise=this.doInitialize(),await this.initPromise,this.initialized=!0}}async doInitialize(){this.becomeHostPromise=new Promise(e=>{this.becomeHostResolver=e}),await this.hostElection.start();let e=new Promise(e=>{setTimeout(()=>e(`timeout`),500)});await Promise.race([this.becomeHostPromise.then(()=>`host`),e])===`host`||this.hostElection.isHost&&await this.becomeHostPromise}async becomeHost(){c.log(`Tab ${this.tabId} becoming Host for ${this.options.path}`),this.dbHost=await C.create({dbName:this.options.path,config:this.sqliteConfig}),this.txnSessionManager=new w(this.dbHost,this.hostElection),this.txnSessionManager.startWatchdog(),this.rpcChannel.setRequestHandler(this.handleRequest.bind(this)),this.becomeHostResolver&&=(this.becomeHostResolver(),void 0)}loseHost(){c.log(`Tab ${this.tabId} losing Host for ${this.options.path}`),this.rpcChannel.setRequestHandler(void 0),this.txnSessionManager&&=(this.txnSessionManager.close(),null),this.dbHost&&=(this.dbHost.close(),null)}handleVisibilityChange(e){e&&!this.hostElection.isHost&&!this.closed&&this.hostElection.start()}async handleRequest(e){if(!this.dbHost)throw Error(`Not Host`);switch(e.t){case`req`:return await this.handleRpcRequest(e);case`txn_begin`:return await this.handleTxnBegin(e);case`txn_exec`:return await this.handleTxnExec(e);case`txn_commit`:return await this.handleTxnCommit(e);case`txn_rollback`:return await this.handleTxnRollback(e);case`txn_heartbeat`:this.handleTxnHeartbeat(e);return;default:throw Error(`Unknown request type: ${e.t}`)}}async handleRpcRequest(e){let{op:t,payload:n}=e,{sql:r,params:i}=n;switch(this.txnSessionManager&&await this.txnSessionManager.waitForTransactionSlot(),t){case`query`:return{rows:await this.dbHost.query(r,i),columns:[]};case`queryRaw`:return await this.dbHost.queryRaw(r,i);case`run`:return await this.dbHost.run(r,i);case`exec`:return await this.dbHost.exec(r),null;default:throw Error(`Unknown RPC operation: ${String(t)}`)}}async handleTxnBegin(e){await this.txnSessionManager.beginTransaction(e.txnId,e.from,e.mode)}async handleTxnExec(e){let{txnId:t,op:n,payload:r}=e,{sql:i,params:a}=r;return await this.txnSessionManager.execInTransaction(t,async()=>{switch(n){case`query`:return{rows:await this.dbHost.query(i,a),columns:[]};case`queryRaw`:return await this.dbHost.queryRaw(i,a);case`run`:return await this.dbHost.run(i,a);case`exec`:return await this.dbHost.exec(i),null;default:throw Error(`Unknown transaction operation: ${String(n)}`)}})}async handleTxnCommit(e){await this.txnSessionManager.commitTransaction(e.txnId)}async handleTxnRollback(e){await this.txnSessionManager.rollbackTransaction(e.txnId)}handleTxnHeartbeat(e){this.txnSessionManager?.recordHeartbeat(e.txnId)}async query(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.query(e,t):(await this.rpcChannel.request(`query`,{sql:e,params:t})).rows}async queryRaw(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.queryRaw(e,t):await this.rpcChannel.request(`queryRaw`,{sql:e,params:t})}async run(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.run(e,t):await this.rpcChannel.request(`run`,{sql:e,params:t})}async exec(e){if(await this.initialize(),this.isHost&&this.dbHost){await this.dbHost.exec(e);return}await this.rpcChannel.request(`exec`,{sql:e})}async transaction(e){return await this.initialize(),this.isHost&&this.dbHost?await this.executeLocalTransaction(e,!1):await this.executeRemoteTransaction(e)}async asyncTransaction(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.executeLocalAsyncTransaction(e,t):await this.executeRemoteTransaction(e,t)}getConnectionType(){return`direct`}async close(){this.closed||(this.closed=!0,this.visibilityUnsubscribe?.(),await this.hostElection.close(),this.rpcChannel.close(),this.visibilityManager.destroy(),this.txnSessionManager&&=(await this.txnSessionManager.close(),null),this.dbHost&&=(await this.dbHost.close(),null))}async executeLocalTransaction(e,t){await this.dbHost.execRaw(`BEGIN`);let n=new E(this.dbHost,t);try{let t=e(n),r=t instanceof Promise?await t:t;return await this.dbHost.execRaw(`COMMIT`),r}catch(e){try{await this.dbHost.execRaw(`ROLLBACK`)}catch(e){c.error(`Rollback failed:`,e)}throw e}}async executeLocalAsyncTransaction(e,t){let n=t?.timeoutMs??3e4;await this.dbHost.execRaw(`BEGIN IMMEDIATE`);let r=new E(this.dbHost,!0),i,a=!1;try{let t=new Promise((e,t)=>{i=setTimeout(()=>{a||t(Error(`Async transaction timeout after ${n}ms`))},n)}),o=await Promise.race([e(r),t]);return a=!0,i&&clearTimeout(i),await this.dbHost.execRaw(`COMMIT`),o}catch(e){a=!0,i&&clearTimeout(i);try{await this.dbHost.execRaw(`ROLLBACK`)}catch(e){c.error(`Rollback failed:`,e)}throw e}}async executeRemoteTransaction(e,t){let n=y(),r=t?.timeoutMs??3e4;await this.rpcChannel.txnBegin(n,`immediate`,r);let i=new T(this.rpcChannel,n),a,o=!1;try{let t=new Promise((e,t)=>{a=setTimeout(()=>{o||t(Error(`Remote transaction timeout after ${r}ms`))},r)}),n=e(i),s=n instanceof Promise?n:Promise.resolve(n),c=await Promise.race([s,t]);return o=!0,a&&clearTimeout(a),await i._commit(),c}catch(e){o=!0,a&&clearTimeout(a);try{await i._rollback()}catch(e){c.error(`Remote rollback failed:`,e)}throw e}}getSQLiteInfo(){let e=this.isHost?this.dbHost!==null:this.initialized&&!this.closed;return{config:this.sqliteConfig,isInitialized:this.initialized,isHost:this.isHost,isReady:e,tabId:this.tabId,hasDatabase:this.dbHost!==null,usesWorker:this.dbHost?.isWorkerDbActive()??!1,activeStorageBackend:this.dbHost?.getActiveStorageBackend()??`unknown`,activeOpfsVfs:this.dbHost?.getActiveOpfsVfs()}}getActiveStorageBackend(){return this.dbHost?.getActiveStorageBackend()??`unknown`}};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return D}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return h}}); | ||
| `),o=new Blob([a],{type:`text/javascript`}),s=URL.createObjectURL(o),c=new Worker(s,{type:`module`});return c.addEventListener(`message`,()=>{URL.revokeObjectURL(s)},{once:!0}),c.addEventListener(`error`,()=>{URL.revokeObjectURL(s)},{once:!0}),c}async getOrCreateWorkerPromiser(e){if(this.workerPromiser)return this.workerPromiser;if(this.workerPromiserInitPromise)return await this.workerPromiserInitPromise;let t=await this.loadSQLiteWorkerPromiserFactory(),n=this.config.storageBackend===`opfs`||this.config.opfsVfsType===`opfs`||this.config.opfsVfsType===`sahpool`,r=this.config.loadStrategy===`global`&&!n?5e3:2e4;this.workerPromiserInitPromise=new Promise((n,i)=>{let a=!1,o=setTimeout(()=>{a||(a=!0,i(Error(`SQLite worker initialization timed out after ${r}ms`)))},r),s=(e,t)=>{a||(a=!0,clearTimeout(o),e(t))},c=e=>{s(i,e instanceof Error?e:Error(String(e)))},l;try{l=t({...e?.worker?{worker:e.worker}:{},onready:e=>{if(typeof e==`function`){s(n,e);return}if(typeof l==`function`){s(n,l);return}if(l&&typeof l.then==`function`){l.then(e=>s(n,e),e=>c(e));return}c(Error(`sqlite3Worker1Promiser did not provide a usable promiser`))},onerror:e=>{let t=e instanceof Error?e.message:String(e);c(Error(`SQLite worker error: ${t}`))}}),l&&typeof l.then==`function`&&l.then(e=>s(n,e),e=>c(e))}catch(e){c(e)}});let i=await this.workerPromiserInitPromise;return this.workerPromiser=i,i}async tryCreateWrappedWorkerOpfsDatabase(e,t){try{let n=!this.workerPromiser&&!this.workerPromiserInitPromise?(()=>{let e=this.createWorkerFromConfig();if(e)return{worker:e};if(this.config.loadStrategy===`global`)return;let n=t===`opfs-sahpool`?this.createWorkerFromCdn({installSahpool:!0}):this.createWorkerFromCdn({installSahpool:!1});return n?{worker:n}:void 0})():void 0,r=await(await this.getOrCreateWorkerPromiser(n))(`open`,{filename:`file:${e}`,vfs:t}),i=r.dbId??r.result?.dbId;if(i===void 0)throw Error(`SQLite worker did not return a dbId`);if(typeof i!=`number`&&typeof i!=`string`)throw Error(`SQLite worker returned an invalid dbId (type=${typeof i})`);return this.db=void 0,this.workerDbId=i,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=t,m.log(`Successfully created OPFS-backed database via wrapped worker (${t}):`,e),!0}catch(e){return m.warn(`Wrapped-worker OPFS database creation failed (vfs=${t}):`,e),!1}}},j=class{constructor(e,t,n){this.dbHost=e,this.hostElection=t,this.activeTxn=null,this.txnQueue=[],this.heartbeatTimers=new Map,this.txnPending=!1,this.rpcQueue=[],this.txnTimeoutMs=n?.txnTimeoutMs??3e4,this.opTimeoutMs=n?.opTimeoutMs??1e4,this.maxQueueSize=n?.maxQueueSize??10,this.heartbeatTimeoutMs=n?.heartbeatTimeoutMs??15e3}startWatchdog(){this.watchdogTimer||=setInterval(()=>{if(this.activeTxn){let e=Date.now()-this.activeTxn.lastActivityAt;e>this.txnTimeoutMs&&(f.warn(`Transaction ${this.activeTxn.txnId} timed out after ${e}ms, rolling back`),this.forceRollback())}let e=Date.now(),t=this.txnQueue.filter(t=>e-t.enqueuedAt>this.txnTimeoutMs);for(let e of t){let t=this.txnQueue.indexOf(e);t!==-1&&(this.txnQueue.splice(t,1),e.reject(Error(`Transaction queue wait timeout`)))}},5e3)}stopWatchdog(){this.watchdogTimer&&=(clearInterval(this.watchdogTimer),void 0)}getActiveTransaction(){return this.activeTxn}hasActiveTransaction(){return this.activeTxn!==null}isTransactionBlocking(){return this.activeTxn!==null||this.txnPending}async waitForTransactionSlot(){if(this.isTransactionBlocking())return new Promise((e,t)=>{this.rpcQueue.push({resolve:e,reject:t})})}async beginTransaction(e,t,n=`immediate`){if(this.activeTxn||this.txnPending){if(this.txnQueue.length>=this.maxQueueSize)throw Error(`Transaction queue full`);await new Promise((r,i)=>{this.txnQueue.push({txnId:e,originTabId:t,mode:n,resolve:r,reject:i,enqueuedAt:Date.now()})})}this.txnPending=!0;try{this.txnLockRelease=await this.hostElection.acquireTransactionLock();let r=n===`deferred`?`BEGIN`:n===`exclusive`?`BEGIN EXCLUSIVE`:`BEGIN IMMEDIATE`;await this.dbHost.execRaw(r),this.activeTxn={txnId:e,originTabId:t,startedAt:Date.now(),lastActivityAt:Date.now(),mode:n},this.resetHeartbeatTimer(e)}catch(e){throw this.txnLockRelease?.(),this.txnLockRelease=void 0,this.txnPending=!1,this.processQueue(),this.processRpcQueue(),e}finally{this.txnPending=!1}}async execInTransaction(e,t){return this.validateActiveTxn(e),this.activeTxn.lastActivityAt=Date.now(),this.resetHeartbeatTimer(e),await t()}recordHeartbeat(e){this.activeTxn?.txnId===e&&(this.activeTxn.lastActivityAt=Date.now(),this.resetHeartbeatTimer(e))}async commitTransaction(e){this.validateActiveTxn(e);try{await this.dbHost.execRaw(`COMMIT`)}finally{this.endTransaction()}}async rollbackTransaction(e){this.validateActiveTxn(e);try{await this.dbHost.execRaw(`ROLLBACK`)}finally{this.endTransaction()}}forceRollback(){if(this.activeTxn){try{this.dbHost.execRaw(`ROLLBACK`).catch(e=>{f.error(`Force rollback failed:`,e)})}catch(e){f.error(`Force rollback failed:`,e)}this.endTransaction()}}endTransaction(){if(this.activeTxn){let e=this.heartbeatTimers.get(this.activeTxn.txnId);e&&(clearTimeout(e),this.heartbeatTimers.delete(this.activeTxn.txnId)),this.activeTxn=null}this.txnLockRelease&&=(this.txnLockRelease(),void 0),this.processQueue(),this.processRpcQueue()}processQueue(){this.txnQueue.length>0&&!this.activeTxn&&!this.txnPending&&this.txnQueue.shift().resolve()}processRpcQueue(){if(this.activeTxn||this.txnPending)return;let e=this.rpcQueue;this.rpcQueue=[];for(let{resolve:t}of e)t()}resetHeartbeatTimer(e){let t=this.heartbeatTimers.get(e);t&&clearTimeout(t);let n=setTimeout(()=>{this.activeTxn?.txnId===e&&(f.warn(`No heartbeat for transaction ${e} in ${this.heartbeatTimeoutMs}ms, rolling back`),this.forceRollback())},this.heartbeatTimeoutMs);this.heartbeatTimers.set(e,n)}validateActiveTxn(e){if(!this.activeTxn)throw Error(`No active transaction (expected ${e})`);if(this.activeTxn.txnId!==e)throw Error(`Transaction ID mismatch: expected ${this.activeTxn.txnId}, got ${e}`);let t=Date.now()-this.activeTxn.lastActivityAt;if(t>this.txnTimeoutMs)throw this.forceRollback(),Error(`Transaction ${e} timed out after ${t}ms`)}async close(){this.stopWatchdog(),this.activeTxn&&this.forceRollback();for(let e of this.txnQueue)e.reject(Error(`Transaction session manager closed`));this.txnQueue=[];for(let e of this.rpcQueue)e.reject(Error(`Transaction session manager closed`));this.rpcQueue=[];for(let e of this.heartbeatTimers.values())clearTimeout(e);this.heartbeatTimers.clear()}},M=class{constructor(e,t,n=5e3){this.rpcChannel=e,this.txnId=t,this.heartbeatIntervalMs=n,this.committed=!1,this.rolledBack=!1,this.startHeartbeat()}startHeartbeat(){this.heartbeatInterval=setInterval(()=>{!this.committed&&!this.rolledBack&&this.rpcChannel.txnHeartbeat(this.txnId)},this.heartbeatIntervalMs)}stopHeartbeat(){this.heartbeatInterval&&=(clearInterval(this.heartbeatInterval),void 0)}ensureActive(){if(this.committed)throw Error(`Transaction already committed`);if(this.rolledBack)throw Error(`Transaction already rolled back`)}async query(e,t){return this.ensureActive(),(await this.rpcChannel.txnExec(this.txnId,`query`,{sql:e,params:t})).rows}async queryRaw(e,t){return this.ensureActive(),await this.rpcChannel.txnExec(this.txnId,`queryRaw`,{sql:e,params:t})}async run(e,t){this.ensureActive();let n=await this.rpcChannel.txnExec(this.txnId,`run`,{sql:e,params:t});return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){this.ensureActive(),await this.rpcChannel.txnExec(this.txnId,`exec`,{sql:e})}async transaction(e){this.ensureActive();let t=e(this);return t instanceof Promise?await t:t}async asyncTransaction(e,t){return this.ensureActive(),await e(this)}getConnectionType(){return`asyncTxn`}async close(){}async _commit(){this.ensureActive(),this.stopHeartbeat();try{await this.rpcChannel.txnCommit(this.txnId),this.committed=!0}catch(e){throw this.rolledBack=!0,e}}async _rollback(){if(!(this.committed||this.rolledBack)){this.stopHeartbeat();try{await this.rpcChannel.txnRollback(this.txnId)}finally{this.rolledBack=!0}}}isCommitted(){return this.committed}isRolledBack(){return this.rolledBack}},N=class{constructor(e,t){this.dbHost=e,this.isAsync=t}async query(e,t){return(await this.dbHost.executeSqlRaw(e,t)).rows}async queryRaw(e,t){return await this.dbHost.executeSqlRaw(e,t)}async run(e,t){let n=await this.dbHost.executeSqlRaw(e,t);return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){await this.dbHost.execRaw(e)}async transaction(e){let t=e(this);return t instanceof Promise?await t:t}async asyncTransaction(e){return await e(this)}getConnectionType(){return this.isAsync?`asyncTxn`:`syncTxn`}async close(){}},P=class t extends e.t{constructor(e){let t=e.path??e.name??`default`;super({...e,path:t}),this.dbHost=null,this.txnSessionManager=null,this.initialized=!1,this.closed=!1,this.roleListeners=new Set,this.roleValue=`unknown`,this.options={...e,path:t},this.tabId=w(),this.sqliteConfig={loadStrategy:`global`,storageBackend:`auto`,opfsVfsType:`auto`,cdnBaseUrl:`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm`,version:`3.50.1-build1`,bundlerFriendly:!1,...e.sqlite},_.isSupported()||(p.warn(`Web Locks API not supported. Falling back to memory-only storage.`),this.sqliteConfig.storageBackend=`memory`),this.visibilityManager=new v,this.rpcChannel=new E(t,this.tabId),this.hostElection=new _({dbName:t,tabId:this.tabId,onBecomeHost:()=>this.becomeHost(),onLoseHost:()=>this.loseHost(),checkVisibility:()=>this.visibilityManager.isVisible()}),this.visibilityUnsubscribe=this.visibilityManager.onVisibilityChange(e=>{this.handleVisibilityChange(e)})}static async create(e){let n=new t(e);return await n.initialize(),n}getTabId(){return this.tabId}get isHost(){return this.hostElection.isHost}getRole(){return this.roleValue}subscribeRoleChange(e){this.roleListeners.add(e);try{e(this.roleValue)}catch(e){p.error(`Role change listener threw:`,e)}return()=>{this.roleListeners.delete(e)}}async waitForRole(e,t=3e4){this.roleValue!==e&&await new Promise((n,r)=>{let i=t=>{t===e&&(a(),n())},a=()=>{clearTimeout(o),this.roleListeners.delete(i)},o=setTimeout(()=>{a(),r(Error(`Timed out waiting for role '${e}' after ${t}ms`))},t);this.roleListeners.add(i),this.roleValue===e&&(a(),n())})}async initialize(){if(!this.initialized){if(this.initPromise)return this.initPromise;this.initPromise=this.doInitialize(),await this.initPromise,this.initialized=!0,this.refreshRole()}}async doInitialize(){this.becomeHostPromise=new Promise(e=>{this.becomeHostResolver=e}),await this.hostElection.start();let e=new Promise(e=>{setTimeout(()=>e(`timeout`),500)});await Promise.race([this.becomeHostPromise.then(()=>`host`),e])===`host`||this.hostElection.isHost&&await this.becomeHostPromise}async becomeHost(){p.log(`Tab ${this.tabId} becoming Host for ${this.options.path}`),this.dbHost=await A.create({dbName:this.options.path,config:this.sqliteConfig}),this.txnSessionManager=new j(this.dbHost,this.hostElection),this.txnSessionManager.startWatchdog(),this.rpcChannel.setRequestHandler(this.handleRequest.bind(this)),this.becomeHostResolver&&=(this.becomeHostResolver(),void 0),this.refreshRole()}loseHost(){p.log(`Tab ${this.tabId} losing Host for ${this.options.path}`),this.rpcChannel.setRequestHandler(void 0),this.txnSessionManager&&=(this.txnSessionManager.close(),null),this.dbHost&&=(this.dbHost.close(),null),this.refreshRole()}handleVisibilityChange(e){e&&!this.hostElection.isHost&&!this.closed&&this.hostElection.start()}async handleRequest(e){if(!this.dbHost)throw Error(`Not Host`);switch(e.t){case`req`:return await this.handleRpcRequest(e);case`txn_begin`:return await this.handleTxnBegin(e);case`txn_exec`:return await this.handleTxnExec(e);case`txn_commit`:return await this.handleTxnCommit(e);case`txn_rollback`:return await this.handleTxnRollback(e);case`txn_heartbeat`:this.handleTxnHeartbeat(e);return;default:throw Error(`Unknown request type: ${e.t}`)}}async handleRpcRequest(e){let{op:t,payload:n}=e,{sql:r,params:i}=n;switch(this.txnSessionManager&&await this.txnSessionManager.waitForTransactionSlot(),t){case`query`:return{rows:await this.dbHost.query(r,i),columns:[]};case`queryRaw`:return await this.dbHost.queryRaw(r,i);case`run`:return await this.dbHost.run(r,i);case`exec`:return await this.dbHost.exec(r),null;default:throw Error(`Unknown RPC operation: ${String(t)}`)}}async handleTxnBegin(e){await this.txnSessionManager.beginTransaction(e.txnId,e.from,e.mode)}async handleTxnExec(e){let{txnId:t,op:n,payload:r}=e,{sql:i,params:a}=r;return await this.txnSessionManager.execInTransaction(t,async()=>{switch(n){case`query`:return{rows:await this.dbHost.query(i,a),columns:[]};case`queryRaw`:return await this.dbHost.queryRaw(i,a);case`run`:return await this.dbHost.run(i,a);case`exec`:return await this.dbHost.exec(i),null;default:throw Error(`Unknown transaction operation: ${String(n)}`)}})}async handleTxnCommit(e){await this.txnSessionManager.commitTransaction(e.txnId)}async handleTxnRollback(e){await this.txnSessionManager.rollbackTransaction(e.txnId)}handleTxnHeartbeat(e){this.txnSessionManager?.recordHeartbeat(e.txnId)}async query(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.query(e,t):(await this.rpcChannel.request(`query`,{sql:e,params:t})).rows}async queryRaw(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.queryRaw(e,t):await this.rpcChannel.request(`queryRaw`,{sql:e,params:t})}async run(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.run(e,t):await this.rpcChannel.request(`run`,{sql:e,params:t})}async exec(e){if(await this.initialize(),this.isHost&&this.dbHost){await this.dbHost.exec(e);return}await this.rpcChannel.request(`exec`,{sql:e})}async transaction(e){return await this.initialize(),this.isHost&&this.dbHost?await this.executeLocalTransaction(e,!1):await this.executeRemoteTransaction(e)}async asyncTransaction(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.executeLocalAsyncTransaction(e,t):await this.executeRemoteTransaction(e,t)}getConnectionType(){return`direct`}async close(){this.closed||(this.closed=!0,this.refreshRole(),this.visibilityUnsubscribe?.(),await this.hostElection.close(),this.rpcChannel.close(),this.visibilityManager.destroy(),this.txnSessionManager&&=(await this.txnSessionManager.close(),null),this.dbHost&&=(await this.dbHost.close(),null))}computeRole(){return this.closed?`unknown`:this.hostElection.isHost?this.dbHost?`host`:`unknown`:this.initialized?`participant`:`unknown`}refreshRole(){let e=this.computeRole();e!==this.roleValue&&(this.roleValue=e,this.roleListeners.forEach(t=>{try{t(e)}catch(e){p.error(`Role change listener threw:`,e)}}))}async executeLocalTransaction(e,t){await this.dbHost.execRaw(`BEGIN`);let n=new N(this.dbHost,t);try{let t=e(n),r=t instanceof Promise?await t:t;return await this.dbHost.execRaw(`COMMIT`),r}catch(e){try{await this.dbHost.execRaw(`ROLLBACK`)}catch(e){p.error(`Rollback failed:`,e)}throw e}}async executeLocalAsyncTransaction(e,t){let n=t?.timeoutMs??3e4;await this.dbHost.execRaw(`BEGIN IMMEDIATE`);let r=new N(this.dbHost,!0),i,a=!1;try{let t=new Promise((e,t)=>{i=setTimeout(()=>{a||t(Error(`Async transaction timeout after ${n}ms`))},n)}),o=await Promise.race([e(r),t]);return a=!0,i&&clearTimeout(i),await this.dbHost.execRaw(`COMMIT`),o}catch(e){a=!0,i&&clearTimeout(i);try{await this.dbHost.execRaw(`ROLLBACK`)}catch(e){p.error(`Rollback failed:`,e)}throw e}}async executeRemoteTransaction(e,t){let n=T(),r=t?.timeoutMs??3e4;await this.rpcChannel.txnBegin(n,`immediate`,r);let i=new M(this.rpcChannel,n),a,o=!1;try{let t=new Promise((e,t)=>{a=setTimeout(()=>{o||t(Error(`Remote transaction timeout after ${r}ms`))},r)}),n=e(i),s=n instanceof Promise?n:Promise.resolve(n),c=await Promise.race([s,t]);return o=!0,a&&clearTimeout(a),await i._commit(),c}catch(e){o=!0,a&&clearTimeout(a);try{await i._rollback()}catch(e){p.error(`Remote rollback failed:`,e)}throw e}}getSQLiteInfo(){let e=this.isHost?this.dbHost!==null:this.initialized&&!this.closed;return{config:this.sqliteConfig,isInitialized:this.initialized,isHost:this.isHost,isReady:e,tabId:this.tabId,hasDatabase:this.dbHost!==null,usesWorker:this.dbHost?.isWorkerDbActive()??!1,activeStorageBackend:this.dbHost?.getActiveStorageBackend()??`unknown`,activeOpfsVfs:this.dbHost?.getActiveOpfsVfs()}}getActiveStorageBackend(){return this.dbHost?.getActiveStorageBackend()??`unknown`}};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return E}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return w}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return S}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return A}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return T}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return _}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return b}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return C}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return P}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return x}}); | ||
| //# sourceMappingURL=browser2.cjs.map |
@@ -1,8 +0,8 @@ | ||
| import{t as e}from"./base.mjs";import t from"debug";const n=`unisqlite`;function r(e){let r=t(`${n}:${e}`),i=t(`${n}:${e}:warn`),a=t(`${n}:${e}:error`);return a.enabled=!0,{log:r,warn:i,error:a}}const i=r(`host`),a=r(`txn`),o=r(`adapter`),s=r(`wasm`),c=r(`visibility`);var l=class e{constructor(e){this.options=e,this.abortController=null,this._isHost=!1,this._isStarted=!1,this.releaseResolver=null,this.hostReadyResolver=null,this.hostReadyPromise=null,this.lockName=`sqlite:host:${e.dbName}`,this.txnLockName=`sqlite:txn:${e.dbName}`}static isSupported(){return typeof navigator<`u`&&navigator.locks!==void 0}get isHost(){return this._isHost}async start(){if(!this._isStarted){if(!e.isSupported()){i.warn(`Web Locks API not supported. Running in local-only mode.`),this._isStarted=!0,this._isHost=!0,this.options.onBecomeHost().catch(e=>{i.error(`Error in onBecomeHost callback:`,e),this._isHost=!1});return}this.options.checkVisibility&&!this.options.checkVisibility()||(this._isStarted=!0,this.hostReadyPromise=new Promise(e=>{this.hostReadyResolver=e}),this.tryAcquireHost().catch(e=>{i.error(`Error in host election:`,e)}))}}async waitForHost(){if(!this._isHost&&(this.hostReadyPromise||(await this.start(),this.hostReadyPromise)))return this.hostReadyPromise}async tryAcquireHost(){this.abortController=new AbortController;try{await navigator.locks.request(this.lockName,{mode:`exclusive`,signal:this.abortController.signal},async e=>{if(e){this._isHost=!0,i.log(`Tab ${this.options.tabId} became Host for ${this.options.dbName}`);try{await this.options.onBecomeHost()}catch(e){throw i.error(`Error in onBecomeHost callback:`,e),this._isHost=!1,e}this.hostReadyResolver&&(this.hostReadyResolver(),this.hostReadyResolver=null,this.hostReadyPromise=null),await new Promise(e=>{this.releaseResolver=e,this.abortController.signal.addEventListener(`abort`,()=>{e()})}),this._isHost=!1,this.releaseResolver=null,i.log(`Tab ${this.options.tabId} lost Host role for ${this.options.dbName}`);try{this.options.onLoseHost()}catch(e){i.error(`Error in onLoseHost callback:`,e)}}})}catch(e){if(e.name===`AbortError`)return;throw i.error(`Error acquiring Host lock:`,e),e}finally{this._isStarted=!1,this.abortController=null}}releaseHost(){this.releaseResolver&&=(this.releaseResolver(),null),this.abortController&&=(this.abortController.abort(),null)}async acquireTransactionLock(){if(!e.isSupported())return()=>{};let t=new AbortController,n=null,r=null,a=new Promise(e=>{r=e});return navigator.locks.request(this.txnLockName,{mode:`exclusive`,signal:t.signal},async e=>{e&&(r?.(),await new Promise(e=>{n=e}))}).catch(e=>{e.name!==`AbortError`&&i.error(`Error acquiring transaction lock:`,e),r?.()}),await a,()=>{n?n():t.abort()}}async hasTransactionLock(){if(!e.isSupported())return!1;try{return(await navigator.locks.query()).held?.some(e=>e.name===this.txnLockName)??!1}catch{return!1}}async close(){this.releaseHost()}},u=class{constructor(){this.listeners=new Set,this.boundHandler=this.handleVisibilityChange.bind(this),typeof document<`u`&&document.addEventListener(`visibilitychange`,this.boundHandler)}handleVisibilityChange(){let e=this.isVisible();this.listeners.forEach(t=>{try{t(e)}catch(e){c.error(`Error in visibility change listener:`,e)}})}isVisible(){return typeof document>`u`?!0:document.visibilityState===`visible`}onVisibilityChange(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}destroy(){typeof document<`u`&&document.removeEventListener(`visibilitychange`,this.boundHandler),this.listeners.clear()}};function d(e){return e instanceof Error?{name:e.name,message:e.message,stack:e.stack}:{name:`Error`,message:String(e)}}function f(e){let t=Error(e.message);return t.name=e.name,e.stack&&(t.stack=e.stack),t}function p(e){return e.t===`req`||e.t===`txn_begin`||e.t===`txn_exec`||e.t===`txn_commit`||e.t===`txn_rollback`||e.t===`txn_heartbeat`}function m(e){return e.t===`res`||e.t===`txn_ack`||e.t===`txn_result`||e.t===`txn_done`||e.t===`txn_error`}function h(){return crypto.randomUUID()}function g(){return crypto.randomUUID()}function _(){return crypto.randomUUID()}var v=class{constructor(e,t,n=3e4){this.dbName=e,this.tabId=t,this.defaultTimeoutMs=n,this.pending=new Map,this.closed=!1,this.channel=new BroadcastChannel(`sqlite:rpc:${e}`),this.channel.onmessage=this.handleMessage.bind(this)}handleMessage(e){if(this.closed)return;let t=e.data;!t||typeof t.t!=`string`||(m(t)?this.handleResponse(t):p(t)&&this.requestHandler&&`from`in t&&t.from!==this.tabId&&this.handleRequest(t))}handleResponse(e){if(`to`in e&&e.to!==this.tabId||!(`id`in e))return;let t=this.pending.get(e.id);if(t)switch(clearTimeout(t.timer),this.pending.delete(e.id),e.t){case`res`:e.ok?t.resolve(e.result):t.reject(f(e.error));break;case`txn_ack`:case`txn_done`:t.resolve(void 0);break;case`txn_result`:e.ok?t.resolve(e.result):t.reject(f(e.error));break;case`txn_error`:t.reject(f(e.error));break}}async handleRequest(e){if(this.requestHandler)try{let t=await this.requestHandler(e);this.sendResponse(e,t)}catch(t){this.sendErrorResponse(e,t)}}sendResponse(e,t){if(this.closed)return;let n=`from`in e?e.from:void 0,r=`id`in e?e.id:void 0;if(!(!n||!r))switch(e.t){case`req`:this.channel.postMessage({t:`res`,to:n,id:r,ok:!0,result:t});break;case`txn_begin`:this.channel.postMessage({t:`txn_ack`,to:n,id:r,txnId:e.txnId});break;case`txn_exec`:this.channel.postMessage({t:`txn_result`,to:n,id:r,txnId:e.txnId,ok:!0,result:t});break;case`txn_commit`:case`txn_rollback`:this.channel.postMessage({t:`txn_done`,to:n,id:r,txnId:e.txnId});break;case`txn_heartbeat`:break}}sendErrorResponse(e,t){if(this.closed)return;let n=`from`in e?e.from:void 0,r=`id`in e?e.id:void 0;if(!n||!r)return;let i=d(t);switch(e.t){case`req`:this.channel.postMessage({t:`res`,to:n,id:r,ok:!1,error:i});break;case`txn_begin`:case`txn_exec`:case`txn_commit`:case`txn_rollback`:this.channel.postMessage({t:`txn_error`,to:n,id:r,txnId:e.txnId,error:i});break}}setRequestHandler(e){this.requestHandler=e}async request(e,t,n){let r=h(),i=n??this.defaultTimeoutMs;return new Promise((n,a)=>{let o=setTimeout(()=>{this.pending.delete(r),a(Error(`RPC timeout after ${i}ms for ${e}`))},i);this.pending.set(r,{resolve:n,reject:a,timer:o}),this.channel.postMessage({t:`req`,from:this.tabId,id:r,op:e,payload:t})})}async txnBegin(e,t=`immediate`,n){let r=h(),i=n??this.defaultTimeoutMs;return new Promise((n,a)=>{let o=setTimeout(()=>{this.pending.delete(r),a(Error(`Transaction begin timeout after ${i}ms`))},i);this.pending.set(r,{resolve:n,reject:a,timer:o}),this.channel.postMessage({t:`txn_begin`,from:this.tabId,id:r,txnId:e,mode:t})})}async txnExec(e,t,n,r){let i=h(),a=r??1e4;return new Promise((r,o)=>{let s=setTimeout(()=>{this.pending.delete(i),o(Error(`Transaction SQL timeout after ${a}ms`))},a);this.pending.set(i,{resolve:r,reject:o,timer:s}),this.channel.postMessage({t:`txn_exec`,from:this.tabId,id:i,txnId:e,op:t,payload:n})})}async txnCommit(e,t){let n=h(),r=t??1e4;return new Promise((t,i)=>{let a=setTimeout(()=>{this.pending.delete(n),i(Error(`Transaction commit timeout after ${r}ms`))},r);this.pending.set(n,{resolve:t,reject:i,timer:a}),this.channel.postMessage({t:`txn_commit`,from:this.tabId,id:n,txnId:e})})}async txnRollback(e,t){let n=h(),r=t??1e4;return new Promise((t,i)=>{let a=setTimeout(()=>{this.pending.delete(n),i(Error(`Transaction rollback timeout after ${r}ms`))},r);this.pending.set(n,{resolve:t,reject:i,timer:a}),this.channel.postMessage({t:`txn_rollback`,from:this.tabId,id:n,txnId:e})})}txnHeartbeat(e){this.closed||this.channel.postMessage({t:`txn_heartbeat`,from:this.tabId,txnId:e})}close(){this.closed=!0,this.channel.close();for(let e of this.pending.values())clearTimeout(e.timer),e.reject(Error(`RPC channel closed`));this.pending.clear()}};const y=new Map,b=new Map;var x=class e{constructor(e,t){this.dbName=e,this.config=t,this.workerQueue=Promise.resolve(),this.activeStorageBackend=`memory`,this.closed=!1}static async create(t){let n=new e(t.dbName,t.config);return await n.initialize(),n}async initialize(){s.log(`UniSQLite v0.4.0 - Loading SQLite WASM using strategy: ${this.config.loadStrategy}`);let e=await this.loadSQLiteWasm(),t={print:(...e)=>s.log(...e),printErr:(...e)=>s.error(...e)};if(this.config.locateFile)t.locateFile=this.config.locateFile;else if(typeof window<`u`){let e=window;e.sqlite3InitModuleState?.locateFile&&(t.locateFile=e.sqlite3InitModuleState.locateFile)}if(s.log(`Initializing SQLite WASM module...`),this.sqlite3=await e(t),!this.sqlite3)throw Error(`Failed to initialize SQLite WASM module`);let n=this.sqlite3;s.log(`SQLite WASM module initialized successfully`);let r=this.config.storageBackend??`auto`,i=this.dbName===`:memory:`?`:memory:`:`/unisqlite/${this.dbName}`;r===`memory`||this.dbName===`:memory:`?(s.log(`Using in-memory database`),this.db=new n.oo1.DB(`:memory:`),this.activeStorageBackend=`memory`):await this.tryCreatePersistentDatabase(n,i,r)||(s.log(`All persistent storage backends failed, using in-memory database`),this.db=new n.oo1.DB(`:memory:`),this.activeStorageBackend=`memory`);try{await this.execInternal(`PRAGMA journal_mode=WAL`),s.log(`Enabled WAL mode`)}catch(e){s.warn(`Could not enable WAL mode:`,e)}}async query(e,t){return(await this.executeSql(e,t)).rows}async queryRaw(e,t){return await this.executeSql(e,t)}async run(e,t){let n=await this.executeSql(e,t);return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){await this.execInternal(e)}async execRaw(e){await this.execInternal(e,{bypassQueue:!0})}async executeSqlRaw(e,t){return await this.executeSql(e,t,{bypassQueue:!0})}getActiveStorageBackend(){return this.activeStorageBackend}getActiveOpfsVfs(){return this.activeOpfsVfs}isWorkerDbActive(){return!!this.workerPromiser&&this.workerDbId!==void 0}async close(){if(!this.closed){if(this.closed=!0,this.workerPromiser){let e=this.workerPromiser,t=this.workerDbId;try{t!==void 0&&await this.enqueueWorker(async()=>{await e(`close`,{dbId:t})})}catch(e){s.warn(`Failed to close SQLite worker database:`,e)}finally{this.workerDbId=void 0;let t=e.worker;t?.terminate&&t.terminate(),this.workerPromiser=void 0}}this.db&&=(this.db.close(),void 0)}}enqueueWorker(e){let t=async()=>e(),n=this.workerQueue.then(t,t);return this.workerQueue=n.then(()=>void 0,()=>void 0),n}normalizeParams(e){if(!e)return;if(Array.isArray(e))return e;let t=Object.keys(e),n=e;return t.map(e=>n[e])}async execInternal(e,t){if(this.isWorkerDbActive()){let n=async()=>{if(!this.workerPromiser||this.workerDbId===void 0)throw Error(`Database not initialized`);await this.workerPromiser(`exec`,{dbId:this.workerDbId,sql:e})};t?.bypassQueue?await n():await this.enqueueWorker(n);return}if(!this.db)throw Error(`Database not initialized`);this.db.exec(e)}async executeSql(e,t,n){try{if(this.isWorkerDbActive()){let r=async()=>{if(!this.workerPromiser||this.workerDbId===void 0)throw Error(`Database not initialized`);let n=this.normalizeParams(t),r={dbId:this.workerDbId,sql:e,rowMode:`object`,resultRows:[],columnNames:[],countChanges:!0};n&&(r.bind=n);let i=await this.workerPromiser(`exec`,r),a=i.result??i,o=Array.isArray(a.resultRows)?a.resultRows:[],s=Array.isArray(a.columnNames)?a.columnNames:[],c=typeof a.changeCount==`number`?a.changeCount:0,l={dbId:this.workerDbId,sql:`SELECT last_insert_rowid() AS lastInsertRowId`,rowMode:`object`,resultRows:[],columnNames:[]},u=await this.workerPromiser(`exec`,l),d=u.result??u,f=Array.isArray(d.resultRows)?d.resultRows:[];return{rows:o,columns:s,rowsAffected:c,lastInsertRowId:Number(f[0]?.lastInsertRowId??0)}};return n?.bypassQueue?await r():await this.enqueueWorker(r)}if(!this.db||!this.sqlite3)throw Error(`Database not initialized`);let r=this.normalizeParams(t),i=[],a=[],o=this.db.prepare(e);try{let e=o;r&&o.bind(r);let t=typeof e.getColumnCount==`function`?e.getColumnCount():typeof e.columnCount==`number`?e.columnCount:0;if(t>0)if(typeof e.getColumnNames==`function`)a=e.getColumnNames();else for(let e=0;e<t;e++)a.push(o.getColumnName(e));for(;o.step();){let e={};for(let n=0;n<t;n++)e[a[n]]=o.get(n);i.push(e)}return{rows:i,columns:a,rowsAffected:this.db.changes(),lastInsertRowId:Number(this.sqlite3.capi.sqlite3_last_insert_rowid(this.db.pointer)||0)}}finally{o.finalize()}}catch(e){let t=e instanceof Error?e.message:String(e),n=Error(`SQLite error: ${t}`);throw e instanceof Error&&(n.cause=e),n}}async loadSQLiteWasm(){let e=`${this.config.loadStrategy}-${this.config.wasmUrl||this.config.cdnBaseUrl}-${this.config.version}`;if(y.has(e))return y.get(e);let t=this.loadSQLiteWasmInternal();return y.set(e,t),t}async loadSQLiteWasmInternal(){let{loadStrategy:e,wasmUrl:t,cdnBaseUrl:n,version:r,bundlerFriendly:i}=this.config;switch(e){case`npm`:return await this.loadFromNpm();case`cdn`:return await this.loadFromCdn(n,r,i);case`url`:if(!t)throw Error(`wasmUrl must be provided when using 'url' loading strategy`);return await this.loadFromUrl(t);case`module`:return await this.loadAsModule();case`global`:default:return this.loadFromGlobal()}}async loadFromNpm(){try{let e=await import(`@sqlite.org/sqlite-wasm`);return e.default||e}catch(e){return s.warn(`Failed to load SQLite WASM from npm, falling back to CDN:`,e),this.loadFromCdn(this.config.cdnBaseUrl,this.config.version,this.config.bundlerFriendly)}}async loadFromCdn(e,t,n){let r=n?`sqlite3-bundler-friendly.mjs`:`index.mjs`,i=[`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm@${t}/${r}`,`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm@${t}/dist/${r}`],a;for(let e of i)try{s.log(`Trying to load SQLite WASM from: ${e}`);let t=await import(e);return s.log(`Successfully loaded SQLite WASM from: ${e}`),t.default||t}catch(t){a=t,s.warn(`Failed to load SQLite WASM from ${e}:`,t)}let o=a instanceof Error?a.message:String(a);throw Error(`Failed to load SQLite WASM from any CDN source. Last error: ${o}`)}async loadFromUrl(e){let t=await import(e);return t.default||t.sqlite3InitModule}async loadAsModule(){if(globalThis.importScripts!==void 0)throw Error(`ES6 module loading not supported in Web Worker context`);for(let e of[`./sqlite3.mjs`,`./sqlite3-bundler-friendly.mjs`,`/sqlite3.mjs`])try{let t=await import(e);return t.default||t.sqlite3InitModule}catch(t){s.warn(`Failed to load SQLite WASM from ${e}:`,t)}throw Error(`Could not load SQLite WASM as ES6 module from any known path`)}loadFromGlobal(){if(typeof window<`u`){let e=window;if(e.sqlite3InitModule)return e.sqlite3InitModule}throw Error(`SQLite WASM module not found globally. Please: | ||
| import{t as e}from"./base.mjs";const t=`unisqlite`;let n=``,r=[];function i(e){try{return typeof localStorage<`u`?localStorage.getItem(e):null}catch{return null}}function a(e){return e.split(/[\s,]+/g).map(e=>e.trim()).filter(Boolean).map(e=>{let t=e.replace(/[.*+?^${}()|[\]\\]/g,`\\$&`).replace(/\\\*/g,`.*`);return RegExp(`^${t}$`)})}function o(e){return r.some(t=>t.test(e))}function s(e){return`[${e}]`}function c(e,t){if(t===`error`)return(...t)=>console.error(s(e),...t);let n=t===`warn`?console.warn.bind(console):console.log.bind(console);return(...t)=>{o(e)&&n(s(e),...t)}}function l(e){let n=`${t}:${e}`,r=`${t}:${e}:warn`,i=`${t}:${e}:error`;return{log:c(n,`log`),warn:c(r,`warn`),error:c(i,`error`)}}const u=l(`host`),d=l(`txn`),f=l(`adapter`),p=l(`wasm`),m=l(`visibility`);function h(){let e=i(`debug`)??``;e!==n&&(n=e,r=a(e))}h();var g=class e{constructor(e){this.options=e,this.abortController=null,this._isHost=!1,this._isStarted=!1,this.releaseResolver=null,this.hostReadyResolver=null,this.hostReadyPromise=null,this.lockName=`sqlite:host:${e.dbName}`,this.txnLockName=`sqlite:txn:${e.dbName}`}static isSupported(){return typeof navigator<`u`&&navigator.locks!==void 0}get isHost(){return this._isHost}async start(){if(!this._isStarted){if(!e.isSupported()){u.warn(`Web Locks API not supported. Running in local-only mode.`),this._isStarted=!0,this._isHost=!0,this.options.onBecomeHost().catch(e=>{u.error(`Error in onBecomeHost callback:`,e),this._isHost=!1});return}this.options.checkVisibility&&!this.options.checkVisibility()||(this._isStarted=!0,this.hostReadyPromise=new Promise(e=>{this.hostReadyResolver=e}),this.tryAcquireHost().catch(e=>{u.error(`Error in host election:`,e)}))}}async waitForHost(){if(!this._isHost&&(this.hostReadyPromise||(await this.start(),this.hostReadyPromise)))return this.hostReadyPromise}async tryAcquireHost(){this.abortController=new AbortController;try{await navigator.locks.request(this.lockName,{mode:`exclusive`,signal:this.abortController.signal},async e=>{if(e){this._isHost=!0,u.log(`Tab ${this.options.tabId} became Host for ${this.options.dbName}`);try{await this.options.onBecomeHost()}catch(e){throw u.error(`Error in onBecomeHost callback:`,e),this._isHost=!1,e}this.hostReadyResolver&&(this.hostReadyResolver(),this.hostReadyResolver=null,this.hostReadyPromise=null),await new Promise(e=>{this.releaseResolver=e,this.abortController.signal.addEventListener(`abort`,()=>{e()})}),this._isHost=!1,this.releaseResolver=null,u.log(`Tab ${this.options.tabId} lost Host role for ${this.options.dbName}`);try{this.options.onLoseHost()}catch(e){u.error(`Error in onLoseHost callback:`,e)}}})}catch(e){if(e.name===`AbortError`)return;throw u.error(`Error acquiring Host lock:`,e),e}finally{this._isStarted=!1,this.abortController=null}}releaseHost(){this.releaseResolver&&=(this.releaseResolver(),null),this.abortController&&=(this.abortController.abort(),null)}async acquireTransactionLock(){if(!e.isSupported())return()=>{};let t=new AbortController,n=null,r=null,i=new Promise(e=>{r=e});return navigator.locks.request(this.txnLockName,{mode:`exclusive`,signal:t.signal},async e=>{e&&(r?.(),await new Promise(e=>{n=e}))}).catch(e=>{e.name!==`AbortError`&&u.error(`Error acquiring transaction lock:`,e),r?.()}),await i,()=>{n?n():t.abort()}}async hasTransactionLock(){if(!e.isSupported())return!1;try{return(await navigator.locks.query()).held?.some(e=>e.name===this.txnLockName)??!1}catch{return!1}}async close(){this.releaseHost()}},_=class{constructor(){this.listeners=new Set,this.boundHandler=this.handleVisibilityChange.bind(this),typeof document<`u`&&document.addEventListener(`visibilitychange`,this.boundHandler)}handleVisibilityChange(){let e=this.isVisible();this.listeners.forEach(t=>{try{t(e)}catch(e){m.error(`Error in visibility change listener:`,e)}})}isVisible(){return typeof document>`u`?!0:document.visibilityState===`visible`}onVisibilityChange(e){return this.listeners.add(e),()=>{this.listeners.delete(e)}}destroy(){typeof document<`u`&&document.removeEventListener(`visibilitychange`,this.boundHandler),this.listeners.clear()}};function v(e){return e instanceof Error?{name:e.name,message:e.message,stack:e.stack}:{name:`Error`,message:String(e)}}function y(e){let t=Error(e.message);return t.name=e.name,e.stack&&(t.stack=e.stack),t}function b(e){return e.t===`req`||e.t===`txn_begin`||e.t===`txn_exec`||e.t===`txn_commit`||e.t===`txn_rollback`||e.t===`txn_heartbeat`}function x(e){return e.t===`res`||e.t===`txn_ack`||e.t===`txn_result`||e.t===`txn_done`||e.t===`txn_error`}function S(){return crypto.randomUUID()}function C(){return crypto.randomUUID()}function w(){return crypto.randomUUID()}var T=class{constructor(e,t,n=3e4){this.dbName=e,this.tabId=t,this.defaultTimeoutMs=n,this.pending=new Map,this.closed=!1,this.channel=new BroadcastChannel(`sqlite:rpc:${e}`),this.channel.onmessage=this.handleMessage.bind(this)}handleMessage(e){if(this.closed)return;let t=e.data;!t||typeof t.t!=`string`||(x(t)?this.handleResponse(t):b(t)&&this.requestHandler&&`from`in t&&t.from!==this.tabId&&this.handleRequest(t))}handleResponse(e){if(`to`in e&&e.to!==this.tabId||!(`id`in e))return;let t=this.pending.get(e.id);if(t)switch(clearTimeout(t.timer),this.pending.delete(e.id),e.t){case`res`:e.ok?t.resolve(e.result):t.reject(y(e.error));break;case`txn_ack`:case`txn_done`:t.resolve(void 0);break;case`txn_result`:e.ok?t.resolve(e.result):t.reject(y(e.error));break;case`txn_error`:t.reject(y(e.error));break}}async handleRequest(e){if(this.requestHandler)try{let t=await this.requestHandler(e);this.sendResponse(e,t)}catch(t){this.sendErrorResponse(e,t)}}sendResponse(e,t){if(this.closed)return;let n=`from`in e?e.from:void 0,r=`id`in e?e.id:void 0;if(!(!n||!r))switch(e.t){case`req`:this.channel.postMessage({t:`res`,to:n,id:r,ok:!0,result:t});break;case`txn_begin`:this.channel.postMessage({t:`txn_ack`,to:n,id:r,txnId:e.txnId});break;case`txn_exec`:this.channel.postMessage({t:`txn_result`,to:n,id:r,txnId:e.txnId,ok:!0,result:t});break;case`txn_commit`:case`txn_rollback`:this.channel.postMessage({t:`txn_done`,to:n,id:r,txnId:e.txnId});break;case`txn_heartbeat`:break}}sendErrorResponse(e,t){if(this.closed)return;let n=`from`in e?e.from:void 0,r=`id`in e?e.id:void 0;if(!n||!r)return;let i=v(t);switch(e.t){case`req`:this.channel.postMessage({t:`res`,to:n,id:r,ok:!1,error:i});break;case`txn_begin`:case`txn_exec`:case`txn_commit`:case`txn_rollback`:this.channel.postMessage({t:`txn_error`,to:n,id:r,txnId:e.txnId,error:i});break}}setRequestHandler(e){this.requestHandler=e}async request(e,t,n){let r=S(),i=n??this.defaultTimeoutMs;return new Promise((n,a)=>{let o=setTimeout(()=>{this.pending.delete(r),a(Error(`RPC timeout after ${i}ms for ${e}`))},i);this.pending.set(r,{resolve:n,reject:a,timer:o}),this.channel.postMessage({t:`req`,from:this.tabId,id:r,op:e,payload:t})})}async txnBegin(e,t=`immediate`,n){let r=S(),i=n??this.defaultTimeoutMs;return new Promise((n,a)=>{let o=setTimeout(()=>{this.pending.delete(r),a(Error(`Transaction begin timeout after ${i}ms`))},i);this.pending.set(r,{resolve:n,reject:a,timer:o}),this.channel.postMessage({t:`txn_begin`,from:this.tabId,id:r,txnId:e,mode:t})})}async txnExec(e,t,n,r){let i=S(),a=r??1e4;return new Promise((r,o)=>{let s=setTimeout(()=>{this.pending.delete(i),o(Error(`Transaction SQL timeout after ${a}ms`))},a);this.pending.set(i,{resolve:r,reject:o,timer:s}),this.channel.postMessage({t:`txn_exec`,from:this.tabId,id:i,txnId:e,op:t,payload:n})})}async txnCommit(e,t){let n=S(),r=t??1e4;return new Promise((t,i)=>{let a=setTimeout(()=>{this.pending.delete(n),i(Error(`Transaction commit timeout after ${r}ms`))},r);this.pending.set(n,{resolve:t,reject:i,timer:a}),this.channel.postMessage({t:`txn_commit`,from:this.tabId,id:n,txnId:e})})}async txnRollback(e,t){let n=S(),r=t??1e4;return new Promise((t,i)=>{let a=setTimeout(()=>{this.pending.delete(n),i(Error(`Transaction rollback timeout after ${r}ms`))},r);this.pending.set(n,{resolve:t,reject:i,timer:a}),this.channel.postMessage({t:`txn_rollback`,from:this.tabId,id:n,txnId:e})})}txnHeartbeat(e){this.closed||this.channel.postMessage({t:`txn_heartbeat`,from:this.tabId,txnId:e})}close(){this.closed=!0,this.channel.close();for(let e of this.pending.values())clearTimeout(e.timer),e.reject(Error(`RPC channel closed`));this.pending.clear()}};const E=new Map,D=new Map;function O(e){if(e instanceof Error)return e.message;if(typeof e==`string`)return e;if(e&&typeof e==`object`){let t=e;if(typeof t.message==`string`)return t.message;if(t.error&&typeof t.error==`object`){let e=t.error;if(typeof e.message==`string`)return e.message}if(t.result&&typeof t.result==`object`){let e=t.result;if(typeof e.message==`string`)return e.message}try{let t=JSON.stringify(e);if(t&&t!==`{}`)return t}catch{}}return String(e)}var k=class e{constructor(e,t){this.dbName=e,this.config=t,this.workerQueue=Promise.resolve(),this.activeStorageBackend=`memory`,this.closed=!1}static async create(t){let n=new e(t.dbName,t.config);return await n.initialize(),n}async initialize(){p.log(`UniSQLite v0.4.0 - Loading SQLite WASM using strategy: ${this.config.loadStrategy}`);let e=await this.loadSQLiteWasm(),t={print:(...e)=>p.log(...e),printErr:(...e)=>p.error(...e)};if(this.config.locateFile)t.locateFile=this.config.locateFile;else if(typeof window<`u`){let e=window;e.sqlite3InitModuleState?.locateFile&&(t.locateFile=e.sqlite3InitModuleState.locateFile)}if(p.log(`Initializing SQLite WASM module...`),this.sqlite3=await e(t),!this.sqlite3)throw Error(`Failed to initialize SQLite WASM module`);let n=this.sqlite3;p.log(`SQLite WASM module initialized successfully`);let r=this.config.storageBackend??`auto`,i=this.dbName===`:memory:`?`:memory:`:`/unisqlite/${this.dbName}`;r===`memory`||this.dbName===`:memory:`?(p.log(`Using in-memory database`),this.db=new n.oo1.DB(`:memory:`),this.activeStorageBackend=`memory`):await this.tryCreatePersistentDatabase(n,i,r)||(p.log(`All persistent storage backends failed, using in-memory database`),this.db=new n.oo1.DB(`:memory:`),this.activeStorageBackend=`memory`);try{await this.execInternal(`PRAGMA journal_mode=WAL`),p.log(`Enabled WAL mode`)}catch(e){p.warn(`Could not enable WAL mode:`,e)}}async query(e,t){return(await this.executeSql(e,t)).rows}async queryRaw(e,t){return await this.executeSql(e,t)}async run(e,t){let n=await this.executeSql(e,t);return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){await this.execInternal(e)}async execRaw(e){await this.execInternal(e,{bypassQueue:!0})}async executeSqlRaw(e,t){return await this.executeSql(e,t,{bypassQueue:!0})}getActiveStorageBackend(){return this.activeStorageBackend}getActiveOpfsVfs(){return this.activeOpfsVfs}isWorkerDbActive(){return!!this.workerPromiser&&this.workerDbId!==void 0}async close(){if(!this.closed){if(this.closed=!0,this.workerPromiser){let e=this.workerPromiser,t=this.workerDbId;try{t!==void 0&&await this.enqueueWorker(async()=>{await e(`close`,{dbId:t})})}catch(e){p.warn(`Failed to close SQLite worker database:`,e)}finally{this.workerDbId=void 0;let t=e.worker;t?.terminate&&t.terminate(),this.workerPromiser=void 0}}this.db&&=(this.db.close(),void 0)}}enqueueWorker(e){let t=async()=>e(),n=this.workerQueue.then(t,t);return this.workerQueue=n.then(()=>void 0,()=>void 0),n}normalizeParams(e){if(!e)return;if(Array.isArray(e))return e;let t=Object.keys(e),n=e;return t.map(e=>n[e])}async execInternal(e,t){if(this.isWorkerDbActive()){let n=async()=>{if(!this.workerPromiser||this.workerDbId===void 0)throw Error(`Database not initialized`);await this.workerPromiser(`exec`,{dbId:this.workerDbId,sql:e})};t?.bypassQueue?await n():await this.enqueueWorker(n);return}if(!this.db)throw Error(`Database not initialized`);this.db.exec(e)}async executeSql(e,t,n){try{if(this.isWorkerDbActive()){let r=async()=>{if(!this.workerPromiser||this.workerDbId===void 0)throw Error(`Database not initialized`);let n=this.normalizeParams(t),r={dbId:this.workerDbId,sql:e,rowMode:`object`,resultRows:[],columnNames:[],countChanges:!0};n&&(r.bind=n);let i=await this.workerPromiser(`exec`,r),a=i.result??i,o=Array.isArray(a.resultRows)?a.resultRows:[],s=Array.isArray(a.columnNames)?a.columnNames:[],c=typeof a.changeCount==`number`?a.changeCount:0,l={dbId:this.workerDbId,sql:`SELECT last_insert_rowid() AS lastInsertRowId`,rowMode:`object`,resultRows:[],columnNames:[]},u=await this.workerPromiser(`exec`,l),d=u.result??u,f=Array.isArray(d.resultRows)?d.resultRows:[];return{rows:o,columns:s,rowsAffected:c,lastInsertRowId:Number(f[0]?.lastInsertRowId??0)}};return n?.bypassQueue?await r():await this.enqueueWorker(r)}if(!this.db||!this.sqlite3)throw Error(`Database not initialized`);let r=this.normalizeParams(t),i=[],a=[],o=this.db.prepare(e);try{let e=o;r&&o.bind(r);let t=typeof e.getColumnCount==`function`?e.getColumnCount():typeof e.columnCount==`number`?e.columnCount:0;if(t>0)if(typeof e.getColumnNames==`function`)a=e.getColumnNames();else for(let e=0;e<t;e++)a.push(o.getColumnName(e));for(;o.step();){let e={};for(let n=0;n<t;n++)e[a[n]]=o.get(n);i.push(e)}return{rows:i,columns:a,rowsAffected:this.db.changes(),lastInsertRowId:Number(this.sqlite3.capi.sqlite3_last_insert_rowid(this.db.pointer)||0)}}finally{o.finalize()}}catch(e){let t=O(e),n=Error(`SQLite error: ${t}`);throw e instanceof Error&&(n.cause=e),n}}async loadSQLiteWasm(){let e=`${this.config.loadStrategy}-${this.config.wasmUrl||this.config.cdnBaseUrl}-${this.config.version}`;if(E.has(e))return E.get(e);let t=this.loadSQLiteWasmInternal();return E.set(e,t),t}async loadSQLiteWasmInternal(){let{loadStrategy:e,wasmUrl:t,cdnBaseUrl:n,version:r,bundlerFriendly:i}=this.config;switch(e){case`npm`:return await this.loadFromNpm();case`cdn`:return await this.loadFromCdn(n,r,i);case`url`:if(!t)throw Error(`wasmUrl must be provided when using 'url' loading strategy`);return await this.loadFromUrl(t);case`module`:return await this.loadAsModule();case`global`:default:return this.loadFromGlobal()}}async loadFromNpm(){try{let e=await import(`@sqlite.org/sqlite-wasm`);return e.default||e}catch(e){return p.warn(`Failed to load SQLite WASM from npm, falling back to CDN:`,e),this.loadFromCdn(this.config.cdnBaseUrl,this.config.version,this.config.bundlerFriendly)}}async loadFromCdn(e,t,n){let r=n?`sqlite3-bundler-friendly.mjs`:`index.mjs`,i=[`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm@${t}/${r}`,`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm@${t}/dist/${r}`],a;for(let e of i)try{p.log(`Trying to load SQLite WASM from: ${e}`);let t=await import(e);return p.log(`Successfully loaded SQLite WASM from: ${e}`),t.default||t}catch(t){a=t,p.warn(`Failed to load SQLite WASM from ${e}:`,t)}let o=a instanceof Error?a.message:String(a);throw Error(`Failed to load SQLite WASM from any CDN source. Last error: ${o}`)}async loadFromUrl(e){let t=await import(e);return t.default||t.sqlite3InitModule}async loadAsModule(){if(globalThis.importScripts!==void 0)throw Error(`ES6 module loading not supported in Web Worker context`);for(let e of[`./sqlite3.mjs`,`./sqlite3-bundler-friendly.mjs`,`/sqlite3.mjs`])try{let t=await import(e);return t.default||t.sqlite3InitModule}catch(t){p.warn(`Failed to load SQLite WASM from ${e}:`,t)}throw Error(`Could not load SQLite WASM as ES6 module from any known path`)}loadFromGlobal(){if(typeof window<`u`){let e=window;if(e.sqlite3InitModule)return e.sqlite3InitModule}throw Error(`SQLite WASM module not found globally. Please: | ||
| 1. Install via npm: npm install @sqlite.org/sqlite-wasm | ||
| 2. Or set loadStrategy to 'cdn' for automatic CDN loading | ||
| 3. Or include SQLite WASM script in your HTML before using UniSqlite`)}async loadSQLiteWorkerPromiserFactory(){let e=`worker-${this.config.loadStrategy}-${this.config.wasmUrl||this.config.cdnBaseUrl}-${this.config.version}`;if(b.has(e))return b.get(e);let t=this.loadSQLiteWorkerPromiserFactoryInternal();return b.set(e,t),t}async loadSQLiteWorkerPromiserFactoryInternal(){let{loadStrategy:e,wasmUrl:t,cdnBaseUrl:n,version:r,bundlerFriendly:i}=this.config;switch(e){case`npm`:return await this.loadWorkerPromiserFromNpm();case`cdn`:return await this.loadWorkerPromiserFromCdn(n,r,i);case`url`:if(!t)throw Error(`wasmUrl must be provided when using 'url' loading strategy`);return await this.loadWorkerPromiserFromUrl(t);case`module`:return await this.loadWorkerPromiserAsModule();case`global`:default:return this.loadWorkerPromiserFromGlobal()}}async loadWorkerPromiserFromNpm(){try{let e=(await import(`@sqlite.org/sqlite-wasm`)).sqlite3Worker1Promiser;if(typeof e==`function`)return e;throw Error(`sqlite3Worker1Promiser export not found`)}catch(e){return s.warn(`Failed to load SQLite WASM worker promiser from npm, falling back to CDN:`,e),this.loadWorkerPromiserFromCdn(this.config.cdnBaseUrl,this.config.version,this.config.bundlerFriendly)}}async loadWorkerPromiserFromCdn(e,t,n){let r=n?`sqlite3-bundler-friendly.mjs`:`index.mjs`,i=[`${e}@${t}/${r}`,`${e}@${t}/dist/${r}`],a;for(let e of i)try{s.log(`Trying to load SQLite WASM worker promiser from: ${e}`);let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t==`function`)return s.log(`Successfully loaded SQLite WASM worker promiser from: ${e}`),t;throw Error(`sqlite3Worker1Promiser export not found`)}catch(t){a=t,s.warn(`Failed to load SQLite WASM worker promiser from ${e}:`,t)}let o=a instanceof Error?a.message:String(a);throw Error(`Failed to load sqlite3Worker1Promiser from any CDN source. Last error: ${o}`)}async loadWorkerPromiserFromUrl(e){let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t!=`function`)throw Error(`sqlite3Worker1Promiser export not found in module loaded from ${e}`);return t}async loadWorkerPromiserAsModule(){if(globalThis.importScripts!==void 0)throw Error(`ES6 module loading not supported in Web Worker context`);for(let e of[`./sqlite3.mjs`,`./sqlite3-bundler-friendly.mjs`,`/sqlite3.mjs`])try{let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t==`function`)return t}catch(t){s.warn(`Failed to load SQLite WASM worker promiser from ${e}:`,t)}throw Error(`Could not load sqlite3Worker1Promiser as ES6 module from any known path`)}loadWorkerPromiserFromGlobal(){let e=globalThis;if(typeof e.sqlite3Worker1Promiser==`function`)return e.sqlite3Worker1Promiser;throw Error(`SQLite WASM worker promiser not found globally. Please: | ||
| 3. Or include SQLite WASM script in your HTML before using UniSqlite`)}async loadSQLiteWorkerPromiserFactory(){let e=`worker-${this.config.loadStrategy}-${this.config.wasmUrl||this.config.cdnBaseUrl}-${this.config.version}`;if(D.has(e))return D.get(e);let t=this.loadSQLiteWorkerPromiserFactoryInternal();return D.set(e,t),t}async loadSQLiteWorkerPromiserFactoryInternal(){let{loadStrategy:e,wasmUrl:t,cdnBaseUrl:n,version:r,bundlerFriendly:i}=this.config;switch(e){case`npm`:return await this.loadWorkerPromiserFromNpm();case`cdn`:return await this.loadWorkerPromiserFromCdn(n,r,i);case`url`:if(!t)throw Error(`wasmUrl must be provided when using 'url' loading strategy`);return await this.loadWorkerPromiserFromUrl(t);case`module`:return await this.loadWorkerPromiserAsModule();case`global`:default:return this.loadWorkerPromiserFromGlobal()}}async loadWorkerPromiserFromNpm(){try{let e=(await import(`@sqlite.org/sqlite-wasm`)).sqlite3Worker1Promiser;if(typeof e==`function`)return e;throw Error(`sqlite3Worker1Promiser export not found`)}catch(e){return p.warn(`Failed to load SQLite WASM worker promiser from npm, falling back to CDN:`,e),this.loadWorkerPromiserFromCdn(this.config.cdnBaseUrl,this.config.version,this.config.bundlerFriendly)}}async loadWorkerPromiserFromCdn(e,t,n){let r=n?`sqlite3-bundler-friendly.mjs`:`index.mjs`,i=[`${e}@${t}/${r}`,`${e}@${t}/dist/${r}`],a;for(let e of i)try{p.log(`Trying to load SQLite WASM worker promiser from: ${e}`);let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t==`function`)return p.log(`Successfully loaded SQLite WASM worker promiser from: ${e}`),t;throw Error(`sqlite3Worker1Promiser export not found`)}catch(t){a=t,p.warn(`Failed to load SQLite WASM worker promiser from ${e}:`,t)}let o=a instanceof Error?a.message:String(a);throw Error(`Failed to load sqlite3Worker1Promiser from any CDN source. Last error: ${o}`)}async loadWorkerPromiserFromUrl(e){let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t!=`function`)throw Error(`sqlite3Worker1Promiser export not found in module loaded from ${e}`);return t}async loadWorkerPromiserAsModule(){if(globalThis.importScripts!==void 0)throw Error(`ES6 module loading not supported in Web Worker context`);for(let e of[`./sqlite3.mjs`,`./sqlite3-bundler-friendly.mjs`,`/sqlite3.mjs`])try{let t=(await import(e)).sqlite3Worker1Promiser;if(typeof t==`function`)return t}catch(t){p.warn(`Failed to load SQLite WASM worker promiser from ${e}:`,t)}throw Error(`Could not load sqlite3Worker1Promiser as ES6 module from any known path`)}loadWorkerPromiserFromGlobal(){let e=globalThis;if(typeof e.sqlite3Worker1Promiser==`function`)return e.sqlite3Worker1Promiser;throw Error(`SQLite WASM worker promiser not found globally. Please: | ||
| 1. Install via npm: npm install @sqlite.org/sqlite-wasm and set loadStrategy to 'npm' | ||
| 2. Or set loadStrategy to 'cdn' for automatic CDN loading | ||
| 3. Or include SQLite WASM worker promiser script in your HTML before using UniSqlite`)}async tryCreatePersistentDatabase(e,t,n){if(n===`opfs`||n===`auto`){let r=this.config.opfsVfsType??`auto`,i=r===`auto`?e.oo1.OpfsDb?[`opfs`]:[`opfs-sahpool`,`opfs`]:r===`sahpool`?[`opfs-sahpool`]:[`opfs`];for(let n of i)if(await this.tryCreateOpfsDatabase(e,t,n))return!0;if(n===`opfs`)throw Error(this.getOpfsUnavailableError())}if(n===`localStorage`||n===`auto`){if(e.oo1.JsStorageDb)if(this.dbName===`local`||this.dbName===`session`)try{return s.log(`Creating localStorage-backed database:`,this.dbName),this.db=new e.oo1.JsStorageDb(this.dbName),this.activeStorageBackend=`localStorage`,s.log(`Successfully created localStorage-backed database`),!0}catch(e){if(s.warn(`localStorage database creation failed:`,e),n===`localStorage`)throw Error(`localStorage database creation failed: ${e instanceof Error?e.message:String(e)}`)}else if(n===`localStorage`)throw Error(`localStorage storage backend requires path to be 'local' or 'session', got '${this.dbName}'. Use storageBackend: 'opfs' for custom database names with persistence.`);else s.log(`Skipping localStorage: path '${this.dbName}' is not 'local' or 'session'`);else if(n===`localStorage`)throw Error(`JsStorageDb is not available in this environment.`)}return!1}getOpfsUnavailableError(){return`OPFS is not available. | ||
| 3. Or include SQLite WASM worker promiser script in your HTML before using UniSqlite`)}async tryCreatePersistentDatabase(e,t,n){if(n===`opfs`||n===`auto`){let r=this.config.opfsVfsType??`auto`,i=r===`auto`?e.oo1.OpfsDb?[`opfs`]:[`opfs-sahpool`,`opfs`]:r===`sahpool`?[`opfs-sahpool`]:[`opfs`];for(let n of i)if(await this.tryCreateOpfsDatabase(e,t,n))return!0;if(n===`opfs`)throw Error(this.getOpfsUnavailableError())}if(n===`localStorage`||n===`auto`){if(e.oo1.JsStorageDb)if(this.dbName===`local`||this.dbName===`session`)try{return p.log(`Creating localStorage-backed database:`,this.dbName),this.db=new e.oo1.JsStorageDb(this.dbName),this.activeStorageBackend=`localStorage`,p.log(`Successfully created localStorage-backed database`),!0}catch(e){if(p.warn(`localStorage database creation failed:`,e),n===`localStorage`){let t=`localStorage database creation failed: ${e instanceof Error?e.message:String(e)}`,n=Error(t);throw e instanceof Error&&(n.cause=e),n}}else if(n===`localStorage`)throw Error(`localStorage storage backend requires path to be 'local' or 'session', got '${this.dbName}'. Use storageBackend: 'opfs' for custom database names with persistence.`);else p.log(`Skipping localStorage: path '${this.dbName}' is not 'local' or 'session'`);else if(n===`localStorage`)throw Error(`JsStorageDb is not available in this environment.`)}return!1}getOpfsUnavailableError(){return`OPFS is not available. | ||
@@ -14,5 +14,5 @@ SQLite WASM OPFS backends only work in Worker contexts. When running on the main thread, UniSQLite will try to use SQLite WASM's wrapped-worker API (sqlite3Worker1Promiser) under the hood. | ||
| - For VFS 'opfs': COOP/COEP headers are set and SharedArrayBuffer is available | ||
| - Your sqlite load strategy can load sqlite3Worker1Promiser (use sqlite.loadStrategy = 'npm' or 'cdn', or provide it globally)`}async tryCreateOpfsDatabase(e,t,n){if(n===`opfs`){if(e.oo1.OpfsDb)try{return s.log(`Creating OPFS-backed database (OpfsDb):`,t),this.db=new e.oo1.OpfsDb(t),this.workerDbId=void 0,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=`opfs`,s.log(`Successfully created OPFS-backed database (OpfsDb)`),!0}catch(e){s.warn(`OPFS database creation via OpfsDb failed:`,e)}return await this.tryCreateWrappedWorkerOpfsDatabase(t,n)}if(await this.ensureSahpoolVfs(e))try{return s.log(`Creating SAHPool OPFS-backed database:`,t),this.db=new e.oo1.DB(t,`c`,`opfs-sahpool`),this.workerDbId=void 0,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=`opfs-sahpool`,s.log(`Successfully created SAHPool OPFS-backed database`),!0}catch(e){s.warn(`SAHPool OPFS database creation failed:`,e)}return await this.tryCreateWrappedWorkerOpfsDatabase(t,n)}async ensureSahpoolVfs(e){if(globalThis.importScripts===void 0)return!1;let t=`opfs-sahpool`;try{if(e.capi.sqlite3_vfs_find?.(t))return!0}catch(e){s.warn(`SAHPool VFS lookup failed:`,e)}if(typeof e.installOpfsSAHPoolVfs!=`function`)return!1;try{await e.installOpfsSAHPoolVfs({name:t,directory:`/unisqlite-sahpool`})}catch(e){return s.warn(`SAHPool VFS installation failed:`,e),!1}try{return!!e.capi.sqlite3_vfs_find?.(t)}catch{return!0}}createWorkerFromConfig(){if(!this.config.workerUrl||typeof Worker>`u`)return;let e=typeof this.config.workerUrl==`string`?this.config.workerUrl:this.config.workerUrl.href;try{return new Worker(e,{type:`module`})}catch(e){s.warn(`Failed to create Worker from custom workerUrl:`,e);return}}createWorkerFromCdn(e){if(typeof Worker>`u`||typeof Blob>`u`||typeof URL>`u`)return;let t=this.config.cdnBaseUrl,n=this.config.version;if(!t||!n)return;let r=`${`${t}@${n}/sqlite-wasm/jswasm`}/sqlite3-bundler-friendly.mjs`,i=e.installSahpool?[` if (typeof sqlite3.installOpfsSAHPoolVfs === "function") {`,` await sqlite3.installOpfsSAHPoolVfs({ name: "opfs-sahpool", directory: "/unisqlite-sahpool" });`,` }`].join(` | ||
| - Your sqlite load strategy can load sqlite3Worker1Promiser (use sqlite.loadStrategy = 'npm' or 'cdn', or provide it globally)`}async tryCreateOpfsDatabase(e,t,n){if(n===`opfs`){if(e.oo1.OpfsDb)try{return p.log(`Creating OPFS-backed database (OpfsDb):`,t),this.db=new e.oo1.OpfsDb(t),this.workerDbId=void 0,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=`opfs`,p.log(`Successfully created OPFS-backed database (OpfsDb)`),!0}catch(e){p.warn(`OPFS database creation via OpfsDb failed:`,e)}return await this.tryCreateWrappedWorkerOpfsDatabase(t,n)}if(await this.ensureSahpoolVfs(e))try{return p.log(`Creating SAHPool OPFS-backed database:`,t),this.db=new e.oo1.DB(t,`c`,`opfs-sahpool`),this.workerDbId=void 0,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=`opfs-sahpool`,p.log(`Successfully created SAHPool OPFS-backed database`),!0}catch(e){p.warn(`SAHPool OPFS database creation failed:`,e)}return await this.tryCreateWrappedWorkerOpfsDatabase(t,n)}async ensureSahpoolVfs(e){if(globalThis.importScripts===void 0)return!1;let t=`opfs-sahpool`;try{if(e.capi.sqlite3_vfs_find?.(t))return!0}catch(e){p.warn(`SAHPool VFS lookup failed:`,e)}if(typeof e.installOpfsSAHPoolVfs!=`function`)return!1;try{await e.installOpfsSAHPoolVfs({name:t,directory:`/unisqlite-sahpool`})}catch(e){return p.warn(`SAHPool VFS installation failed:`,e),!1}try{return!!e.capi.sqlite3_vfs_find?.(t)}catch{return!0}}createWorkerFromConfig(){if(!this.config.workerUrl||typeof Worker>`u`)return;let e=typeof this.config.workerUrl==`string`?this.config.workerUrl:this.config.workerUrl.href;try{return new Worker(e,{type:`module`})}catch(e){p.warn(`Failed to create Worker from custom workerUrl:`,e);return}}createWorkerFromCdn(e){if(typeof Worker>`u`||typeof Blob>`u`||typeof URL>`u`)return;let t=this.config.cdnBaseUrl,n=this.config.version;if(!t||!n)return;let r=`${`${t}@${n}/sqlite-wasm/jswasm`}/sqlite3-bundler-friendly.mjs`,i=e.installSahpool?[` if (typeof sqlite3.installOpfsSAHPoolVfs === "function") {`,` await sqlite3.installOpfsSAHPoolVfs({ name: "opfs-sahpool", directory: "/unisqlite-sahpool" });`,` }`].join(` | ||
| `):``,a=[`import sqlite3InitModule from ${JSON.stringify(r)};`,`sqlite3InitModule().then(async (sqlite3) => {`,` try {`,i||` // no-op`,` } catch (e) {`,` console.warn("SAHPool VFS installation failed:", e);`,` }`,` sqlite3.initWorker1API();`,`});`,``].join(` | ||
| `),o=new Blob([a],{type:`text/javascript`}),s=URL.createObjectURL(o),c=new Worker(s,{type:`module`});return c.addEventListener(`message`,()=>{URL.revokeObjectURL(s)},{once:!0}),c.addEventListener(`error`,()=>{URL.revokeObjectURL(s)},{once:!0}),c}async getOrCreateWorkerPromiser(e){if(this.workerPromiser)return this.workerPromiser;if(this.workerPromiserInitPromise)return await this.workerPromiserInitPromise;let t=await this.loadSQLiteWorkerPromiserFactory(),n=this.config.storageBackend===`opfs`||this.config.opfsVfsType===`opfs`||this.config.opfsVfsType===`sahpool`,r=this.config.loadStrategy===`global`&&!n?5e3:2e4;this.workerPromiserInitPromise=new Promise((n,i)=>{let a=!1,o=setTimeout(()=>{a||(a=!0,i(Error(`SQLite worker initialization timed out after ${r}ms`)))},r),s=(e,t)=>{a||(a=!0,clearTimeout(o),e(t))},c=e=>{s(i,e instanceof Error?e:Error(String(e)))},l;try{l=t({...e?.worker?{worker:e.worker}:{},onready:e=>{if(typeof e==`function`){s(n,e);return}if(typeof l==`function`){s(n,l);return}if(l&&typeof l.then==`function`){l.then(e=>s(n,e),e=>c(e));return}c(Error(`sqlite3Worker1Promiser did not provide a usable promiser`))},onerror:e=>{let t=e instanceof Error?e.message:String(e);c(Error(`SQLite worker error: ${t}`))}}),l&&typeof l.then==`function`&&l.then(e=>s(n,e),e=>c(e))}catch(e){c(e)}});let i=await this.workerPromiserInitPromise;return this.workerPromiser=i,i}async tryCreateWrappedWorkerOpfsDatabase(e,t){try{let n=!this.workerPromiser&&!this.workerPromiserInitPromise?(()=>{let e=this.createWorkerFromConfig()??(t===`opfs-sahpool`?this.createWorkerFromCdn({installSahpool:!0}):this.createWorkerFromCdn({installSahpool:!1}));return e?{worker:e}:void 0})():void 0,r=await(await this.getOrCreateWorkerPromiser(n))(`open`,{filename:`file:${e}`,vfs:t}),i=r.dbId??r.result?.dbId;if(i===void 0)throw Error(`SQLite worker did not return a dbId`);if(typeof i!=`number`&&typeof i!=`string`)throw Error(`SQLite worker returned an invalid dbId (type=${typeof i})`);return this.db=void 0,this.workerDbId=i,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=t,s.log(`Successfully created OPFS-backed database via wrapped worker (${t}):`,e),!0}catch(e){return s.warn(`Wrapped-worker OPFS database creation failed (vfs=${t}):`,e),!1}}},S=class{constructor(e,t,n){this.dbHost=e,this.hostElection=t,this.activeTxn=null,this.txnQueue=[],this.heartbeatTimers=new Map,this.txnPending=!1,this.rpcQueue=[],this.txnTimeoutMs=n?.txnTimeoutMs??3e4,this.opTimeoutMs=n?.opTimeoutMs??1e4,this.maxQueueSize=n?.maxQueueSize??10,this.heartbeatTimeoutMs=n?.heartbeatTimeoutMs??15e3}startWatchdog(){this.watchdogTimer||=setInterval(()=>{if(this.activeTxn){let e=Date.now()-this.activeTxn.lastActivityAt;e>this.txnTimeoutMs&&(a.warn(`Transaction ${this.activeTxn.txnId} timed out after ${e}ms, rolling back`),this.forceRollback())}let e=Date.now(),t=this.txnQueue.filter(t=>e-t.enqueuedAt>this.txnTimeoutMs);for(let e of t){let t=this.txnQueue.indexOf(e);t!==-1&&(this.txnQueue.splice(t,1),e.reject(Error(`Transaction queue wait timeout`)))}},5e3)}stopWatchdog(){this.watchdogTimer&&=(clearInterval(this.watchdogTimer),void 0)}getActiveTransaction(){return this.activeTxn}hasActiveTransaction(){return this.activeTxn!==null}isTransactionBlocking(){return this.activeTxn!==null||this.txnPending}async waitForTransactionSlot(){if(this.isTransactionBlocking())return new Promise((e,t)=>{this.rpcQueue.push({resolve:e,reject:t})})}async beginTransaction(e,t,n=`immediate`){if(this.activeTxn||this.txnPending){if(this.txnQueue.length>=this.maxQueueSize)throw Error(`Transaction queue full`);await new Promise((r,i)=>{this.txnQueue.push({txnId:e,originTabId:t,mode:n,resolve:r,reject:i,enqueuedAt:Date.now()})})}this.txnPending=!0;try{this.txnLockRelease=await this.hostElection.acquireTransactionLock();let r=n===`deferred`?`BEGIN`:n===`exclusive`?`BEGIN EXCLUSIVE`:`BEGIN IMMEDIATE`;await this.dbHost.execRaw(r),this.activeTxn={txnId:e,originTabId:t,startedAt:Date.now(),lastActivityAt:Date.now(),mode:n},this.resetHeartbeatTimer(e)}catch(e){throw this.txnLockRelease?.(),this.txnLockRelease=void 0,this.txnPending=!1,this.processQueue(),this.processRpcQueue(),e}finally{this.txnPending=!1}}async execInTransaction(e,t){return this.validateActiveTxn(e),this.activeTxn.lastActivityAt=Date.now(),this.resetHeartbeatTimer(e),await t()}recordHeartbeat(e){this.activeTxn?.txnId===e&&(this.activeTxn.lastActivityAt=Date.now(),this.resetHeartbeatTimer(e))}async commitTransaction(e){this.validateActiveTxn(e);try{await this.dbHost.execRaw(`COMMIT`)}finally{this.endTransaction()}}async rollbackTransaction(e){this.validateActiveTxn(e);try{await this.dbHost.execRaw(`ROLLBACK`)}finally{this.endTransaction()}}forceRollback(){if(this.activeTxn){try{this.dbHost.execRaw(`ROLLBACK`).catch(e=>{a.error(`Force rollback failed:`,e)})}catch(e){a.error(`Force rollback failed:`,e)}this.endTransaction()}}endTransaction(){if(this.activeTxn){let e=this.heartbeatTimers.get(this.activeTxn.txnId);e&&(clearTimeout(e),this.heartbeatTimers.delete(this.activeTxn.txnId)),this.activeTxn=null}this.txnLockRelease&&=(this.txnLockRelease(),void 0),this.processQueue(),this.processRpcQueue()}processQueue(){this.txnQueue.length>0&&!this.activeTxn&&!this.txnPending&&this.txnQueue.shift().resolve()}processRpcQueue(){if(this.activeTxn||this.txnPending)return;let e=this.rpcQueue;this.rpcQueue=[];for(let{resolve:t}of e)t()}resetHeartbeatTimer(e){let t=this.heartbeatTimers.get(e);t&&clearTimeout(t);let n=setTimeout(()=>{this.activeTxn?.txnId===e&&(a.warn(`No heartbeat for transaction ${e} in ${this.heartbeatTimeoutMs}ms, rolling back`),this.forceRollback())},this.heartbeatTimeoutMs);this.heartbeatTimers.set(e,n)}validateActiveTxn(e){if(!this.activeTxn)throw Error(`No active transaction (expected ${e})`);if(this.activeTxn.txnId!==e)throw Error(`Transaction ID mismatch: expected ${this.activeTxn.txnId}, got ${e}`);let t=Date.now()-this.activeTxn.lastActivityAt;if(t>this.txnTimeoutMs)throw this.forceRollback(),Error(`Transaction ${e} timed out after ${t}ms`)}async close(){this.stopWatchdog(),this.activeTxn&&this.forceRollback();for(let e of this.txnQueue)e.reject(Error(`Transaction session manager closed`));this.txnQueue=[];for(let e of this.rpcQueue)e.reject(Error(`Transaction session manager closed`));this.rpcQueue=[];for(let e of this.heartbeatTimers.values())clearTimeout(e);this.heartbeatTimers.clear()}},C=class{constructor(e,t,n=5e3){this.rpcChannel=e,this.txnId=t,this.heartbeatIntervalMs=n,this.committed=!1,this.rolledBack=!1,this.startHeartbeat()}startHeartbeat(){this.heartbeatInterval=setInterval(()=>{!this.committed&&!this.rolledBack&&this.rpcChannel.txnHeartbeat(this.txnId)},this.heartbeatIntervalMs)}stopHeartbeat(){this.heartbeatInterval&&=(clearInterval(this.heartbeatInterval),void 0)}ensureActive(){if(this.committed)throw Error(`Transaction already committed`);if(this.rolledBack)throw Error(`Transaction already rolled back`)}async query(e,t){return this.ensureActive(),(await this.rpcChannel.txnExec(this.txnId,`query`,{sql:e,params:t})).rows}async queryRaw(e,t){return this.ensureActive(),await this.rpcChannel.txnExec(this.txnId,`queryRaw`,{sql:e,params:t})}async run(e,t){this.ensureActive();let n=await this.rpcChannel.txnExec(this.txnId,`run`,{sql:e,params:t});return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){this.ensureActive(),await this.rpcChannel.txnExec(this.txnId,`exec`,{sql:e})}async transaction(e){this.ensureActive();let t=e(this);return t instanceof Promise?await t:t}async asyncTransaction(e,t){return this.ensureActive(),await e(this)}getConnectionType(){return`asyncTxn`}async close(){}async _commit(){this.ensureActive(),this.stopHeartbeat();try{await this.rpcChannel.txnCommit(this.txnId),this.committed=!0}catch(e){throw this.rolledBack=!0,e}}async _rollback(){if(!(this.committed||this.rolledBack)){this.stopHeartbeat();try{await this.rpcChannel.txnRollback(this.txnId)}finally{this.rolledBack=!0}}}isCommitted(){return this.committed}isRolledBack(){return this.rolledBack}},w=class{constructor(e,t){this.dbHost=e,this.isAsync=t}async query(e,t){return(await this.dbHost.executeSqlRaw(e,t)).rows}async queryRaw(e,t){return await this.dbHost.executeSqlRaw(e,t)}async run(e,t){let n=await this.dbHost.executeSqlRaw(e,t);return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){await this.dbHost.execRaw(e)}async transaction(e){let t=e(this);return t instanceof Promise?await t:t}async asyncTransaction(e){return await e(this)}getConnectionType(){return this.isAsync?`asyncTxn`:`syncTxn`}async close(){}},T=class t extends e{constructor(e){let t=e.path??e.name??`default`;super({...e,path:t}),this.dbHost=null,this.txnSessionManager=null,this.initialized=!1,this.closed=!1,this.options={...e,path:t},this.tabId=g(),this.sqliteConfig={loadStrategy:`global`,storageBackend:`auto`,opfsVfsType:`auto`,cdnBaseUrl:`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm`,version:`3.50.1-build1`,bundlerFriendly:!1,...e.sqlite},l.isSupported()||(o.warn(`Web Locks API not supported. Falling back to memory-only storage.`),this.sqliteConfig.storageBackend=`memory`),this.visibilityManager=new u,this.rpcChannel=new v(t,this.tabId),this.hostElection=new l({dbName:t,tabId:this.tabId,onBecomeHost:()=>this.becomeHost(),onLoseHost:()=>this.loseHost(),checkVisibility:()=>this.visibilityManager.isVisible()}),this.visibilityUnsubscribe=this.visibilityManager.onVisibilityChange(e=>{this.handleVisibilityChange(e)})}static async create(e){let n=new t(e);return await n.initialize(),n}getTabId(){return this.tabId}get isHost(){return this.hostElection.isHost}async initialize(){if(!this.initialized){if(this.initPromise)return this.initPromise;this.initPromise=this.doInitialize(),await this.initPromise,this.initialized=!0}}async doInitialize(){this.becomeHostPromise=new Promise(e=>{this.becomeHostResolver=e}),await this.hostElection.start();let e=new Promise(e=>{setTimeout(()=>e(`timeout`),500)});await Promise.race([this.becomeHostPromise.then(()=>`host`),e])===`host`||this.hostElection.isHost&&await this.becomeHostPromise}async becomeHost(){o.log(`Tab ${this.tabId} becoming Host for ${this.options.path}`),this.dbHost=await x.create({dbName:this.options.path,config:this.sqliteConfig}),this.txnSessionManager=new S(this.dbHost,this.hostElection),this.txnSessionManager.startWatchdog(),this.rpcChannel.setRequestHandler(this.handleRequest.bind(this)),this.becomeHostResolver&&=(this.becomeHostResolver(),void 0)}loseHost(){o.log(`Tab ${this.tabId} losing Host for ${this.options.path}`),this.rpcChannel.setRequestHandler(void 0),this.txnSessionManager&&=(this.txnSessionManager.close(),null),this.dbHost&&=(this.dbHost.close(),null)}handleVisibilityChange(e){e&&!this.hostElection.isHost&&!this.closed&&this.hostElection.start()}async handleRequest(e){if(!this.dbHost)throw Error(`Not Host`);switch(e.t){case`req`:return await this.handleRpcRequest(e);case`txn_begin`:return await this.handleTxnBegin(e);case`txn_exec`:return await this.handleTxnExec(e);case`txn_commit`:return await this.handleTxnCommit(e);case`txn_rollback`:return await this.handleTxnRollback(e);case`txn_heartbeat`:this.handleTxnHeartbeat(e);return;default:throw Error(`Unknown request type: ${e.t}`)}}async handleRpcRequest(e){let{op:t,payload:n}=e,{sql:r,params:i}=n;switch(this.txnSessionManager&&await this.txnSessionManager.waitForTransactionSlot(),t){case`query`:return{rows:await this.dbHost.query(r,i),columns:[]};case`queryRaw`:return await this.dbHost.queryRaw(r,i);case`run`:return await this.dbHost.run(r,i);case`exec`:return await this.dbHost.exec(r),null;default:throw Error(`Unknown RPC operation: ${String(t)}`)}}async handleTxnBegin(e){await this.txnSessionManager.beginTransaction(e.txnId,e.from,e.mode)}async handleTxnExec(e){let{txnId:t,op:n,payload:r}=e,{sql:i,params:a}=r;return await this.txnSessionManager.execInTransaction(t,async()=>{switch(n){case`query`:return{rows:await this.dbHost.query(i,a),columns:[]};case`queryRaw`:return await this.dbHost.queryRaw(i,a);case`run`:return await this.dbHost.run(i,a);case`exec`:return await this.dbHost.exec(i),null;default:throw Error(`Unknown transaction operation: ${String(n)}`)}})}async handleTxnCommit(e){await this.txnSessionManager.commitTransaction(e.txnId)}async handleTxnRollback(e){await this.txnSessionManager.rollbackTransaction(e.txnId)}handleTxnHeartbeat(e){this.txnSessionManager?.recordHeartbeat(e.txnId)}async query(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.query(e,t):(await this.rpcChannel.request(`query`,{sql:e,params:t})).rows}async queryRaw(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.queryRaw(e,t):await this.rpcChannel.request(`queryRaw`,{sql:e,params:t})}async run(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.run(e,t):await this.rpcChannel.request(`run`,{sql:e,params:t})}async exec(e){if(await this.initialize(),this.isHost&&this.dbHost){await this.dbHost.exec(e);return}await this.rpcChannel.request(`exec`,{sql:e})}async transaction(e){return await this.initialize(),this.isHost&&this.dbHost?await this.executeLocalTransaction(e,!1):await this.executeRemoteTransaction(e)}async asyncTransaction(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.executeLocalAsyncTransaction(e,t):await this.executeRemoteTransaction(e,t)}getConnectionType(){return`direct`}async close(){this.closed||(this.closed=!0,this.visibilityUnsubscribe?.(),await this.hostElection.close(),this.rpcChannel.close(),this.visibilityManager.destroy(),this.txnSessionManager&&=(await this.txnSessionManager.close(),null),this.dbHost&&=(await this.dbHost.close(),null))}async executeLocalTransaction(e,t){await this.dbHost.execRaw(`BEGIN`);let n=new w(this.dbHost,t);try{let t=e(n),r=t instanceof Promise?await t:t;return await this.dbHost.execRaw(`COMMIT`),r}catch(e){try{await this.dbHost.execRaw(`ROLLBACK`)}catch(e){o.error(`Rollback failed:`,e)}throw e}}async executeLocalAsyncTransaction(e,t){let n=t?.timeoutMs??3e4;await this.dbHost.execRaw(`BEGIN IMMEDIATE`);let r=new w(this.dbHost,!0),i,a=!1;try{let t=new Promise((e,t)=>{i=setTimeout(()=>{a||t(Error(`Async transaction timeout after ${n}ms`))},n)}),o=await Promise.race([e(r),t]);return a=!0,i&&clearTimeout(i),await this.dbHost.execRaw(`COMMIT`),o}catch(e){a=!0,i&&clearTimeout(i);try{await this.dbHost.execRaw(`ROLLBACK`)}catch(e){o.error(`Rollback failed:`,e)}throw e}}async executeRemoteTransaction(e,t){let n=_(),r=t?.timeoutMs??3e4;await this.rpcChannel.txnBegin(n,`immediate`,r);let i=new C(this.rpcChannel,n),a,s=!1;try{let t=new Promise((e,t)=>{a=setTimeout(()=>{s||t(Error(`Remote transaction timeout after ${r}ms`))},r)}),n=e(i),o=n instanceof Promise?n:Promise.resolve(n),c=await Promise.race([o,t]);return s=!0,a&&clearTimeout(a),await i._commit(),c}catch(e){s=!0,a&&clearTimeout(a);try{await i._rollback()}catch(e){o.error(`Remote rollback failed:`,e)}throw e}}getSQLiteInfo(){let e=this.isHost?this.dbHost!==null:this.initialized&&!this.closed;return{config:this.sqliteConfig,isInitialized:this.initialized,isHost:this.isHost,isReady:e,tabId:this.tabId,hasDatabase:this.dbHost!==null,usesWorker:this.dbHost?.isWorkerDbActive()??!1,activeStorageBackend:this.dbHost?.getActiveStorageBackend()??`unknown`,activeOpfsVfs:this.dbHost?.getActiveOpfsVfs()}}getActiveStorageBackend(){return this.dbHost?.getActiveStorageBackend()??`unknown`}};export{v as a,g as c,m as d,d as f,x as i,_ as l,l as m,C as n,f as o,u as p,S as r,h as s,T as t,p as u}; | ||
| `),o=new Blob([a],{type:`text/javascript`}),s=URL.createObjectURL(o),c=new Worker(s,{type:`module`});return c.addEventListener(`message`,()=>{URL.revokeObjectURL(s)},{once:!0}),c.addEventListener(`error`,()=>{URL.revokeObjectURL(s)},{once:!0}),c}async getOrCreateWorkerPromiser(e){if(this.workerPromiser)return this.workerPromiser;if(this.workerPromiserInitPromise)return await this.workerPromiserInitPromise;let t=await this.loadSQLiteWorkerPromiserFactory(),n=this.config.storageBackend===`opfs`||this.config.opfsVfsType===`opfs`||this.config.opfsVfsType===`sahpool`,r=this.config.loadStrategy===`global`&&!n?5e3:2e4;this.workerPromiserInitPromise=new Promise((n,i)=>{let a=!1,o=setTimeout(()=>{a||(a=!0,i(Error(`SQLite worker initialization timed out after ${r}ms`)))},r),s=(e,t)=>{a||(a=!0,clearTimeout(o),e(t))},c=e=>{s(i,e instanceof Error?e:Error(String(e)))},l;try{l=t({...e?.worker?{worker:e.worker}:{},onready:e=>{if(typeof e==`function`){s(n,e);return}if(typeof l==`function`){s(n,l);return}if(l&&typeof l.then==`function`){l.then(e=>s(n,e),e=>c(e));return}c(Error(`sqlite3Worker1Promiser did not provide a usable promiser`))},onerror:e=>{let t=e instanceof Error?e.message:String(e);c(Error(`SQLite worker error: ${t}`))}}),l&&typeof l.then==`function`&&l.then(e=>s(n,e),e=>c(e))}catch(e){c(e)}});let i=await this.workerPromiserInitPromise;return this.workerPromiser=i,i}async tryCreateWrappedWorkerOpfsDatabase(e,t){try{let n=!this.workerPromiser&&!this.workerPromiserInitPromise?(()=>{let e=this.createWorkerFromConfig();if(e)return{worker:e};if(this.config.loadStrategy===`global`)return;let n=t===`opfs-sahpool`?this.createWorkerFromCdn({installSahpool:!0}):this.createWorkerFromCdn({installSahpool:!1});return n?{worker:n}:void 0})():void 0,r=await(await this.getOrCreateWorkerPromiser(n))(`open`,{filename:`file:${e}`,vfs:t}),i=r.dbId??r.result?.dbId;if(i===void 0)throw Error(`SQLite worker did not return a dbId`);if(typeof i!=`number`&&typeof i!=`string`)throw Error(`SQLite worker returned an invalid dbId (type=${typeof i})`);return this.db=void 0,this.workerDbId=i,this.activeStorageBackend=`opfs`,this.activeOpfsVfs=t,p.log(`Successfully created OPFS-backed database via wrapped worker (${t}):`,e),!0}catch(e){return p.warn(`Wrapped-worker OPFS database creation failed (vfs=${t}):`,e),!1}}},A=class{constructor(e,t,n){this.dbHost=e,this.hostElection=t,this.activeTxn=null,this.txnQueue=[],this.heartbeatTimers=new Map,this.txnPending=!1,this.rpcQueue=[],this.txnTimeoutMs=n?.txnTimeoutMs??3e4,this.opTimeoutMs=n?.opTimeoutMs??1e4,this.maxQueueSize=n?.maxQueueSize??10,this.heartbeatTimeoutMs=n?.heartbeatTimeoutMs??15e3}startWatchdog(){this.watchdogTimer||=setInterval(()=>{if(this.activeTxn){let e=Date.now()-this.activeTxn.lastActivityAt;e>this.txnTimeoutMs&&(d.warn(`Transaction ${this.activeTxn.txnId} timed out after ${e}ms, rolling back`),this.forceRollback())}let e=Date.now(),t=this.txnQueue.filter(t=>e-t.enqueuedAt>this.txnTimeoutMs);for(let e of t){let t=this.txnQueue.indexOf(e);t!==-1&&(this.txnQueue.splice(t,1),e.reject(Error(`Transaction queue wait timeout`)))}},5e3)}stopWatchdog(){this.watchdogTimer&&=(clearInterval(this.watchdogTimer),void 0)}getActiveTransaction(){return this.activeTxn}hasActiveTransaction(){return this.activeTxn!==null}isTransactionBlocking(){return this.activeTxn!==null||this.txnPending}async waitForTransactionSlot(){if(this.isTransactionBlocking())return new Promise((e,t)=>{this.rpcQueue.push({resolve:e,reject:t})})}async beginTransaction(e,t,n=`immediate`){if(this.activeTxn||this.txnPending){if(this.txnQueue.length>=this.maxQueueSize)throw Error(`Transaction queue full`);await new Promise((r,i)=>{this.txnQueue.push({txnId:e,originTabId:t,mode:n,resolve:r,reject:i,enqueuedAt:Date.now()})})}this.txnPending=!0;try{this.txnLockRelease=await this.hostElection.acquireTransactionLock();let r=n===`deferred`?`BEGIN`:n===`exclusive`?`BEGIN EXCLUSIVE`:`BEGIN IMMEDIATE`;await this.dbHost.execRaw(r),this.activeTxn={txnId:e,originTabId:t,startedAt:Date.now(),lastActivityAt:Date.now(),mode:n},this.resetHeartbeatTimer(e)}catch(e){throw this.txnLockRelease?.(),this.txnLockRelease=void 0,this.txnPending=!1,this.processQueue(),this.processRpcQueue(),e}finally{this.txnPending=!1}}async execInTransaction(e,t){return this.validateActiveTxn(e),this.activeTxn.lastActivityAt=Date.now(),this.resetHeartbeatTimer(e),await t()}recordHeartbeat(e){this.activeTxn?.txnId===e&&(this.activeTxn.lastActivityAt=Date.now(),this.resetHeartbeatTimer(e))}async commitTransaction(e){this.validateActiveTxn(e);try{await this.dbHost.execRaw(`COMMIT`)}finally{this.endTransaction()}}async rollbackTransaction(e){this.validateActiveTxn(e);try{await this.dbHost.execRaw(`ROLLBACK`)}finally{this.endTransaction()}}forceRollback(){if(this.activeTxn){try{this.dbHost.execRaw(`ROLLBACK`).catch(e=>{d.error(`Force rollback failed:`,e)})}catch(e){d.error(`Force rollback failed:`,e)}this.endTransaction()}}endTransaction(){if(this.activeTxn){let e=this.heartbeatTimers.get(this.activeTxn.txnId);e&&(clearTimeout(e),this.heartbeatTimers.delete(this.activeTxn.txnId)),this.activeTxn=null}this.txnLockRelease&&=(this.txnLockRelease(),void 0),this.processQueue(),this.processRpcQueue()}processQueue(){this.txnQueue.length>0&&!this.activeTxn&&!this.txnPending&&this.txnQueue.shift().resolve()}processRpcQueue(){if(this.activeTxn||this.txnPending)return;let e=this.rpcQueue;this.rpcQueue=[];for(let{resolve:t}of e)t()}resetHeartbeatTimer(e){let t=this.heartbeatTimers.get(e);t&&clearTimeout(t);let n=setTimeout(()=>{this.activeTxn?.txnId===e&&(d.warn(`No heartbeat for transaction ${e} in ${this.heartbeatTimeoutMs}ms, rolling back`),this.forceRollback())},this.heartbeatTimeoutMs);this.heartbeatTimers.set(e,n)}validateActiveTxn(e){if(!this.activeTxn)throw Error(`No active transaction (expected ${e})`);if(this.activeTxn.txnId!==e)throw Error(`Transaction ID mismatch: expected ${this.activeTxn.txnId}, got ${e}`);let t=Date.now()-this.activeTxn.lastActivityAt;if(t>this.txnTimeoutMs)throw this.forceRollback(),Error(`Transaction ${e} timed out after ${t}ms`)}async close(){this.stopWatchdog(),this.activeTxn&&this.forceRollback();for(let e of this.txnQueue)e.reject(Error(`Transaction session manager closed`));this.txnQueue=[];for(let e of this.rpcQueue)e.reject(Error(`Transaction session manager closed`));this.rpcQueue=[];for(let e of this.heartbeatTimers.values())clearTimeout(e);this.heartbeatTimers.clear()}},j=class{constructor(e,t,n=5e3){this.rpcChannel=e,this.txnId=t,this.heartbeatIntervalMs=n,this.committed=!1,this.rolledBack=!1,this.startHeartbeat()}startHeartbeat(){this.heartbeatInterval=setInterval(()=>{!this.committed&&!this.rolledBack&&this.rpcChannel.txnHeartbeat(this.txnId)},this.heartbeatIntervalMs)}stopHeartbeat(){this.heartbeatInterval&&=(clearInterval(this.heartbeatInterval),void 0)}ensureActive(){if(this.committed)throw Error(`Transaction already committed`);if(this.rolledBack)throw Error(`Transaction already rolled back`)}async query(e,t){return this.ensureActive(),(await this.rpcChannel.txnExec(this.txnId,`query`,{sql:e,params:t})).rows}async queryRaw(e,t){return this.ensureActive(),await this.rpcChannel.txnExec(this.txnId,`queryRaw`,{sql:e,params:t})}async run(e,t){this.ensureActive();let n=await this.rpcChannel.txnExec(this.txnId,`run`,{sql:e,params:t});return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){this.ensureActive(),await this.rpcChannel.txnExec(this.txnId,`exec`,{sql:e})}async transaction(e){this.ensureActive();let t=e(this);return t instanceof Promise?await t:t}async asyncTransaction(e,t){return this.ensureActive(),await e(this)}getConnectionType(){return`asyncTxn`}async close(){}async _commit(){this.ensureActive(),this.stopHeartbeat();try{await this.rpcChannel.txnCommit(this.txnId),this.committed=!0}catch(e){throw this.rolledBack=!0,e}}async _rollback(){if(!(this.committed||this.rolledBack)){this.stopHeartbeat();try{await this.rpcChannel.txnRollback(this.txnId)}finally{this.rolledBack=!0}}}isCommitted(){return this.committed}isRolledBack(){return this.rolledBack}},M=class{constructor(e,t){this.dbHost=e,this.isAsync=t}async query(e,t){return(await this.dbHost.executeSqlRaw(e,t)).rows}async queryRaw(e,t){return await this.dbHost.executeSqlRaw(e,t)}async run(e,t){let n=await this.dbHost.executeSqlRaw(e,t);return{rowsAffected:n.rowsAffected??0,lastInsertRowId:n.lastInsertRowId??0}}async exec(e){await this.dbHost.execRaw(e)}async transaction(e){let t=e(this);return t instanceof Promise?await t:t}async asyncTransaction(e){return await e(this)}getConnectionType(){return this.isAsync?`asyncTxn`:`syncTxn`}async close(){}},N=class t extends e{constructor(e){let t=e.path??e.name??`default`;super({...e,path:t}),this.dbHost=null,this.txnSessionManager=null,this.initialized=!1,this.closed=!1,this.roleListeners=new Set,this.roleValue=`unknown`,this.options={...e,path:t},this.tabId=C(),this.sqliteConfig={loadStrategy:`global`,storageBackend:`auto`,opfsVfsType:`auto`,cdnBaseUrl:`https://cdn.jsdelivr.net/npm/@sqlite.org/sqlite-wasm`,version:`3.50.1-build1`,bundlerFriendly:!1,...e.sqlite},g.isSupported()||(f.warn(`Web Locks API not supported. Falling back to memory-only storage.`),this.sqliteConfig.storageBackend=`memory`),this.visibilityManager=new _,this.rpcChannel=new T(t,this.tabId),this.hostElection=new g({dbName:t,tabId:this.tabId,onBecomeHost:()=>this.becomeHost(),onLoseHost:()=>this.loseHost(),checkVisibility:()=>this.visibilityManager.isVisible()}),this.visibilityUnsubscribe=this.visibilityManager.onVisibilityChange(e=>{this.handleVisibilityChange(e)})}static async create(e){let n=new t(e);return await n.initialize(),n}getTabId(){return this.tabId}get isHost(){return this.hostElection.isHost}getRole(){return this.roleValue}subscribeRoleChange(e){this.roleListeners.add(e);try{e(this.roleValue)}catch(e){f.error(`Role change listener threw:`,e)}return()=>{this.roleListeners.delete(e)}}async waitForRole(e,t=3e4){this.roleValue!==e&&await new Promise((n,r)=>{let i=t=>{t===e&&(a(),n())},a=()=>{clearTimeout(o),this.roleListeners.delete(i)},o=setTimeout(()=>{a(),r(Error(`Timed out waiting for role '${e}' after ${t}ms`))},t);this.roleListeners.add(i),this.roleValue===e&&(a(),n())})}async initialize(){if(!this.initialized){if(this.initPromise)return this.initPromise;this.initPromise=this.doInitialize(),await this.initPromise,this.initialized=!0,this.refreshRole()}}async doInitialize(){this.becomeHostPromise=new Promise(e=>{this.becomeHostResolver=e}),await this.hostElection.start();let e=new Promise(e=>{setTimeout(()=>e(`timeout`),500)});await Promise.race([this.becomeHostPromise.then(()=>`host`),e])===`host`||this.hostElection.isHost&&await this.becomeHostPromise}async becomeHost(){f.log(`Tab ${this.tabId} becoming Host for ${this.options.path}`),this.dbHost=await k.create({dbName:this.options.path,config:this.sqliteConfig}),this.txnSessionManager=new A(this.dbHost,this.hostElection),this.txnSessionManager.startWatchdog(),this.rpcChannel.setRequestHandler(this.handleRequest.bind(this)),this.becomeHostResolver&&=(this.becomeHostResolver(),void 0),this.refreshRole()}loseHost(){f.log(`Tab ${this.tabId} losing Host for ${this.options.path}`),this.rpcChannel.setRequestHandler(void 0),this.txnSessionManager&&=(this.txnSessionManager.close(),null),this.dbHost&&=(this.dbHost.close(),null),this.refreshRole()}handleVisibilityChange(e){e&&!this.hostElection.isHost&&!this.closed&&this.hostElection.start()}async handleRequest(e){if(!this.dbHost)throw Error(`Not Host`);switch(e.t){case`req`:return await this.handleRpcRequest(e);case`txn_begin`:return await this.handleTxnBegin(e);case`txn_exec`:return await this.handleTxnExec(e);case`txn_commit`:return await this.handleTxnCommit(e);case`txn_rollback`:return await this.handleTxnRollback(e);case`txn_heartbeat`:this.handleTxnHeartbeat(e);return;default:throw Error(`Unknown request type: ${e.t}`)}}async handleRpcRequest(e){let{op:t,payload:n}=e,{sql:r,params:i}=n;switch(this.txnSessionManager&&await this.txnSessionManager.waitForTransactionSlot(),t){case`query`:return{rows:await this.dbHost.query(r,i),columns:[]};case`queryRaw`:return await this.dbHost.queryRaw(r,i);case`run`:return await this.dbHost.run(r,i);case`exec`:return await this.dbHost.exec(r),null;default:throw Error(`Unknown RPC operation: ${String(t)}`)}}async handleTxnBegin(e){await this.txnSessionManager.beginTransaction(e.txnId,e.from,e.mode)}async handleTxnExec(e){let{txnId:t,op:n,payload:r}=e,{sql:i,params:a}=r;return await this.txnSessionManager.execInTransaction(t,async()=>{switch(n){case`query`:return{rows:await this.dbHost.query(i,a),columns:[]};case`queryRaw`:return await this.dbHost.queryRaw(i,a);case`run`:return await this.dbHost.run(i,a);case`exec`:return await this.dbHost.exec(i),null;default:throw Error(`Unknown transaction operation: ${String(n)}`)}})}async handleTxnCommit(e){await this.txnSessionManager.commitTransaction(e.txnId)}async handleTxnRollback(e){await this.txnSessionManager.rollbackTransaction(e.txnId)}handleTxnHeartbeat(e){this.txnSessionManager?.recordHeartbeat(e.txnId)}async query(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.query(e,t):(await this.rpcChannel.request(`query`,{sql:e,params:t})).rows}async queryRaw(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.queryRaw(e,t):await this.rpcChannel.request(`queryRaw`,{sql:e,params:t})}async run(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.dbHost.run(e,t):await this.rpcChannel.request(`run`,{sql:e,params:t})}async exec(e){if(await this.initialize(),this.isHost&&this.dbHost){await this.dbHost.exec(e);return}await this.rpcChannel.request(`exec`,{sql:e})}async transaction(e){return await this.initialize(),this.isHost&&this.dbHost?await this.executeLocalTransaction(e,!1):await this.executeRemoteTransaction(e)}async asyncTransaction(e,t){return await this.initialize(),this.isHost&&this.dbHost?await this.executeLocalAsyncTransaction(e,t):await this.executeRemoteTransaction(e,t)}getConnectionType(){return`direct`}async close(){this.closed||(this.closed=!0,this.refreshRole(),this.visibilityUnsubscribe?.(),await this.hostElection.close(),this.rpcChannel.close(),this.visibilityManager.destroy(),this.txnSessionManager&&=(await this.txnSessionManager.close(),null),this.dbHost&&=(await this.dbHost.close(),null))}computeRole(){return this.closed?`unknown`:this.hostElection.isHost?this.dbHost?`host`:`unknown`:this.initialized?`participant`:`unknown`}refreshRole(){let e=this.computeRole();e!==this.roleValue&&(this.roleValue=e,this.roleListeners.forEach(t=>{try{t(e)}catch(e){f.error(`Role change listener threw:`,e)}}))}async executeLocalTransaction(e,t){await this.dbHost.execRaw(`BEGIN`);let n=new M(this.dbHost,t);try{let t=e(n),r=t instanceof Promise?await t:t;return await this.dbHost.execRaw(`COMMIT`),r}catch(e){try{await this.dbHost.execRaw(`ROLLBACK`)}catch(e){f.error(`Rollback failed:`,e)}throw e}}async executeLocalAsyncTransaction(e,t){let n=t?.timeoutMs??3e4;await this.dbHost.execRaw(`BEGIN IMMEDIATE`);let r=new M(this.dbHost,!0),i,a=!1;try{let t=new Promise((e,t)=>{i=setTimeout(()=>{a||t(Error(`Async transaction timeout after ${n}ms`))},n)}),o=await Promise.race([e(r),t]);return a=!0,i&&clearTimeout(i),await this.dbHost.execRaw(`COMMIT`),o}catch(e){a=!0,i&&clearTimeout(i);try{await this.dbHost.execRaw(`ROLLBACK`)}catch(e){f.error(`Rollback failed:`,e)}throw e}}async executeRemoteTransaction(e,t){let n=w(),r=t?.timeoutMs??3e4;await this.rpcChannel.txnBegin(n,`immediate`,r);let i=new j(this.rpcChannel,n),a,o=!1;try{let t=new Promise((e,t)=>{a=setTimeout(()=>{o||t(Error(`Remote transaction timeout after ${r}ms`))},r)}),n=e(i),s=n instanceof Promise?n:Promise.resolve(n),c=await Promise.race([s,t]);return o=!0,a&&clearTimeout(a),await i._commit(),c}catch(e){o=!0,a&&clearTimeout(a);try{await i._rollback()}catch(e){f.error(`Remote rollback failed:`,e)}throw e}}getSQLiteInfo(){let e=this.isHost?this.dbHost!==null:this.initialized&&!this.closed;return{config:this.sqliteConfig,isInitialized:this.initialized,isHost:this.isHost,isReady:e,tabId:this.tabId,hasDatabase:this.dbHost!==null,usesWorker:this.dbHost?.isWorkerDbActive()??!1,activeStorageBackend:this.dbHost?.getActiveStorageBackend()??`unknown`,activeOpfsVfs:this.dbHost?.getActiveOpfsVfs()}}getActiveStorageBackend(){return this.dbHost?.getActiveStorageBackend()??`unknown`}};export{T as a,C as c,x as d,v as f,k as i,w as l,g as m,j as n,y as o,_ as p,A as r,S as s,N as t,b as u}; | ||
| //# sourceMappingURL=browser2.mjs.map |
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, i as QueryResult, l as UniStoreConnection, n as ConnectionType, o as SQLiteParams, r as DurableObjectStorage, s as SQLiteValue, u as UniStoreOptions } from "./types.mjs"; | ||
| import { a as DurableObjectStorage, c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, i as ConnectionType, l as SQLiteValue, o as QueryResult, s as RunResult } from "./types.mjs"; | ||
| import { t as BaseAdapter } from "./base.mjs"; | ||
@@ -3,0 +3,0 @@ |
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, i as QueryResult, l as UniStoreConnection, n as ConnectionType, o as SQLiteParams, r as DurableObjectStorage, s as SQLiteValue, u as UniStoreOptions } from "./types.js"; | ||
| import { a as DurableObjectStorage, c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, i as ConnectionType, l as SQLiteValue, o as QueryResult, s as RunResult } from "./types.js"; | ||
| import { t as BaseAdapter } from "./base.js"; | ||
@@ -3,0 +3,0 @@ |
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, i as QueryResult, l as UniStoreConnection, o as SQLiteParams, s as SQLiteValue, t as ChangeEvent, u as UniStoreOptions } from "./types.mjs"; | ||
| import { c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, l as SQLiteValue, o as QueryResult, s as RunResult, t as ChangeEvent } from "./types.mjs"; | ||
| import { n as createCloudflareDOAdapter, t as CloudflareDOAdapter } from "./cloudflare-do.mjs"; | ||
@@ -3,0 +3,0 @@ |
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, i as QueryResult, l as UniStoreConnection, o as SQLiteParams, s as SQLiteValue, t as ChangeEvent, u as UniStoreOptions } from "./types.js"; | ||
| import { c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, l as SQLiteValue, o as QueryResult, s as RunResult, t as ChangeEvent } from "./types.js"; | ||
| import { n as createCloudflareDOAdapter, t as CloudflareDOAdapter } from "./cloudflare-do.js"; | ||
@@ -3,0 +3,0 @@ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.cjs","names":["CloudflareDOAdapter"],"sources":["../src/index.ts"],"sourcesContent":["export type {\n ChangeEvent,\n ConnectionType,\n QueryResult,\n RunResult,\n SQLiteParams,\n SQLiteValue,\n SQLiteWasmConfig,\n UniStoreConnection,\n UniStoreOptions,\n} from \"./types.js\";\n\nexport type {\n BrowserSQLiteDatabase,\n BrowserSQLiteStatement,\n NodeSQLiteDatabase,\n NodeSQLiteStatement,\n} from \"./platform-types.js\";\n\n// Export the new Cloudflare DO adapter\nexport { CloudflareDOAdapter, createCloudflareDOAdapter } from \"./adapters/cloudflare-do.js\";\n\nimport type { UniStoreConnection, UniStoreOptions } from \"./types.js\";\n\nexport async function openStore(options: UniStoreOptions): Promise<UniStoreConnection> {\n const platform = detectPlatform();\n\n switch (platform) {\n case \"node\":\n const { NodeAdapter } = await import(\"./adapters/node.js\");\n return new NodeAdapter(options);\n\n case \"browser\":\n const { BrowserAdapter } = await import(\"./adapters/browser/index.js\");\n return BrowserAdapter.create(options);\n\n case \"cloudflare\":\n const { CloudflareDOAdapter } = await import(\"./adapters/cloudflare-do.js\");\n return new CloudflareDOAdapter(options);\n\n default:\n throw new Error(`Unsupported platform: ${platform}`);\n }\n}\n\ntype Platform = \"node\" | \"browser\" | \"cloudflare\" | \"unknown\";\n\nfunction detectPlatform(): Platform {\n if (typeof globalThis !== \"undefined\") {\n if (globalThis.process?.versions?.node) {\n return \"node\";\n }\n\n if (globalThis.navigator?.userAgent === \"Cloudflare-Workers\") {\n return \"cloudflare\";\n }\n\n if (globalThis.navigator?.userAgent) {\n return \"browser\";\n }\n }\n\n return \"unknown\";\n}\n"],"mappings":"wCAwBA,eAAsB,EAAU,EAAuD,CACrF,IAAM,EAAW,GAAgB,CAEjC,OAAQ,EAAR,CACE,IAAK,OACH,GAAM,CAAE,eAAgB,MAAA,QAAA,SAAA,CAAA,SAAA,QAAM,cAAA,CAAA,CAC9B,OAAO,IAAI,EAAY,EAAQ,CAEjC,IAAK,UACH,GAAM,CAAE,kBAAmB,MAAA,QAAA,SAAA,CAAA,SAAA,QAAM,iBAAA,CAAA,CACjC,OAAO,EAAe,OAAO,EAAQ,CAEvC,IAAK,aACH,GAAM,CAAE,oBAAA,GAAwB,MAAA,QAAA,SAAA,CAAA,SAAA,QAAM,sBAAA,CAAA,CACtC,OAAO,IAAIA,EAAoB,EAAQ,CAEzC,QACE,MAAU,MAAM,yBAAyB,IAAW,EAM1D,SAAS,GAA2B,CAClC,GAAI,OAAO,WAAe,IAAa,CACrC,GAAI,WAAW,SAAS,UAAU,KAChC,MAAO,OAGT,GAAI,WAAW,WAAW,YAAc,qBACtC,MAAO,aAGT,GAAI,WAAW,WAAW,UACxB,MAAO,UAIX,MAAO"} | ||
| {"version":3,"file":"index.cjs","names":["CloudflareDOAdapter"],"sources":["../src/index.ts"],"sourcesContent":["export type {\n ChangeEvent,\n ConnectionType,\n ConnectionRole,\n ConnectionRoleChangeListener,\n QueryResult,\n RunResult,\n SQLiteParams,\n SQLiteValue,\n SQLiteWasmConfig,\n UniStoreConnection,\n UniStoreOptions,\n} from \"./types.js\";\n\nexport type {\n BrowserSQLiteDatabase,\n BrowserSQLiteStatement,\n NodeSQLiteDatabase,\n NodeSQLiteStatement,\n} from \"./platform-types.js\";\n\n// Export the new Cloudflare DO adapter\nexport { CloudflareDOAdapter, createCloudflareDOAdapter } from \"./adapters/cloudflare-do.js\";\n\nimport type { UniStoreConnection, UniStoreOptions } from \"./types.js\";\n\nexport async function openStore(options: UniStoreOptions): Promise<UniStoreConnection> {\n const platform = detectPlatform();\n\n switch (platform) {\n case \"node\":\n const { NodeAdapter } = await import(\"./adapters/node.js\");\n return new NodeAdapter(options);\n\n case \"browser\":\n const { BrowserAdapter } = await import(\"./adapters/browser/index.js\");\n return BrowserAdapter.create(options);\n\n case \"cloudflare\":\n const { CloudflareDOAdapter } = await import(\"./adapters/cloudflare-do.js\");\n return new CloudflareDOAdapter(options);\n\n default:\n throw new Error(`Unsupported platform: ${platform}`);\n }\n}\n\ntype Platform = \"node\" | \"browser\" | \"cloudflare\" | \"unknown\";\n\nfunction detectPlatform(): Platform {\n if (typeof globalThis !== \"undefined\") {\n if (globalThis.process?.versions?.node) {\n return \"node\";\n }\n\n if (globalThis.navigator?.userAgent === \"Cloudflare-Workers\") {\n return \"cloudflare\";\n }\n\n if (globalThis.navigator?.userAgent) {\n return \"browser\";\n }\n }\n\n return \"unknown\";\n}\n"],"mappings":"wCA0BA,eAAsB,EAAU,EAAuD,CACrF,IAAM,EAAW,GAAgB,CAEjC,OAAQ,EAAR,CACE,IAAK,OACH,GAAM,CAAE,eAAgB,MAAA,QAAA,SAAA,CAAA,SAAA,QAAM,cAAA,CAAA,CAC9B,OAAO,IAAI,EAAY,EAAQ,CAEjC,IAAK,UACH,GAAM,CAAE,kBAAmB,MAAA,QAAA,SAAA,CAAA,SAAA,QAAM,iBAAA,CAAA,CACjC,OAAO,EAAe,OAAO,EAAQ,CAEvC,IAAK,aACH,GAAM,CAAE,oBAAA,GAAwB,MAAA,QAAA,SAAA,CAAA,SAAA,QAAM,sBAAA,CAAA,CACtC,OAAO,IAAIA,EAAoB,EAAQ,CAEzC,QACE,MAAU,MAAM,yBAAyB,IAAW,EAM1D,SAAS,GAA2B,CAClC,GAAI,OAAO,WAAe,IAAa,CACrC,GAAI,WAAW,SAAS,UAAU,KAChC,MAAO,OAGT,GAAI,WAAW,WAAW,YAAc,qBACtC,MAAO,aAGT,GAAI,WAAW,WAAW,UACxB,MAAO,UAIX,MAAO"} |
+2
-2
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, c as SQLiteWasmConfig, i as QueryResult, l as UniStoreConnection, n as ConnectionType, o as SQLiteParams, s as SQLiteValue, t as ChangeEvent, u as UniStoreOptions } from "./types.mjs"; | ||
| import { c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, i as ConnectionType, l as SQLiteValue, n as ConnectionRole, o as QueryResult, r as ConnectionRoleChangeListener, s as RunResult, t as ChangeEvent, u as SQLiteWasmConfig } from "./types.mjs"; | ||
| import { i as NodeSQLiteStatement, n as BrowserSQLiteStatement, r as NodeSQLiteDatabase, t as BrowserSQLiteDatabase } from "./platform-types.mjs"; | ||
@@ -8,3 +8,3 @@ import { n as createCloudflareDOAdapter, t as CloudflareDOAdapter } from "./cloudflare-do.mjs"; | ||
| //#endregion | ||
| export { type BrowserSQLiteDatabase, type BrowserSQLiteStatement, type ChangeEvent, CloudflareDOAdapter, type ConnectionType, type NodeSQLiteDatabase, type NodeSQLiteStatement, type QueryResult, type RunResult, type SQLiteParams, type SQLiteValue, type SQLiteWasmConfig, type UniStoreConnection, type UniStoreOptions, createCloudflareDOAdapter, openStore }; | ||
| export { type BrowserSQLiteDatabase, type BrowserSQLiteStatement, type ChangeEvent, CloudflareDOAdapter, type ConnectionRole, type ConnectionRoleChangeListener, type ConnectionType, type NodeSQLiteDatabase, type NodeSQLiteStatement, type QueryResult, type RunResult, type SQLiteParams, type SQLiteValue, type SQLiteWasmConfig, type UniStoreConnection, type UniStoreOptions, createCloudflareDOAdapter, openStore }; | ||
| //# sourceMappingURL=index.d.mts.map |
+2
-2
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, c as SQLiteWasmConfig, i as QueryResult, l as UniStoreConnection, n as ConnectionType, o as SQLiteParams, s as SQLiteValue, t as ChangeEvent, u as UniStoreOptions } from "./types.js"; | ||
| import { c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, i as ConnectionType, l as SQLiteValue, n as ConnectionRole, o as QueryResult, r as ConnectionRoleChangeListener, s as RunResult, t as ChangeEvent, u as SQLiteWasmConfig } from "./types.js"; | ||
| import { i as NodeSQLiteStatement, n as BrowserSQLiteStatement, r as NodeSQLiteDatabase, t as BrowserSQLiteDatabase } from "./platform-types.js"; | ||
@@ -8,3 +8,3 @@ import { n as createCloudflareDOAdapter, t as CloudflareDOAdapter } from "./cloudflare-do.js"; | ||
| //#endregion | ||
| export { type BrowserSQLiteDatabase, type BrowserSQLiteStatement, type ChangeEvent, CloudflareDOAdapter, type ConnectionType, type NodeSQLiteDatabase, type NodeSQLiteStatement, type QueryResult, type RunResult, type SQLiteParams, type SQLiteValue, type SQLiteWasmConfig, type UniStoreConnection, type UniStoreOptions, createCloudflareDOAdapter, openStore }; | ||
| export { type BrowserSQLiteDatabase, type BrowserSQLiteStatement, type ChangeEvent, CloudflareDOAdapter, type ConnectionRole, type ConnectionRoleChangeListener, type ConnectionType, type NodeSQLiteDatabase, type NodeSQLiteStatement, type QueryResult, type RunResult, type SQLiteParams, type SQLiteValue, type SQLiteWasmConfig, type UniStoreConnection, type UniStoreOptions, createCloudflareDOAdapter, openStore }; | ||
| //# sourceMappingURL=index.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.mjs","names":["CloudflareDOAdapter"],"sources":["../src/index.ts"],"sourcesContent":["export type {\n ChangeEvent,\n ConnectionType,\n QueryResult,\n RunResult,\n SQLiteParams,\n SQLiteValue,\n SQLiteWasmConfig,\n UniStoreConnection,\n UniStoreOptions,\n} from \"./types.js\";\n\nexport type {\n BrowserSQLiteDatabase,\n BrowserSQLiteStatement,\n NodeSQLiteDatabase,\n NodeSQLiteStatement,\n} from \"./platform-types.js\";\n\n// Export the new Cloudflare DO adapter\nexport { CloudflareDOAdapter, createCloudflareDOAdapter } from \"./adapters/cloudflare-do.js\";\n\nimport type { UniStoreConnection, UniStoreOptions } from \"./types.js\";\n\nexport async function openStore(options: UniStoreOptions): Promise<UniStoreConnection> {\n const platform = detectPlatform();\n\n switch (platform) {\n case \"node\":\n const { NodeAdapter } = await import(\"./adapters/node.js\");\n return new NodeAdapter(options);\n\n case \"browser\":\n const { BrowserAdapter } = await import(\"./adapters/browser/index.js\");\n return BrowserAdapter.create(options);\n\n case \"cloudflare\":\n const { CloudflareDOAdapter } = await import(\"./adapters/cloudflare-do.js\");\n return new CloudflareDOAdapter(options);\n\n default:\n throw new Error(`Unsupported platform: ${platform}`);\n }\n}\n\ntype Platform = \"node\" | \"browser\" | \"cloudflare\" | \"unknown\";\n\nfunction detectPlatform(): Platform {\n if (typeof globalThis !== \"undefined\") {\n if (globalThis.process?.versions?.node) {\n return \"node\";\n }\n\n if (globalThis.navigator?.userAgent === \"Cloudflare-Workers\") {\n return \"cloudflare\";\n }\n\n if (globalThis.navigator?.userAgent) {\n return \"browser\";\n }\n }\n\n return \"unknown\";\n}\n"],"mappings":"2CAwBA,eAAsB,EAAU,EAAuD,CACrF,IAAM,EAAW,GAAgB,CAEjC,OAAQ,EAAR,CACE,IAAK,OACH,GAAM,CAAE,eAAgB,MAAM,OAAO,eACrC,OAAO,IAAI,EAAY,EAAQ,CAEjC,IAAK,UACH,GAAM,CAAE,kBAAmB,MAAM,OAAO,kBACxC,OAAO,EAAe,OAAO,EAAQ,CAEvC,IAAK,aACH,GAAM,CAAE,oBAAA,GAAwB,MAAM,OAAO,uBAC7C,OAAO,IAAIA,EAAoB,EAAQ,CAEzC,QACE,MAAU,MAAM,yBAAyB,IAAW,EAM1D,SAAS,GAA2B,CAClC,GAAI,OAAO,WAAe,IAAa,CACrC,GAAI,WAAW,SAAS,UAAU,KAChC,MAAO,OAGT,GAAI,WAAW,WAAW,YAAc,qBACtC,MAAO,aAGT,GAAI,WAAW,WAAW,UACxB,MAAO,UAIX,MAAO"} | ||
| {"version":3,"file":"index.mjs","names":["CloudflareDOAdapter"],"sources":["../src/index.ts"],"sourcesContent":["export type {\n ChangeEvent,\n ConnectionType,\n ConnectionRole,\n ConnectionRoleChangeListener,\n QueryResult,\n RunResult,\n SQLiteParams,\n SQLiteValue,\n SQLiteWasmConfig,\n UniStoreConnection,\n UniStoreOptions,\n} from \"./types.js\";\n\nexport type {\n BrowserSQLiteDatabase,\n BrowserSQLiteStatement,\n NodeSQLiteDatabase,\n NodeSQLiteStatement,\n} from \"./platform-types.js\";\n\n// Export the new Cloudflare DO adapter\nexport { CloudflareDOAdapter, createCloudflareDOAdapter } from \"./adapters/cloudflare-do.js\";\n\nimport type { UniStoreConnection, UniStoreOptions } from \"./types.js\";\n\nexport async function openStore(options: UniStoreOptions): Promise<UniStoreConnection> {\n const platform = detectPlatform();\n\n switch (platform) {\n case \"node\":\n const { NodeAdapter } = await import(\"./adapters/node.js\");\n return new NodeAdapter(options);\n\n case \"browser\":\n const { BrowserAdapter } = await import(\"./adapters/browser/index.js\");\n return BrowserAdapter.create(options);\n\n case \"cloudflare\":\n const { CloudflareDOAdapter } = await import(\"./adapters/cloudflare-do.js\");\n return new CloudflareDOAdapter(options);\n\n default:\n throw new Error(`Unsupported platform: ${platform}`);\n }\n}\n\ntype Platform = \"node\" | \"browser\" | \"cloudflare\" | \"unknown\";\n\nfunction detectPlatform(): Platform {\n if (typeof globalThis !== \"undefined\") {\n if (globalThis.process?.versions?.node) {\n return \"node\";\n }\n\n if (globalThis.navigator?.userAgent === \"Cloudflare-Workers\") {\n return \"cloudflare\";\n }\n\n if (globalThis.navigator?.userAgent) {\n return \"browser\";\n }\n }\n\n return \"unknown\";\n}\n"],"mappings":"2CA0BA,eAAsB,EAAU,EAAuD,CACrF,IAAM,EAAW,GAAgB,CAEjC,OAAQ,EAAR,CACE,IAAK,OACH,GAAM,CAAE,eAAgB,MAAM,OAAO,eACrC,OAAO,IAAI,EAAY,EAAQ,CAEjC,IAAK,UACH,GAAM,CAAE,kBAAmB,MAAM,OAAO,kBACxC,OAAO,EAAe,OAAO,EAAQ,CAEvC,IAAK,aACH,GAAM,CAAE,oBAAA,GAAwB,MAAM,OAAO,uBAC7C,OAAO,IAAIA,EAAoB,EAAQ,CAEzC,QACE,MAAU,MAAM,yBAAyB,IAAW,EAM1D,SAAS,GAA2B,CAClC,GAAI,OAAO,WAAe,IAAa,CACrC,GAAI,WAAW,SAAS,UAAU,KAChC,MAAO,OAGT,GAAI,WAAW,WAAW,YAAc,qBACtC,MAAO,aAGT,GAAI,WAAW,WAAW,UACxB,MAAO,UAIX,MAAO"} |
+1
-1
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, i as QueryResult, l as UniStoreConnection, n as ConnectionType, o as SQLiteParams, s as SQLiteValue, t as ChangeEvent, u as UniStoreOptions } from "./types.mjs"; | ||
| import { c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, i as ConnectionType, l as SQLiteValue, o as QueryResult, s as RunResult, t as ChangeEvent } from "./types.mjs"; | ||
| import { i as NodeSQLiteStatement, r as NodeSQLiteDatabase } from "./platform-types.mjs"; | ||
@@ -3,0 +3,0 @@ import { t as BaseAdapter } from "./base.mjs"; |
+1
-1
@@ -1,2 +0,2 @@ | ||
| import { a as RunResult, i as QueryResult, l as UniStoreConnection, n as ConnectionType, o as SQLiteParams, s as SQLiteValue, t as ChangeEvent, u as UniStoreOptions } from "./types.js"; | ||
| import { c as SQLiteParams, d as UniStoreConnection, f as UniStoreOptions, i as ConnectionType, l as SQLiteValue, o as QueryResult, s as RunResult, t as ChangeEvent } from "./types.js"; | ||
| import { i as NodeSQLiteStatement, r as NodeSQLiteDatabase } from "./platform-types.js"; | ||
@@ -3,0 +3,0 @@ import { t as BaseAdapter } from "./base.js"; |
+1
-1
@@ -1,2 +0,2 @@ | ||
| const e=require(`./chunk.cjs`),t=require(`./base.cjs`);let n=require(`debug`);n=e.t(n);let r=require(`better-sqlite3`);r=e.t(r);const i=`unisqlite`,a=`0.4.0`;function o(e){let t=(0,n.default)(`${i}:${e}`),r=(0,n.default)(`${i}:${e}:warn`),a=(0,n.default)(`${i}:${e}:error`);return a.enabled=!0,{log:t,warn:r,error:a}}const s=o(`node`);var c=class extends t.t{constructor(e){super(e),this._closed=!1,s.log(`UniSQLite v0.4.0 - Opening database: ${e.path}`),this.db=new r.default(e.path,{fileMustExist:!1}),this.db.pragma(`journal_mode = WAL`),this.db.pragma(`busy_timeout = 5000`),s.log(`Database opened with WAL mode`)}normalizeParams(e){return e?Array.isArray(e)?e:Object.values(e):[]}convertBuffersToUint8Array(e){if(e==null)return e;if(Buffer.isBuffer(e))return new Uint8Array(e);if(Array.isArray(e))return e.map(e=>this.convertBuffersToUint8Array(e));if(typeof e==`object`){let t={};for(let[n,r]of Object.entries(e))t[n]=this.convertBuffersToUint8Array(r);return t}return e}checkConnection(){if(this._closed||!this.db.open)throw Error(`Database connection is closed`)}async query(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r);return this.convertBuffersToUint8Array(i)}async queryRaw(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=n.columns().map(e=>e.name);return{rows:this.convertBuffersToUint8Array(i),columns:a,rowsAffected:0,lastInsertRowId:0}}async run(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r);return{rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid}}async exec(e){this.checkConnection(),this.db.exec(e)}transaction(e){this.checkConnection(),this.db.exec(`BEGIN`);let t=new u(this.db);return(async()=>{try{let n=await e(t);return this.db.exec(`COMMIT`),n}catch(e){throw this.db.exec(`ROLLBACK`),e}})()}async asyncTransaction(e,t){this.checkConnection();let n=t?.timeoutMs??3e4,r=!1;try{this.db.exec(`BEGIN IMMEDIATE`)}catch(e){if(e.message.includes(`cannot start a transaction within a transaction`))r=!0;else throw e}let i,a=!1;try{let t=new Promise((e,t)=>{i=setTimeout(()=>{a||t(Error(`Async transaction timeout after ${n}ms`))},n)}),o=new d(this.db),s=await Promise.race([e(o),t]);return a=!0,i&&clearTimeout(i),r||this.db.exec(`COMMIT`),s}catch(e){a=!0,i&&clearTimeout(i);try{this.db.exec(`ROLLBACK`)}catch(e){s.error(`Rollback failed:`,e)}throw e}}getConnectionType(){return`direct`}async close(){!this._closed&&this.db.open&&(this.db.close(),this._closed=!0)}get isOpen(){return!this._closed&&this.db.open}},l=class{constructor(e){this.db=e,this.inTransaction=!0}normalizeParams(e){return e?Array.isArray(e)?e:Object.values(e):[]}convertBuffersToUint8Array(e){if(e==null)return e;if(Buffer.isBuffer(e))return new Uint8Array(e);if(Array.isArray(e))return e.map(e=>this.convertBuffersToUint8Array(e));if(typeof e==`object`){let t={};for(let[n,r]of Object.entries(e))t[n]=this.convertBuffersToUint8Array(r);return t}return e}query(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=this.convertBuffersToUint8Array(i);return Promise.resolve(a)}queryRaw(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=n.columns().map(e=>e.name),o={rows:this.convertBuffersToUint8Array(i),columns:a,rowsAffected:0,lastInsertRowId:0};return Promise.resolve(o)}run(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r),a={rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid};return Promise.resolve(a)}exec(e){return this.db.exec(e),Promise.resolve()}querySync(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r);return this.convertBuffersToUint8Array(i)}runSync(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r);return{rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid}}execSync(e){this.db.exec(e)}async close(){}},u=class extends l{getConnectionType(){return`syncTxn`}async transaction(e){return await e(this)}async asyncTransaction(e,t){throw Error(`asyncTransaction is not supported in syncTxn connections. Use transaction() instead or create a direct connection.`)}},d=class extends l{getConnectionType(){return`asyncTxn`}async transaction(e){return await e(this)}async asyncTransaction(e,t){return await e(this)}};Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return c}}); | ||
| var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));const c=require(`./base.cjs`);let l=require(`better-sqlite3`);l=s(l);let u=require(`debug`);u=s(u);const d=`unisqlite`,f=`0.4.0`;function p(e){let t=(0,u.default)(`${d}:${e}`),n=(0,u.default)(`${d}:${e}:warn`),r=(0,u.default)(`${d}:${e}:error`);return r.enabled=!0,{log:t,warn:n,error:r}}const m=p(`node`);var h=class extends c.t{constructor(e){super(e),this._closed=!1,m.log(`UniSQLite v0.4.0 - Opening database: ${e.path}`),this.db=new l.default(e.path,{fileMustExist:!1}),this.db.pragma(`journal_mode = WAL`),this.db.pragma(`busy_timeout = 5000`),m.log(`Database opened with WAL mode`)}normalizeParams(e){return e?Array.isArray(e)?e:Object.values(e):[]}convertBuffersToUint8Array(e){if(e==null)return e;if(Buffer.isBuffer(e))return new Uint8Array(e);if(Array.isArray(e))return e.map(e=>this.convertBuffersToUint8Array(e));if(typeof e==`object`){let t={};for(let[n,r]of Object.entries(e))t[n]=this.convertBuffersToUint8Array(r);return t}return e}checkConnection(){if(this._closed||!this.db.open)throw Error(`Database connection is closed`)}async query(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r);return this.convertBuffersToUint8Array(i)}async queryRaw(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=n.columns().map(e=>e.name);return{rows:this.convertBuffersToUint8Array(i),columns:a,rowsAffected:0,lastInsertRowId:0}}async run(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r);return{rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid}}async exec(e){this.checkConnection(),this.db.exec(e)}transaction(e){this.checkConnection(),this.db.exec(`BEGIN`);let t=new _(this.db);return(async()=>{try{let n=await e(t);return this.db.exec(`COMMIT`),n}catch(e){throw this.db.exec(`ROLLBACK`),e}})()}async asyncTransaction(e,t){this.checkConnection();let n=t?.timeoutMs??3e4,r=!1;try{this.db.exec(`BEGIN IMMEDIATE`)}catch(e){if(e.message.includes(`cannot start a transaction within a transaction`))r=!0;else throw e}let i,a=!1;try{let t=new Promise((e,t)=>{i=setTimeout(()=>{a||t(Error(`Async transaction timeout after ${n}ms`))},n)}),o=new v(this.db),s=await Promise.race([e(o),t]);return a=!0,i&&clearTimeout(i),r||this.db.exec(`COMMIT`),s}catch(e){a=!0,i&&clearTimeout(i);try{this.db.exec(`ROLLBACK`)}catch(e){m.error(`Rollback failed:`,e)}throw e}}getConnectionType(){return`direct`}async close(){!this._closed&&this.db.open&&(this.db.close(),this._closed=!0)}get isOpen(){return!this._closed&&this.db.open}},g=class{constructor(e){this.db=e,this.inTransaction=!0}normalizeParams(e){return e?Array.isArray(e)?e:Object.values(e):[]}convertBuffersToUint8Array(e){if(e==null)return e;if(Buffer.isBuffer(e))return new Uint8Array(e);if(Array.isArray(e))return e.map(e=>this.convertBuffersToUint8Array(e));if(typeof e==`object`){let t={};for(let[n,r]of Object.entries(e))t[n]=this.convertBuffersToUint8Array(r);return t}return e}query(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=this.convertBuffersToUint8Array(i);return Promise.resolve(a)}queryRaw(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=n.columns().map(e=>e.name),o={rows:this.convertBuffersToUint8Array(i),columns:a,rowsAffected:0,lastInsertRowId:0};return Promise.resolve(o)}run(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r),a={rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid};return Promise.resolve(a)}exec(e){return this.db.exec(e),Promise.resolve()}querySync(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r);return this.convertBuffersToUint8Array(i)}runSync(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r);return{rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid}}execSync(e){this.db.exec(e)}async close(){}},_=class extends g{getConnectionType(){return`syncTxn`}async transaction(e){return await e(this)}async asyncTransaction(e,t){throw Error(`asyncTransaction is not supported in syncTxn connections. Use transaction() instead or create a direct connection.`)}},v=class extends g{getConnectionType(){return`asyncTxn`}async transaction(e){return await e(this)}async asyncTransaction(e,t){return await e(this)}};Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return h}}); | ||
| //# sourceMappingURL=node2.cjs.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"node2.cjs","names":["BaseAdapter","Database"],"sources":["../src/adapters/logger.ts","../src/adapters/node.ts"],"sourcesContent":["/**\n * Debug Logger for UniSQLite\n *\n * Uses the `debug` package for conditional logging.\n * Enable logs by setting DEBUG env var:\n *\n * - `unisqlite:*` - Enable all unisqlite logs\n * - `unisqlite:node` - Node.js adapter logs only\n *\n * In Node.js:\n * DEBUG=unisqlite:* node app.js\n */\n\nimport debug from \"debug\";\n\nconst NAMESPACE = \"unisqlite\";\n\n// Package version - will be output on initialization\nexport const VERSION = \"0.4.0\";\n\n/**\n * Logger interface with log, warn, and error methods\n */\nexport interface Logger {\n log: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n}\n\n/**\n * Create a namespaced debug logger with log/warn/error methods\n */\nfunction createModuleLogger(name: string): Logger {\n const logDebug = debug(`${NAMESPACE}:${name}`);\n const warnDebug = debug(`${NAMESPACE}:${name}:warn`);\n const errorDebug = debug(`${NAMESPACE}:${name}:error`);\n\n // Enable error logs by default (they go to stderr)\n errorDebug.enabled = true;\n\n return {\n log: logDebug as (...args: unknown[]) => void,\n warn: warnDebug as (...args: unknown[]) => void,\n error: errorDebug as (...args: unknown[]) => void,\n };\n}\n\n// Pre-created logger for node adapter\nexport const nodeLogger = createModuleLogger(\"node\");\n\n/**\n * Create a custom debug logger\n */\nexport function createDebugLogger(name: string): Logger {\n return createModuleLogger(name);\n}\n\n/**\n * Enable debug logging programmatically\n * This is useful for enabling logs without setting localStorage/env\n *\n * @param namespaces - Debug namespaces to enable (default: \"unisqlite:*\")\n *\n * @example\n * // Enable all unisqlite logs\n * enableDebug(\"unisqlite:*\");\n *\n * // Enable only node adapter logs\n * enableDebug(\"unisqlite:node\");\n */\nexport function enableDebug(namespaces: string = \"unisqlite:*\"): void {\n debug.enable(namespaces);\n}\n\n/**\n * Disable all debug logging\n */\nexport function disableDebug(): void {\n debug.disable();\n}\n","import { BaseAdapter } from \"./base.js\";\nimport type {\n QueryResult,\n RunResult,\n SQLiteParams,\n SQLiteValue,\n UniStoreConnection,\n UniStoreOptions,\n ConnectionType,\n} from \"../types.js\";\nimport Database from \"better-sqlite3\";\nimport { nodeLogger as logger, VERSION } from \"./logger.js\";\n\nexport class NodeAdapter extends BaseAdapter {\n private db: Database.Database;\n private _closed: boolean = false;\n\n constructor(options: UniStoreOptions) {\n super(options);\n logger.log(`UniSQLite v${VERSION} - Opening database: ${options.path}`);\n // Open database with WAL mode for better concurrency\n this.db = new Database(options.path, { fileMustExist: false });\n this.db.pragma(\"journal_mode = WAL\");\n this.db.pragma(\"busy_timeout = 5000\"); // 5 second timeout for locks\n logger.log(\"Database opened with WAL mode\");\n }\n\n private normalizeParams(params?: SQLiteParams): unknown[] {\n if (!params) return [];\n if (Array.isArray(params)) return params;\n // Convert object params to array format for better-sqlite3\n // This is a simplified conversion - in practice you'd need to handle named parameters\n return Object.values(params);\n }\n\n private convertBuffersToUint8Array<T>(value: T): T {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (Buffer.isBuffer(value)) {\n return new Uint8Array(value) as T;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => this.convertBuffersToUint8Array(item)) as T;\n }\n\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n result[key] = this.convertBuffersToUint8Array(val);\n }\n return result as T;\n }\n\n return value;\n }\n\n private checkConnection(): void {\n if (this._closed || !this.db.open) {\n throw new Error(\"Database connection is closed\");\n }\n }\n\n async query<T = Record<string, SQLiteValue>>(sql: string, params?: SQLiteParams): Promise<T[]> {\n this.checkConnection();\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const rows = stmt.all(paramsArray) as T[];\n return this.convertBuffersToUint8Array(rows);\n }\n\n async queryRaw<T = Record<string, SQLiteValue>>(sql: string, params?: SQLiteParams): Promise<QueryResult<T>> {\n this.checkConnection();\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const rows = stmt.all(paramsArray) as T[];\n const columns = stmt.columns().map((col) => col.name);\n\n return {\n rows: this.convertBuffersToUint8Array(rows),\n columns,\n rowsAffected: 0,\n lastInsertRowId: 0,\n };\n }\n\n async run(sql: string, params?: SQLiteParams): Promise<RunResult> {\n this.checkConnection();\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const info = stmt.run(paramsArray);\n\n return {\n rowsAffected: info.changes,\n lastInsertRowId: info.lastInsertRowid as number,\n };\n }\n\n async exec(sql: string): Promise<void> {\n this.checkConnection();\n this.db.exec(sql);\n }\n\n transaction<T>(fn: (tx: UniStoreConnection) => Promise<T> | T): Promise<T> {\n this.checkConnection();\n // better-sqlite3 supports synchronous transactions\n this.db.exec(\"BEGIN\");\n const transactionAdapter = new SyncTransactionAdapter(this.db);\n return (async () => {\n try {\n const result = await fn(transactionAdapter);\n this.db.exec(\"COMMIT\");\n return result;\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n })();\n }\n\n /**\n * Execute an async transaction with manual transaction management.\n * This allows async operations within the transaction but comes with timeout support\n * to prevent long-running transactions from blocking the database.\n *\n * @param fn Transaction function that can contain async operations\n * @param options Transaction options including timeout (default: 30000ms)\n */\n async asyncTransaction<T>(fn: (tx: UniStoreConnection) => Promise<T>, options?: { timeoutMs?: number }): Promise<T> {\n this.checkConnection();\n\n const timeoutMs = options?.timeoutMs ?? 30000; // Default 30 seconds\n let alreadyInTransaction = false;\n\n // Manual transaction management using BEGIN/COMMIT/ROLLBACK\n try {\n this.db.exec(\"BEGIN IMMEDIATE\");\n } catch (e) {\n if ((e as Error).message.includes(\"cannot start a transaction within a transaction\")) {\n alreadyInTransaction = true;\n } else {\n throw e;\n }\n }\n\n let timeoutHandle: NodeJS.Timeout | undefined;\n let transactionCompleted = false;\n\n try {\n // Set up timeout\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n if (!transactionCompleted) {\n reject(new Error(`Async transaction timeout after ${timeoutMs}ms`));\n }\n }, timeoutMs);\n });\n\n const transactionAdapter = new AsyncTransactionAdapter(this.db);\n\n // Race between transaction execution and timeout\n const result = await Promise.race([fn(transactionAdapter), timeoutPromise]);\n\n transactionCompleted = true;\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n\n // If we get here, the transaction completed successfully\n if (!alreadyInTransaction) {\n this.db.exec(\"COMMIT\");\n }\n return result;\n } catch (error) {\n transactionCompleted = true;\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n\n try {\n this.db.exec(\"ROLLBACK\");\n } catch (rollbackError) {\n logger.error(\"Rollback failed:\", rollbackError);\n }\n\n throw error;\n }\n }\n\n getConnectionType(): ConnectionType {\n return \"direct\";\n }\n\n async close(): Promise<void> {\n if (!this._closed && this.db.open) {\n this.db.close();\n this._closed = true;\n }\n }\n\n get isOpen(): boolean {\n return !this._closed && this.db.open;\n }\n}\n\nabstract class BaseTransactionAdapter implements UniStoreConnection {\n public readonly inTransaction = true;\n\n constructor(protected db: Database.Database) {}\n\n private normalizeParams(params?: SQLiteParams): unknown[] {\n if (!params) return [];\n if (Array.isArray(params)) return params;\n return Object.values(params);\n }\n\n private convertBuffersToUint8Array<T>(value: T): T {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (Buffer.isBuffer(value)) {\n return new Uint8Array(value) as T;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => this.convertBuffersToUint8Array(item)) as T;\n }\n\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n result[key] = this.convertBuffersToUint8Array(val);\n }\n return result as T;\n }\n\n return value;\n }\n\n // Synchronous database methods for use in transactions\n query<T = Record<string, SQLiteValue>>(sql: string, params?: SQLiteParams): Promise<T[]> {\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const rows = stmt.all(paramsArray) as T[];\n const result = this.convertBuffersToUint8Array(rows);\n return Promise.resolve(result);\n }\n\n queryRaw<T = Record<string, SQLiteValue>>(sql: string, params?: SQLiteParams): Promise<QueryResult<T>> {\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const rows = stmt.all(paramsArray) as T[];\n const columns = stmt.columns().map((col) => col.name);\n\n const result = {\n rows: this.convertBuffersToUint8Array(rows),\n columns,\n rowsAffected: 0,\n lastInsertRowId: 0,\n };\n return Promise.resolve(result);\n }\n\n run(sql: string, params?: SQLiteParams): Promise<RunResult> {\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const info = stmt.run(paramsArray);\n\n const result = {\n rowsAffected: info.changes,\n lastInsertRowId: info.lastInsertRowid as number,\n };\n return Promise.resolve(result);\n }\n\n exec(sql: string): Promise<void> {\n this.db.exec(sql);\n return Promise.resolve();\n }\n\n // Synchronous versions for use in sync transactions\n querySync<T = Record<string, SQLiteValue>>(sql: string, params?: SQLiteParams): T[] {\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const rows = stmt.all(paramsArray) as T[];\n return this.convertBuffersToUint8Array(rows);\n }\n\n runSync(sql: string, params?: SQLiteParams): RunResult {\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const info = stmt.run(paramsArray);\n\n return {\n rowsAffected: info.changes,\n lastInsertRowId: info.lastInsertRowid as number,\n };\n }\n\n execSync(sql: string): void {\n this.db.exec(sql);\n }\n\n abstract getConnectionType(): ConnectionType;\n abstract transaction<T>(fn: (tx: UniStoreConnection) => Promise<T> | T): Promise<T>;\n abstract asyncTransaction<T>(\n fn: (tx: UniStoreConnection) => Promise<T>,\n options?: { timeoutMs?: number }\n ): Promise<T>;\n\n async close(): Promise<void> {\n // No-op for transaction\n }\n}\n\nclass SyncTransactionAdapter extends BaseTransactionAdapter {\n getConnectionType(): ConnectionType {\n return \"syncTxn\";\n }\n\n async transaction<T>(fn: (tx: UniStoreConnection) => Promise<T> | T): Promise<T> {\n // For syncTxn, we can execute the transaction using itself as the connection\n // This allows nested operations within the same transaction\n return await fn(this);\n }\n\n async asyncTransaction<T>(\n _fn: (tx: UniStoreConnection) => Promise<T>,\n _options?: { timeoutMs?: number }\n ): Promise<T> {\n // syncTxn connections cannot run asyncTransaction\n throw new Error(\n \"asyncTransaction is not supported in syncTxn connections. Use transaction() instead or create a direct connection.\"\n );\n }\n}\n\nclass AsyncTransactionAdapter extends BaseTransactionAdapter {\n getConnectionType(): ConnectionType {\n return \"asyncTxn\";\n }\n\n async transaction<T>(fn: (tx: UniStoreConnection) => Promise<T> | T): Promise<T> {\n // For asyncTxn, we can execute the transaction using itself as the connection\n // This allows nested operations within the same transaction\n return await fn(this);\n }\n\n async asyncTransaction<T>(fn: (tx: UniStoreConnection) => Promise<T>, _options?: { timeoutMs?: number }): Promise<T> {\n // For asyncTxn, we can execute asyncTransaction using itself as the connection\n // This allows nested async operations within the same transaction\n return await fn(this);\n }\n}\n"],"mappings":"gIAeA,MAAM,EAAY,YAGL,EAAU,QAcvB,SAAS,EAAmB,EAAsB,CAChD,IAAM,GAAA,EAAA,EAAA,SAAiB,GAAG,EAAU,GAAG,IAAO,CACxC,GAAA,EAAA,EAAA,SAAkB,GAAG,EAAU,GAAG,EAAK,OAAO,CAC9C,GAAA,EAAA,EAAA,SAAmB,GAAG,EAAU,GAAG,EAAK,QAAQ,CAKtD,MAFA,GAAW,QAAU,GAEd,CACL,IAAK,EACL,KAAM,EACN,MAAO,EACR,CAIH,MAAa,EAAa,EAAmB,OAAO,CCnCpD,IAAa,EAAb,cAAiCA,EAAAA,CAAY,CAI3C,YAAY,EAA0B,CACpC,MAAM,EAAQ,cAHW,GAIzB,EAAO,IAAI,wCAA6C,EAAQ,OAAO,CAEvE,KAAK,GAAK,IAAIC,EAAAA,QAAS,EAAQ,KAAM,CAAE,cAAe,GAAO,CAAC,CAC9D,KAAK,GAAG,OAAO,qBAAqB,CACpC,KAAK,GAAG,OAAO,sBAAsB,CACrC,EAAO,IAAI,gCAAgC,CAG7C,gBAAwB,EAAkC,CAKxD,OAJK,EACD,MAAM,QAAQ,EAAO,CAAS,EAG3B,OAAO,OAAO,EAAO,CAJR,EAAE,CAOxB,2BAAsC,EAAa,CACjD,GAAI,GAAU,KACZ,OAAO,EAGT,GAAI,OAAO,SAAS,EAAM,CACxB,OAAO,IAAI,WAAW,EAAM,CAG9B,GAAI,MAAM,QAAQ,EAAM,CACtB,OAAO,EAAM,IAAK,GAAS,KAAK,2BAA2B,EAAK,CAAC,CAGnE,GAAI,OAAO,GAAU,SAAU,CAC7B,IAAM,EAAkC,EAAE,CAC1C,IAAK,GAAM,CAAC,EAAK,KAAQ,OAAO,QAAQ,EAAM,CAC5C,EAAO,GAAO,KAAK,2BAA2B,EAAI,CAEpD,OAAO,EAGT,OAAO,EAGT,iBAAgC,CAC9B,GAAI,KAAK,SAAW,CAAC,KAAK,GAAG,KAC3B,MAAU,MAAM,gCAAgC,CAIpD,MAAM,MAAuC,EAAa,EAAqC,CAC7F,KAAK,iBAAiB,CACtB,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAClC,OAAO,KAAK,2BAA2B,EAAK,CAG9C,MAAM,SAA0C,EAAa,EAAgD,CAC3G,KAAK,iBAAiB,CACtB,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAC5B,EAAU,EAAK,SAAS,CAAC,IAAK,GAAQ,EAAI,KAAK,CAErD,MAAO,CACL,KAAM,KAAK,2BAA2B,EAAK,CAC3C,UACA,aAAc,EACd,gBAAiB,EAClB,CAGH,MAAM,IAAI,EAAa,EAA2C,CAChE,KAAK,iBAAiB,CACtB,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAElC,MAAO,CACL,aAAc,EAAK,QACnB,gBAAiB,EAAK,gBACvB,CAGH,MAAM,KAAK,EAA4B,CACrC,KAAK,iBAAiB,CACtB,KAAK,GAAG,KAAK,EAAI,CAGnB,YAAe,EAA4D,CACzE,KAAK,iBAAiB,CAEtB,KAAK,GAAG,KAAK,QAAQ,CACrB,IAAM,EAAqB,IAAI,EAAuB,KAAK,GAAG,CAC9D,OAAQ,SAAY,CAClB,GAAI,CACF,IAAM,EAAS,MAAM,EAAG,EAAmB,CAE3C,OADA,KAAK,GAAG,KAAK,SAAS,CACf,QACA,EAAO,CAEd,MADA,KAAK,GAAG,KAAK,WAAW,CAClB,MAEN,CAWN,MAAM,iBAAoB,EAA4C,EAA8C,CAClH,KAAK,iBAAiB,CAEtB,IAAM,EAAY,GAAS,WAAa,IACpC,EAAuB,GAG3B,GAAI,CACF,KAAK,GAAG,KAAK,kBAAkB,OACxB,EAAG,CACV,GAAK,EAAY,QAAQ,SAAS,kDAAkD,CAClF,EAAuB,QAEvB,MAAM,EAIV,IAAI,EACA,EAAuB,GAE3B,GAAI,CAEF,IAAM,EAAiB,IAAI,SAAgB,EAAG,IAAW,CACvD,EAAgB,eAAiB,CAC1B,GACH,EAAW,MAAM,mCAAmC,EAAU,IAAI,CAAC,EAEpE,EAAU,EACb,CAEI,EAAqB,IAAI,EAAwB,KAAK,GAAG,CAGzD,EAAS,MAAM,QAAQ,KAAK,CAAC,EAAG,EAAmB,CAAE,EAAe,CAAC,CAW3E,MATA,GAAuB,GACnB,GACF,aAAa,EAAc,CAIxB,GACH,KAAK,GAAG,KAAK,SAAS,CAEjB,QACA,EAAO,CACd,EAAuB,GACnB,GACF,aAAa,EAAc,CAG7B,GAAI,CACF,KAAK,GAAG,KAAK,WAAW,OACjB,EAAe,CACtB,EAAO,MAAM,mBAAoB,EAAc,CAGjD,MAAM,GAIV,mBAAoC,CAClC,MAAO,SAGT,MAAM,OAAuB,CACvB,CAAC,KAAK,SAAW,KAAK,GAAG,OAC3B,KAAK,GAAG,OAAO,CACf,KAAK,QAAU,IAInB,IAAI,QAAkB,CACpB,MAAO,CAAC,KAAK,SAAW,KAAK,GAAG,OAIrB,EAAf,KAAoE,CAGlE,YAAY,EAAiC,CAAvB,KAAA,GAAA,qBAFU,GAIhC,gBAAwB,EAAkC,CAGxD,OAFK,EACD,MAAM,QAAQ,EAAO,CAAS,EAC3B,OAAO,OAAO,EAAO,CAFR,EAAE,CAKxB,2BAAsC,EAAa,CACjD,GAAI,GAAU,KACZ,OAAO,EAGT,GAAI,OAAO,SAAS,EAAM,CACxB,OAAO,IAAI,WAAW,EAAM,CAG9B,GAAI,MAAM,QAAQ,EAAM,CACtB,OAAO,EAAM,IAAK,GAAS,KAAK,2BAA2B,EAAK,CAAC,CAGnE,GAAI,OAAO,GAAU,SAAU,CAC7B,IAAM,EAAkC,EAAE,CAC1C,IAAK,GAAM,CAAC,EAAK,KAAQ,OAAO,QAAQ,EAAM,CAC5C,EAAO,GAAO,KAAK,2BAA2B,EAAI,CAEpD,OAAO,EAGT,OAAO,EAIT,MAAuC,EAAa,EAAqC,CACvF,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAC5B,EAAS,KAAK,2BAA2B,EAAK,CACpD,OAAO,QAAQ,QAAQ,EAAO,CAGhC,SAA0C,EAAa,EAAgD,CACrG,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAC5B,EAAU,EAAK,SAAS,CAAC,IAAK,GAAQ,EAAI,KAAK,CAE/C,EAAS,CACb,KAAM,KAAK,2BAA2B,EAAK,CAC3C,UACA,aAAc,EACd,gBAAiB,EAClB,CACD,OAAO,QAAQ,QAAQ,EAAO,CAGhC,IAAI,EAAa,EAA2C,CAC1D,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAE5B,EAAS,CACb,aAAc,EAAK,QACnB,gBAAiB,EAAK,gBACvB,CACD,OAAO,QAAQ,QAAQ,EAAO,CAGhC,KAAK,EAA4B,CAE/B,OADA,KAAK,GAAG,KAAK,EAAI,CACV,QAAQ,SAAS,CAI1B,UAA2C,EAAa,EAA4B,CAClF,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAClC,OAAO,KAAK,2BAA2B,EAAK,CAG9C,QAAQ,EAAa,EAAkC,CACrD,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAElC,MAAO,CACL,aAAc,EAAK,QACnB,gBAAiB,EAAK,gBACvB,CAGH,SAAS,EAAmB,CAC1B,KAAK,GAAG,KAAK,EAAI,CAUnB,MAAM,OAAuB,IAKzB,EAAN,cAAqC,CAAuB,CAC1D,mBAAoC,CAClC,MAAO,UAGT,MAAM,YAAe,EAA4D,CAG/E,OAAO,MAAM,EAAG,KAAK,CAGvB,MAAM,iBACJ,EACA,EACY,CAEZ,MAAU,MACR,qHACD,GAIC,EAAN,cAAsC,CAAuB,CAC3D,mBAAoC,CAClC,MAAO,WAGT,MAAM,YAAe,EAA4D,CAG/E,OAAO,MAAM,EAAG,KAAK,CAGvB,MAAM,iBAAoB,EAA4C,EAA+C,CAGnH,OAAO,MAAM,EAAG,KAAK"} | ||
| {"version":3,"file":"node2.cjs","names":["BaseAdapter","Database"],"sources":["../src/adapters/logger.ts","../src/adapters/node.ts"],"sourcesContent":["/**\n * Debug Logger for UniSQLite\n *\n * Uses the `debug` package for conditional logging.\n * Enable logs by setting DEBUG env var:\n *\n * - `unisqlite:*` - Enable all unisqlite logs\n * - `unisqlite:node` - Node.js adapter logs only\n *\n * In Node.js:\n * DEBUG=unisqlite:* node app.js\n */\n\nimport debug from \"debug\";\n\nconst NAMESPACE = \"unisqlite\";\n\n// Package version - will be output on initialization\nexport const VERSION = \"0.4.0\";\n\n/**\n * Logger interface with log, warn, and error methods\n */\nexport interface Logger {\n log: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n}\n\n/**\n * Create a namespaced debug logger with log/warn/error methods\n */\nfunction createModuleLogger(name: string): Logger {\n const logDebug = debug(`${NAMESPACE}:${name}`);\n const warnDebug = debug(`${NAMESPACE}:${name}:warn`);\n const errorDebug = debug(`${NAMESPACE}:${name}:error`);\n\n // Enable error logs by default (they go to stderr)\n errorDebug.enabled = true;\n\n return {\n log: logDebug as (...args: unknown[]) => void,\n warn: warnDebug as (...args: unknown[]) => void,\n error: errorDebug as (...args: unknown[]) => void,\n };\n}\n\n// Pre-created logger for node adapter\nexport const nodeLogger = createModuleLogger(\"node\");\n\n/**\n * Create a custom debug logger\n */\nexport function createDebugLogger(name: string): Logger {\n return createModuleLogger(name);\n}\n\n/**\n * Enable debug logging programmatically\n * This is useful for enabling logs without setting localStorage/env\n *\n * @param namespaces - Debug namespaces to enable (default: \"unisqlite:*\")\n *\n * @example\n * // Enable all unisqlite logs\n * enableDebug(\"unisqlite:*\");\n *\n * // Enable only node adapter logs\n * enableDebug(\"unisqlite:node\");\n */\nexport function enableDebug(namespaces: string = \"unisqlite:*\"): void {\n debug.enable(namespaces);\n}\n\n/**\n * Disable all debug logging\n */\nexport function disableDebug(): void {\n debug.disable();\n}\n","import { BaseAdapter } from \"./base.js\";\nimport type {\n QueryResult,\n RunResult,\n SQLiteParams,\n SQLiteValue,\n UniStoreConnection,\n UniStoreOptions,\n ConnectionType,\n} from \"../types.js\";\nimport Database from \"better-sqlite3\";\nimport { nodeLogger as logger, VERSION } from \"./logger.js\";\n\nexport class NodeAdapter extends BaseAdapter {\n private db: Database.Database;\n private _closed: boolean = false;\n\n constructor(options: UniStoreOptions) {\n super(options);\n logger.log(`UniSQLite v${VERSION} - Opening database: ${options.path}`);\n // Open database with WAL mode for better concurrency\n this.db = new Database(options.path, { fileMustExist: false });\n this.db.pragma(\"journal_mode = WAL\");\n this.db.pragma(\"busy_timeout = 5000\"); // 5 second timeout for locks\n logger.log(\"Database opened with WAL mode\");\n }\n\n private normalizeParams(params?: SQLiteParams): unknown[] {\n if (!params) return [];\n if (Array.isArray(params)) return params;\n // Convert object params to array format for better-sqlite3\n // This is a simplified conversion - in practice you'd need to handle named parameters\n return Object.values(params);\n }\n\n private convertBuffersToUint8Array<T>(value: T): T {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (Buffer.isBuffer(value)) {\n return new Uint8Array(value) as T;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => this.convertBuffersToUint8Array(item)) as T;\n }\n\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n result[key] = this.convertBuffersToUint8Array(val);\n }\n return result as T;\n }\n\n return value;\n }\n\n private checkConnection(): void {\n if (this._closed || !this.db.open) {\n throw new Error(\"Database connection is closed\");\n }\n }\n\n async query<T = Record<string, SQLiteValue>>(sql: string, params?: SQLiteParams): Promise<T[]> {\n this.checkConnection();\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const rows = stmt.all(paramsArray) as T[];\n return this.convertBuffersToUint8Array(rows);\n }\n\n async queryRaw<T = Record<string, SQLiteValue>>(sql: string, params?: SQLiteParams): Promise<QueryResult<T>> {\n this.checkConnection();\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const rows = stmt.all(paramsArray) as T[];\n const columns = stmt.columns().map((col) => col.name);\n\n return {\n rows: this.convertBuffersToUint8Array(rows),\n columns,\n rowsAffected: 0,\n lastInsertRowId: 0,\n };\n }\n\n async run(sql: string, params?: SQLiteParams): Promise<RunResult> {\n this.checkConnection();\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const info = stmt.run(paramsArray);\n\n return {\n rowsAffected: info.changes,\n lastInsertRowId: info.lastInsertRowid as number,\n };\n }\n\n async exec(sql: string): Promise<void> {\n this.checkConnection();\n this.db.exec(sql);\n }\n\n transaction<T>(fn: (tx: UniStoreConnection) => Promise<T> | T): Promise<T> {\n this.checkConnection();\n // better-sqlite3 supports synchronous transactions\n this.db.exec(\"BEGIN\");\n const transactionAdapter = new SyncTransactionAdapter(this.db);\n return (async () => {\n try {\n const result = await fn(transactionAdapter);\n this.db.exec(\"COMMIT\");\n return result;\n } catch (error) {\n this.db.exec(\"ROLLBACK\");\n throw error;\n }\n })();\n }\n\n /**\n * Execute an async transaction with manual transaction management.\n * This allows async operations within the transaction but comes with timeout support\n * to prevent long-running transactions from blocking the database.\n *\n * @param fn Transaction function that can contain async operations\n * @param options Transaction options including timeout (default: 30000ms)\n */\n async asyncTransaction<T>(fn: (tx: UniStoreConnection) => Promise<T>, options?: { timeoutMs?: number }): Promise<T> {\n this.checkConnection();\n\n const timeoutMs = options?.timeoutMs ?? 30000; // Default 30 seconds\n let alreadyInTransaction = false;\n\n // Manual transaction management using BEGIN/COMMIT/ROLLBACK\n try {\n this.db.exec(\"BEGIN IMMEDIATE\");\n } catch (e) {\n if ((e as Error).message.includes(\"cannot start a transaction within a transaction\")) {\n alreadyInTransaction = true;\n } else {\n throw e;\n }\n }\n\n let timeoutHandle: NodeJS.Timeout | undefined;\n let transactionCompleted = false;\n\n try {\n // Set up timeout\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n if (!transactionCompleted) {\n reject(new Error(`Async transaction timeout after ${timeoutMs}ms`));\n }\n }, timeoutMs);\n });\n\n const transactionAdapter = new AsyncTransactionAdapter(this.db);\n\n // Race between transaction execution and timeout\n const result = await Promise.race([fn(transactionAdapter), timeoutPromise]);\n\n transactionCompleted = true;\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n\n // If we get here, the transaction completed successfully\n if (!alreadyInTransaction) {\n this.db.exec(\"COMMIT\");\n }\n return result;\n } catch (error) {\n transactionCompleted = true;\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n\n try {\n this.db.exec(\"ROLLBACK\");\n } catch (rollbackError) {\n logger.error(\"Rollback failed:\", rollbackError);\n }\n\n throw error;\n }\n }\n\n getConnectionType(): ConnectionType {\n return \"direct\";\n }\n\n async close(): Promise<void> {\n if (!this._closed && this.db.open) {\n this.db.close();\n this._closed = true;\n }\n }\n\n get isOpen(): boolean {\n return !this._closed && this.db.open;\n }\n}\n\nabstract class BaseTransactionAdapter implements UniStoreConnection {\n public readonly inTransaction = true;\n\n constructor(protected db: Database.Database) {}\n\n private normalizeParams(params?: SQLiteParams): unknown[] {\n if (!params) return [];\n if (Array.isArray(params)) return params;\n return Object.values(params);\n }\n\n private convertBuffersToUint8Array<T>(value: T): T {\n if (value === null || value === undefined) {\n return value;\n }\n\n if (Buffer.isBuffer(value)) {\n return new Uint8Array(value) as T;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => this.convertBuffersToUint8Array(item)) as T;\n }\n\n if (typeof value === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n result[key] = this.convertBuffersToUint8Array(val);\n }\n return result as T;\n }\n\n return value;\n }\n\n // Synchronous database methods for use in transactions\n query<T = Record<string, SQLiteValue>>(sql: string, params?: SQLiteParams): Promise<T[]> {\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const rows = stmt.all(paramsArray) as T[];\n const result = this.convertBuffersToUint8Array(rows);\n return Promise.resolve(result);\n }\n\n queryRaw<T = Record<string, SQLiteValue>>(sql: string, params?: SQLiteParams): Promise<QueryResult<T>> {\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const rows = stmt.all(paramsArray) as T[];\n const columns = stmt.columns().map((col) => col.name);\n\n const result = {\n rows: this.convertBuffersToUint8Array(rows),\n columns,\n rowsAffected: 0,\n lastInsertRowId: 0,\n };\n return Promise.resolve(result);\n }\n\n run(sql: string, params?: SQLiteParams): Promise<RunResult> {\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const info = stmt.run(paramsArray);\n\n const result = {\n rowsAffected: info.changes,\n lastInsertRowId: info.lastInsertRowid as number,\n };\n return Promise.resolve(result);\n }\n\n exec(sql: string): Promise<void> {\n this.db.exec(sql);\n return Promise.resolve();\n }\n\n // Synchronous versions for use in sync transactions\n querySync<T = Record<string, SQLiteValue>>(sql: string, params?: SQLiteParams): T[] {\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const rows = stmt.all(paramsArray) as T[];\n return this.convertBuffersToUint8Array(rows);\n }\n\n runSync(sql: string, params?: SQLiteParams): RunResult {\n const stmt = this.db.prepare(sql);\n const paramsArray = this.normalizeParams(params);\n const info = stmt.run(paramsArray);\n\n return {\n rowsAffected: info.changes,\n lastInsertRowId: info.lastInsertRowid as number,\n };\n }\n\n execSync(sql: string): void {\n this.db.exec(sql);\n }\n\n abstract getConnectionType(): ConnectionType;\n abstract transaction<T>(fn: (tx: UniStoreConnection) => Promise<T> | T): Promise<T>;\n abstract asyncTransaction<T>(\n fn: (tx: UniStoreConnection) => Promise<T>,\n options?: { timeoutMs?: number }\n ): Promise<T>;\n\n async close(): Promise<void> {\n // No-op for transaction\n }\n}\n\nclass SyncTransactionAdapter extends BaseTransactionAdapter {\n getConnectionType(): ConnectionType {\n return \"syncTxn\";\n }\n\n async transaction<T>(fn: (tx: UniStoreConnection) => Promise<T> | T): Promise<T> {\n // For syncTxn, we can execute the transaction using itself as the connection\n // This allows nested operations within the same transaction\n return await fn(this);\n }\n\n async asyncTransaction<T>(\n _fn: (tx: UniStoreConnection) => Promise<T>,\n _options?: { timeoutMs?: number }\n ): Promise<T> {\n // syncTxn connections cannot run asyncTransaction\n throw new Error(\n \"asyncTransaction is not supported in syncTxn connections. Use transaction() instead or create a direct connection.\"\n );\n }\n}\n\nclass AsyncTransactionAdapter extends BaseTransactionAdapter {\n getConnectionType(): ConnectionType {\n return \"asyncTxn\";\n }\n\n async transaction<T>(fn: (tx: UniStoreConnection) => Promise<T> | T): Promise<T> {\n // For asyncTxn, we can execute the transaction using itself as the connection\n // This allows nested operations within the same transaction\n return await fn(this);\n }\n\n async asyncTransaction<T>(fn: (tx: UniStoreConnection) => Promise<T>, _options?: { timeoutMs?: number }): Promise<T> {\n // For asyncTxn, we can execute asyncTransaction using itself as the connection\n // This allows nested async operations within the same transaction\n return await fn(this);\n }\n}\n"],"mappings":"ikBAeA,MAAM,EAAY,YAGL,EAAU,QAcvB,SAAS,EAAmB,EAAsB,CAChD,IAAM,GAAA,EAAA,EAAA,SAAiB,GAAG,EAAU,GAAG,IAAO,CACxC,GAAA,EAAA,EAAA,SAAkB,GAAG,EAAU,GAAG,EAAK,OAAO,CAC9C,GAAA,EAAA,EAAA,SAAmB,GAAG,EAAU,GAAG,EAAK,QAAQ,CAKtD,MAFA,GAAW,QAAU,GAEd,CACL,IAAK,EACL,KAAM,EACN,MAAO,EACR,CAIH,MAAa,EAAa,EAAmB,OAAO,CCnCpD,IAAa,EAAb,cAAiCA,EAAAA,CAAY,CAI3C,YAAY,EAA0B,CACpC,MAAM,EAAQ,cAHW,GAIzB,EAAO,IAAI,wCAA6C,EAAQ,OAAO,CAEvE,KAAK,GAAK,IAAIC,EAAAA,QAAS,EAAQ,KAAM,CAAE,cAAe,GAAO,CAAC,CAC9D,KAAK,GAAG,OAAO,qBAAqB,CACpC,KAAK,GAAG,OAAO,sBAAsB,CACrC,EAAO,IAAI,gCAAgC,CAG7C,gBAAwB,EAAkC,CAKxD,OAJK,EACD,MAAM,QAAQ,EAAO,CAAS,EAG3B,OAAO,OAAO,EAAO,CAJR,EAAE,CAOxB,2BAAsC,EAAa,CACjD,GAAI,GAAU,KACZ,OAAO,EAGT,GAAI,OAAO,SAAS,EAAM,CACxB,OAAO,IAAI,WAAW,EAAM,CAG9B,GAAI,MAAM,QAAQ,EAAM,CACtB,OAAO,EAAM,IAAK,GAAS,KAAK,2BAA2B,EAAK,CAAC,CAGnE,GAAI,OAAO,GAAU,SAAU,CAC7B,IAAM,EAAkC,EAAE,CAC1C,IAAK,GAAM,CAAC,EAAK,KAAQ,OAAO,QAAQ,EAAM,CAC5C,EAAO,GAAO,KAAK,2BAA2B,EAAI,CAEpD,OAAO,EAGT,OAAO,EAGT,iBAAgC,CAC9B,GAAI,KAAK,SAAW,CAAC,KAAK,GAAG,KAC3B,MAAU,MAAM,gCAAgC,CAIpD,MAAM,MAAuC,EAAa,EAAqC,CAC7F,KAAK,iBAAiB,CACtB,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAClC,OAAO,KAAK,2BAA2B,EAAK,CAG9C,MAAM,SAA0C,EAAa,EAAgD,CAC3G,KAAK,iBAAiB,CACtB,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAC5B,EAAU,EAAK,SAAS,CAAC,IAAK,GAAQ,EAAI,KAAK,CAErD,MAAO,CACL,KAAM,KAAK,2BAA2B,EAAK,CAC3C,UACA,aAAc,EACd,gBAAiB,EAClB,CAGH,MAAM,IAAI,EAAa,EAA2C,CAChE,KAAK,iBAAiB,CACtB,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAElC,MAAO,CACL,aAAc,EAAK,QACnB,gBAAiB,EAAK,gBACvB,CAGH,MAAM,KAAK,EAA4B,CACrC,KAAK,iBAAiB,CACtB,KAAK,GAAG,KAAK,EAAI,CAGnB,YAAe,EAA4D,CACzE,KAAK,iBAAiB,CAEtB,KAAK,GAAG,KAAK,QAAQ,CACrB,IAAM,EAAqB,IAAI,EAAuB,KAAK,GAAG,CAC9D,OAAQ,SAAY,CAClB,GAAI,CACF,IAAM,EAAS,MAAM,EAAG,EAAmB,CAE3C,OADA,KAAK,GAAG,KAAK,SAAS,CACf,QACA,EAAO,CAEd,MADA,KAAK,GAAG,KAAK,WAAW,CAClB,MAEN,CAWN,MAAM,iBAAoB,EAA4C,EAA8C,CAClH,KAAK,iBAAiB,CAEtB,IAAM,EAAY,GAAS,WAAa,IACpC,EAAuB,GAG3B,GAAI,CACF,KAAK,GAAG,KAAK,kBAAkB,OACxB,EAAG,CACV,GAAK,EAAY,QAAQ,SAAS,kDAAkD,CAClF,EAAuB,QAEvB,MAAM,EAIV,IAAI,EACA,EAAuB,GAE3B,GAAI,CAEF,IAAM,EAAiB,IAAI,SAAgB,EAAG,IAAW,CACvD,EAAgB,eAAiB,CAC1B,GACH,EAAW,MAAM,mCAAmC,EAAU,IAAI,CAAC,EAEpE,EAAU,EACb,CAEI,EAAqB,IAAI,EAAwB,KAAK,GAAG,CAGzD,EAAS,MAAM,QAAQ,KAAK,CAAC,EAAG,EAAmB,CAAE,EAAe,CAAC,CAW3E,MATA,GAAuB,GACnB,GACF,aAAa,EAAc,CAIxB,GACH,KAAK,GAAG,KAAK,SAAS,CAEjB,QACA,EAAO,CACd,EAAuB,GACnB,GACF,aAAa,EAAc,CAG7B,GAAI,CACF,KAAK,GAAG,KAAK,WAAW,OACjB,EAAe,CACtB,EAAO,MAAM,mBAAoB,EAAc,CAGjD,MAAM,GAIV,mBAAoC,CAClC,MAAO,SAGT,MAAM,OAAuB,CACvB,CAAC,KAAK,SAAW,KAAK,GAAG,OAC3B,KAAK,GAAG,OAAO,CACf,KAAK,QAAU,IAInB,IAAI,QAAkB,CACpB,MAAO,CAAC,KAAK,SAAW,KAAK,GAAG,OAIrB,EAAf,KAAoE,CAGlE,YAAY,EAAiC,CAAvB,KAAA,GAAA,qBAFU,GAIhC,gBAAwB,EAAkC,CAGxD,OAFK,EACD,MAAM,QAAQ,EAAO,CAAS,EAC3B,OAAO,OAAO,EAAO,CAFR,EAAE,CAKxB,2BAAsC,EAAa,CACjD,GAAI,GAAU,KACZ,OAAO,EAGT,GAAI,OAAO,SAAS,EAAM,CACxB,OAAO,IAAI,WAAW,EAAM,CAG9B,GAAI,MAAM,QAAQ,EAAM,CACtB,OAAO,EAAM,IAAK,GAAS,KAAK,2BAA2B,EAAK,CAAC,CAGnE,GAAI,OAAO,GAAU,SAAU,CAC7B,IAAM,EAAkC,EAAE,CAC1C,IAAK,GAAM,CAAC,EAAK,KAAQ,OAAO,QAAQ,EAAM,CAC5C,EAAO,GAAO,KAAK,2BAA2B,EAAI,CAEpD,OAAO,EAGT,OAAO,EAIT,MAAuC,EAAa,EAAqC,CACvF,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAC5B,EAAS,KAAK,2BAA2B,EAAK,CACpD,OAAO,QAAQ,QAAQ,EAAO,CAGhC,SAA0C,EAAa,EAAgD,CACrG,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAC5B,EAAU,EAAK,SAAS,CAAC,IAAK,GAAQ,EAAI,KAAK,CAE/C,EAAS,CACb,KAAM,KAAK,2BAA2B,EAAK,CAC3C,UACA,aAAc,EACd,gBAAiB,EAClB,CACD,OAAO,QAAQ,QAAQ,EAAO,CAGhC,IAAI,EAAa,EAA2C,CAC1D,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAE5B,EAAS,CACb,aAAc,EAAK,QACnB,gBAAiB,EAAK,gBACvB,CACD,OAAO,QAAQ,QAAQ,EAAO,CAGhC,KAAK,EAA4B,CAE/B,OADA,KAAK,GAAG,KAAK,EAAI,CACV,QAAQ,SAAS,CAI1B,UAA2C,EAAa,EAA4B,CAClF,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAClC,OAAO,KAAK,2BAA2B,EAAK,CAG9C,QAAQ,EAAa,EAAkC,CACrD,IAAM,EAAO,KAAK,GAAG,QAAQ,EAAI,CAC3B,EAAc,KAAK,gBAAgB,EAAO,CAC1C,EAAO,EAAK,IAAI,EAAY,CAElC,MAAO,CACL,aAAc,EAAK,QACnB,gBAAiB,EAAK,gBACvB,CAGH,SAAS,EAAmB,CAC1B,KAAK,GAAG,KAAK,EAAI,CAUnB,MAAM,OAAuB,IAKzB,EAAN,cAAqC,CAAuB,CAC1D,mBAAoC,CAClC,MAAO,UAGT,MAAM,YAAe,EAA4D,CAG/E,OAAO,MAAM,EAAG,KAAK,CAGvB,MAAM,iBACJ,EACA,EACY,CAEZ,MAAU,MACR,qHACD,GAIC,EAAN,cAAsC,CAAuB,CAC3D,mBAAoC,CAClC,MAAO,WAGT,MAAM,YAAe,EAA4D,CAG/E,OAAO,MAAM,EAAG,KAAK,CAGvB,MAAM,iBAAoB,EAA4C,EAA+C,CAGnH,OAAO,MAAM,EAAG,KAAK"} |
+1
-1
@@ -1,2 +0,2 @@ | ||
| import{t as e}from"./base.mjs";import t from"debug";import n from"better-sqlite3";const r=`unisqlite`;function i(e){let n=t(`${r}:${e}`),i=t(`${r}:${e}:warn`),a=t(`${r}:${e}:error`);return a.enabled=!0,{log:n,warn:i,error:a}}const a=i(`node`);var o=class extends e{constructor(e){super(e),this._closed=!1,a.log(`UniSQLite v0.4.0 - Opening database: ${e.path}`),this.db=new n(e.path,{fileMustExist:!1}),this.db.pragma(`journal_mode = WAL`),this.db.pragma(`busy_timeout = 5000`),a.log(`Database opened with WAL mode`)}normalizeParams(e){return e?Array.isArray(e)?e:Object.values(e):[]}convertBuffersToUint8Array(e){if(e==null)return e;if(Buffer.isBuffer(e))return new Uint8Array(e);if(Array.isArray(e))return e.map(e=>this.convertBuffersToUint8Array(e));if(typeof e==`object`){let t={};for(let[n,r]of Object.entries(e))t[n]=this.convertBuffersToUint8Array(r);return t}return e}checkConnection(){if(this._closed||!this.db.open)throw Error(`Database connection is closed`)}async query(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r);return this.convertBuffersToUint8Array(i)}async queryRaw(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=n.columns().map(e=>e.name);return{rows:this.convertBuffersToUint8Array(i),columns:a,rowsAffected:0,lastInsertRowId:0}}async run(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r);return{rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid}}async exec(e){this.checkConnection(),this.db.exec(e)}transaction(e){this.checkConnection(),this.db.exec(`BEGIN`);let t=new c(this.db);return(async()=>{try{let n=await e(t);return this.db.exec(`COMMIT`),n}catch(e){throw this.db.exec(`ROLLBACK`),e}})()}async asyncTransaction(e,t){this.checkConnection();let n=t?.timeoutMs??3e4,r=!1;try{this.db.exec(`BEGIN IMMEDIATE`)}catch(e){if(e.message.includes(`cannot start a transaction within a transaction`))r=!0;else throw e}let i,o=!1;try{let t=new Promise((e,t)=>{i=setTimeout(()=>{o||t(Error(`Async transaction timeout after ${n}ms`))},n)}),a=new l(this.db),s=await Promise.race([e(a),t]);return o=!0,i&&clearTimeout(i),r||this.db.exec(`COMMIT`),s}catch(e){o=!0,i&&clearTimeout(i);try{this.db.exec(`ROLLBACK`)}catch(e){a.error(`Rollback failed:`,e)}throw e}}getConnectionType(){return`direct`}async close(){!this._closed&&this.db.open&&(this.db.close(),this._closed=!0)}get isOpen(){return!this._closed&&this.db.open}},s=class{constructor(e){this.db=e,this.inTransaction=!0}normalizeParams(e){return e?Array.isArray(e)?e:Object.values(e):[]}convertBuffersToUint8Array(e){if(e==null)return e;if(Buffer.isBuffer(e))return new Uint8Array(e);if(Array.isArray(e))return e.map(e=>this.convertBuffersToUint8Array(e));if(typeof e==`object`){let t={};for(let[n,r]of Object.entries(e))t[n]=this.convertBuffersToUint8Array(r);return t}return e}query(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=this.convertBuffersToUint8Array(i);return Promise.resolve(a)}queryRaw(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=n.columns().map(e=>e.name),o={rows:this.convertBuffersToUint8Array(i),columns:a,rowsAffected:0,lastInsertRowId:0};return Promise.resolve(o)}run(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r),a={rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid};return Promise.resolve(a)}exec(e){return this.db.exec(e),Promise.resolve()}querySync(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r);return this.convertBuffersToUint8Array(i)}runSync(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r);return{rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid}}execSync(e){this.db.exec(e)}async close(){}},c=class extends s{getConnectionType(){return`syncTxn`}async transaction(e){return await e(this)}async asyncTransaction(e,t){throw Error(`asyncTransaction is not supported in syncTxn connections. Use transaction() instead or create a direct connection.`)}},l=class extends s{getConnectionType(){return`asyncTxn`}async transaction(e){return await e(this)}async asyncTransaction(e,t){return await e(this)}};export{o as t}; | ||
| import{t as e}from"./base.mjs";import t from"better-sqlite3";import n from"debug";const r=`unisqlite`;function i(e){let t=n(`${r}:${e}`),i=n(`${r}:${e}:warn`),a=n(`${r}:${e}:error`);return a.enabled=!0,{log:t,warn:i,error:a}}const a=i(`node`);var o=class extends e{constructor(e){super(e),this._closed=!1,a.log(`UniSQLite v0.4.0 - Opening database: ${e.path}`),this.db=new t(e.path,{fileMustExist:!1}),this.db.pragma(`journal_mode = WAL`),this.db.pragma(`busy_timeout = 5000`),a.log(`Database opened with WAL mode`)}normalizeParams(e){return e?Array.isArray(e)?e:Object.values(e):[]}convertBuffersToUint8Array(e){if(e==null)return e;if(Buffer.isBuffer(e))return new Uint8Array(e);if(Array.isArray(e))return e.map(e=>this.convertBuffersToUint8Array(e));if(typeof e==`object`){let t={};for(let[n,r]of Object.entries(e))t[n]=this.convertBuffersToUint8Array(r);return t}return e}checkConnection(){if(this._closed||!this.db.open)throw Error(`Database connection is closed`)}async query(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r);return this.convertBuffersToUint8Array(i)}async queryRaw(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=n.columns().map(e=>e.name);return{rows:this.convertBuffersToUint8Array(i),columns:a,rowsAffected:0,lastInsertRowId:0}}async run(e,t){this.checkConnection();let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r);return{rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid}}async exec(e){this.checkConnection(),this.db.exec(e)}transaction(e){this.checkConnection(),this.db.exec(`BEGIN`);let t=new c(this.db);return(async()=>{try{let n=await e(t);return this.db.exec(`COMMIT`),n}catch(e){throw this.db.exec(`ROLLBACK`),e}})()}async asyncTransaction(e,t){this.checkConnection();let n=t?.timeoutMs??3e4,r=!1;try{this.db.exec(`BEGIN IMMEDIATE`)}catch(e){if(e.message.includes(`cannot start a transaction within a transaction`))r=!0;else throw e}let i,o=!1;try{let t=new Promise((e,t)=>{i=setTimeout(()=>{o||t(Error(`Async transaction timeout after ${n}ms`))},n)}),a=new l(this.db),s=await Promise.race([e(a),t]);return o=!0,i&&clearTimeout(i),r||this.db.exec(`COMMIT`),s}catch(e){o=!0,i&&clearTimeout(i);try{this.db.exec(`ROLLBACK`)}catch(e){a.error(`Rollback failed:`,e)}throw e}}getConnectionType(){return`direct`}async close(){!this._closed&&this.db.open&&(this.db.close(),this._closed=!0)}get isOpen(){return!this._closed&&this.db.open}},s=class{constructor(e){this.db=e,this.inTransaction=!0}normalizeParams(e){return e?Array.isArray(e)?e:Object.values(e):[]}convertBuffersToUint8Array(e){if(e==null)return e;if(Buffer.isBuffer(e))return new Uint8Array(e);if(Array.isArray(e))return e.map(e=>this.convertBuffersToUint8Array(e));if(typeof e==`object`){let t={};for(let[n,r]of Object.entries(e))t[n]=this.convertBuffersToUint8Array(r);return t}return e}query(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=this.convertBuffersToUint8Array(i);return Promise.resolve(a)}queryRaw(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r),a=n.columns().map(e=>e.name),o={rows:this.convertBuffersToUint8Array(i),columns:a,rowsAffected:0,lastInsertRowId:0};return Promise.resolve(o)}run(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r),a={rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid};return Promise.resolve(a)}exec(e){return this.db.exec(e),Promise.resolve()}querySync(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.all(r);return this.convertBuffersToUint8Array(i)}runSync(e,t){let n=this.db.prepare(e),r=this.normalizeParams(t),i=n.run(r);return{rowsAffected:i.changes,lastInsertRowId:i.lastInsertRowid}}execSync(e){this.db.exec(e)}async close(){}},c=class extends s{getConnectionType(){return`syncTxn`}async transaction(e){return await e(this)}async asyncTransaction(e,t){throw Error(`asyncTransaction is not supported in syncTxn connections. Use transaction() instead or create a direct connection.`)}},l=class extends s{getConnectionType(){return`asyncTxn`}async transaction(e){return await e(this)}async asyncTransaction(e,t){return await e(this)}};export{o as t}; | ||
| //# sourceMappingURL=node2.mjs.map |
@@ -1,2 +0,2 @@ | ||
| import { s as SQLiteValue } from "./types.mjs"; | ||
| import { l as SQLiteValue } from "./types.mjs"; | ||
| import { Database as NodeSQLiteDatabase, Statement as NodeSQLiteStatement } from "better-sqlite3"; | ||
@@ -3,0 +3,0 @@ |
@@ -1,2 +0,2 @@ | ||
| import { s as SQLiteValue } from "./types.js"; | ||
| import { l as SQLiteValue } from "./types.js"; | ||
| import { Database as NodeSQLiteDatabase, Statement as NodeSQLiteStatement } from "better-sqlite3"; | ||
@@ -3,0 +3,0 @@ |
+22
-1
@@ -5,2 +5,4 @@ //#region src/types.d.ts | ||
| type ConnectionType = "direct" | "syncTxn" | "asyncTxn"; | ||
| type ConnectionRole = "host" | "participant" | "unknown"; | ||
| type ConnectionRoleChangeListener = (role: ConnectionRole) => void; | ||
| interface SQLiteWasmConfig { | ||
@@ -198,2 +200,21 @@ /** | ||
| getConnectionType(): ConnectionType; | ||
| /** | ||
| * Connection role for multi-tab browser adapters. | ||
| * | ||
| * - `host`: This tab owns the underlying database instance and can accept writes. | ||
| * - `participant`: This tab forwards requests to the host. | ||
| * - `unknown`: Initializing/closing or host not ready yet. | ||
| * | ||
| * Non-browser adapters typically behave like a stable `host`. | ||
| */ | ||
| getRole?: () => ConnectionRole; | ||
| /** | ||
| * Subscribe to role changes (multi-tab browser adapters). | ||
| * Returns an unsubscribe function. | ||
| */ | ||
| subscribeRoleChange?: (listener: ConnectionRoleChangeListener) => () => void; | ||
| /** | ||
| * Wait until the connection reaches the desired role (multi-tab browser adapters). | ||
| */ | ||
| waitForRole?: (role: ConnectionRole, timeoutMs?: number) => Promise<void>; | ||
| close(): Promise<void>; | ||
@@ -237,3 +258,3 @@ } | ||
| //#endregion | ||
| export { RunResult as a, SQLiteWasmConfig as c, QueryResult as i, UniStoreConnection as l, ConnectionType as n, SQLiteParams as o, DurableObjectStorage as r, SQLiteValue as s, ChangeEvent as t, UniStoreOptions as u }; | ||
| export { DurableObjectStorage as a, SQLiteParams as c, UniStoreConnection as d, UniStoreOptions as f, ConnectionType as i, SQLiteValue as l, ConnectionRole as n, QueryResult as o, ConnectionRoleChangeListener as r, RunResult as s, ChangeEvent as t, SQLiteWasmConfig as u }; | ||
| //# sourceMappingURL=types.d.mts.map |
+22
-1
@@ -5,2 +5,4 @@ //#region src/types.d.ts | ||
| type ConnectionType = "direct" | "syncTxn" | "asyncTxn"; | ||
| type ConnectionRole = "host" | "participant" | "unknown"; | ||
| type ConnectionRoleChangeListener = (role: ConnectionRole) => void; | ||
| interface SQLiteWasmConfig { | ||
@@ -198,2 +200,21 @@ /** | ||
| getConnectionType(): ConnectionType; | ||
| /** | ||
| * Connection role for multi-tab browser adapters. | ||
| * | ||
| * - `host`: This tab owns the underlying database instance and can accept writes. | ||
| * - `participant`: This tab forwards requests to the host. | ||
| * - `unknown`: Initializing/closing or host not ready yet. | ||
| * | ||
| * Non-browser adapters typically behave like a stable `host`. | ||
| */ | ||
| getRole?: () => ConnectionRole; | ||
| /** | ||
| * Subscribe to role changes (multi-tab browser adapters). | ||
| * Returns an unsubscribe function. | ||
| */ | ||
| subscribeRoleChange?: (listener: ConnectionRoleChangeListener) => () => void; | ||
| /** | ||
| * Wait until the connection reaches the desired role (multi-tab browser adapters). | ||
| */ | ||
| waitForRole?: (role: ConnectionRole, timeoutMs?: number) => Promise<void>; | ||
| close(): Promise<void>; | ||
@@ -237,3 +258,3 @@ } | ||
| //#endregion | ||
| export { RunResult as a, SQLiteWasmConfig as c, QueryResult as i, UniStoreConnection as l, ConnectionType as n, SQLiteParams as o, DurableObjectStorage as r, SQLiteValue as s, ChangeEvent as t, UniStoreOptions as u }; | ||
| export { DurableObjectStorage as a, SQLiteParams as c, UniStoreConnection as d, UniStoreOptions as f, ConnectionType as i, SQLiteValue as l, ConnectionRole as n, QueryResult as o, ConnectionRoleChangeListener as r, RunResult as s, ChangeEvent as t, SQLiteWasmConfig as u }; | ||
| //# sourceMappingURL=types.d.ts.map |
+2
-2
| { | ||
| "name": "@loro-dev/unisqlite", | ||
| "type": "module", | ||
| "version": "0.5.0", | ||
| "version": "0.6.0", | ||
| "description": "Cross-platform concurrent SQLite access layer", | ||
@@ -94,3 +94,3 @@ "author": "", | ||
| "check": "pnpm typecheck && pnpm test", | ||
| "pretest:e2e": "pnpm build", | ||
| "pretest:e2e": "pnpm build && pnpm -C ../flock-sqlite build", | ||
| "test:e2e": "playwright test", | ||
@@ -97,0 +97,0 @@ "test:e2e:simple": "playwright test -c playwright-simple.config.ts", |
+58
-3
@@ -12,2 +12,3 @@ # UniSQLite | ||
| - **Leader election**: Browser adapter uses leader election for multi-tab coordination | ||
| - **Role awareness (browser multi-tab)**: Exposes `getRole()` / `subscribeRoleChange()` so apps can avoid redundant host-only work | ||
| - **Cloudflare DO Support**: Native adapter for Cloudflare Durable Objects' SQLite persistence API | ||
@@ -437,8 +438,58 @@ | ||
| - **Node** (requires peer `better-sqlite3`): `import { openNodeStore, NodeAdapter } from "unisqlite/node";` | ||
| - **Browser** (needs `broadcast-channel`, and `@sqlite.org/sqlite-wasm` if you load SQLite from npm): `import { openStore } from "unisqlite/browser";` | ||
| - **Cloudflare Durable Objects**: `import { openCloudflareStore, CloudflareDOAdapter, createCloudflareDOAdapter } from "unisqlite/cloudflare";` | ||
| - **Node** (requires peer `better-sqlite3`): `import { openNodeStore, NodeAdapter } from "@loro-dev/unisqlite/node";` | ||
| - **Browser** (needs `broadcast-channel`, and `@sqlite.org/sqlite-wasm` if you load SQLite from npm): `import { openStore } from "@loro-dev/unisqlite/browser";` | ||
| - **Cloudflare Durable Objects**: `import { openCloudflareStore, CloudflareDOAdapter, createCloudflareDOAdapter } from "@loro-dev/unisqlite/cloudflare";` | ||
| These entrypoints avoid pulling in other platform adapters, keeping browser bundles slim and Node installs free from WASM/downloaded assets. | ||
| ## Multi-tab role awareness (browser) | ||
| In browsers, UniSQLite elects a single writable **host** tab for a given database. SQL calls are already transparently forwarded through RPC, but applications may want to run background tasks (sync, persistence/flush, etc.) only on the host tab. | ||
| The browser adapter exposes a lightweight, capability-based role API: | ||
| - `getRole(): "host" | "participant" | "unknown"` | ||
| - `subscribeRoleChange(listener): () => void` | ||
| - `waitForRole(role, timeoutMs?): Promise<void>` | ||
| These methods are **optional** on `UniStoreConnection` for compatibility with other adapters/older versions, so feature-detect them: | ||
| ```ts | ||
| import { openStore } from "@loro-dev/unisqlite/browser"; | ||
| const db = await openStore({ path: "my.db" }); | ||
| if (db.getRole?.() === "host") { | ||
| // Start background sync/persistence only on the host tab. | ||
| } | ||
| const unsubscribe = db.subscribeRoleChange?.((role) => { | ||
| if (role === "host") { | ||
| // Became host. | ||
| } else if (role === "participant") { | ||
| // Lost host. | ||
| } else { | ||
| // "unknown": initializing/closing; treat conservatively. | ||
| } | ||
| }); | ||
| ``` | ||
| Role semantics: | ||
| - `host` means **host-ready** (safe to run host-only work) | ||
| - `participant` means initialized but not host | ||
| - `unknown` is a transitional state during initialization/close; treat as `participant` | ||
| ### Debug logging (browser) | ||
| UniSQLite browser logs are gated by `localStorage.debug` (errors always log). Examples: | ||
| ```ts | ||
| // All UniSQLite logs | ||
| localStorage.debug = "unisqlite:*"; | ||
| // Only adapter + host election logs | ||
| localStorage.debug = "unisqlite:adapter,unisqlite:host"; | ||
| ``` | ||
| ## API Reference | ||
@@ -451,2 +502,6 @@ | ||
| getConnectionType(): "direct" | "syncTxn" | "asyncTxn"; | ||
| // Optional (browser multi-tab only) | ||
| getRole?: () => "host" | "participant" | "unknown"; | ||
| subscribeRoleChange?: (listener: (role: "host" | "participant" | "unknown") => void) => () => void; | ||
| waitForRole?: (role: "host" | "participant" | "unknown", timeoutMs?: number) => Promise<void>; | ||
| // ... other methods | ||
@@ -453,0 +508,0 @@ } |
@@ -1,2 +0,2 @@ | ||
| import { describe, it, expect, vi } from "vitest"; | ||
| import { describe, it, expect } from "vitest"; | ||
| import { BrowserAdapter } from "./browser/index.js"; | ||
@@ -3,0 +3,0 @@ import { HostElection } from "./browser/host-election.js"; |
@@ -21,2 +21,4 @@ /** | ||
| ConnectionType, | ||
| ConnectionRole, | ||
| ConnectionRoleChangeListener, | ||
| } from "../../types.js"; | ||
@@ -129,2 +131,5 @@ | ||
| private readonly roleListeners = new Set<ConnectionRoleChangeListener>(); | ||
| private roleValue: ConnectionRole = "unknown"; | ||
| constructor(options: ExtendedUniStoreOptions) { | ||
@@ -194,2 +199,51 @@ const path = options.path ?? (options as { name?: string }).name ?? "default"; | ||
| getRole(): ConnectionRole { | ||
| return this.roleValue; | ||
| } | ||
| subscribeRoleChange(listener: ConnectionRoleChangeListener): () => void { | ||
| this.roleListeners.add(listener); | ||
| try { | ||
| listener(this.roleValue); | ||
| } catch (e) { | ||
| logger.error("Role change listener threw:", e); | ||
| } | ||
| return () => { | ||
| this.roleListeners.delete(listener); | ||
| }; | ||
| } | ||
| async waitForRole(role: ConnectionRole, timeoutMs = 30000): Promise<void> { | ||
| if (this.roleValue === role) { | ||
| return; | ||
| } | ||
| await new Promise<void>((resolve, reject) => { | ||
| const listener: ConnectionRoleChangeListener = (next) => { | ||
| if (next !== role) { | ||
| return; | ||
| } | ||
| cleanup(); | ||
| resolve(); | ||
| }; | ||
| const cleanup = () => { | ||
| clearTimeout(timer); | ||
| this.roleListeners.delete(listener); | ||
| }; | ||
| const timer = setTimeout(() => { | ||
| cleanup(); | ||
| reject(new Error(`Timed out waiting for role '${role}' after ${timeoutMs}ms`)); | ||
| }, timeoutMs); | ||
| this.roleListeners.add(listener); | ||
| // Re-check after subscription to avoid races with rapid role changes. | ||
| if (this.roleValue === role) { | ||
| cleanup(); | ||
| resolve(); | ||
| } | ||
| }); | ||
| } | ||
| // =========================================================================== | ||
@@ -206,2 +260,3 @@ // Initialization | ||
| this.initialized = true; | ||
| this.refreshRole(); | ||
| } | ||
@@ -269,2 +324,4 @@ | ||
| } | ||
| this.refreshRole(); | ||
| } | ||
@@ -289,2 +346,4 @@ | ||
| } | ||
| this.refreshRole(); | ||
| } | ||
@@ -488,2 +547,3 @@ | ||
| this.closed = true; | ||
| this.refreshRole(); | ||
@@ -516,2 +576,37 @@ // Unsubscribe from visibility changes | ||
| // =========================================================================== | ||
| // Role Tracking | ||
| // =========================================================================== | ||
| private computeRole(): ConnectionRole { | ||
| if (this.closed) { | ||
| return "unknown"; | ||
| } | ||
| if (this.hostElection.isHost) { | ||
| return this.dbHost ? "host" : "unknown"; | ||
| } | ||
| if (!this.initialized) { | ||
| return "unknown"; | ||
| } | ||
| return "participant"; | ||
| } | ||
| private refreshRole(): void { | ||
| const next = this.computeRole(); | ||
| if (next === this.roleValue) { | ||
| return; | ||
| } | ||
| this.roleValue = next; | ||
| this.roleListeners.forEach((listener) => { | ||
| try { | ||
| listener(next); | ||
| } catch (e) { | ||
| logger.error("Role change listener threw:", e); | ||
| } | ||
| }); | ||
| } | ||
| // =========================================================================== | ||
| // Transaction Implementation | ||
@@ -518,0 +613,0 @@ // =========================================================================== |
@@ -77,2 +77,38 @@ /** | ||
| function extractErrorMessage(error: unknown): string { | ||
| if (error instanceof Error) { | ||
| return error.message; | ||
| } | ||
| if (typeof error === "string") { | ||
| return error; | ||
| } | ||
| if (error && typeof error === "object") { | ||
| const candidate = error as { message?: unknown; error?: unknown; result?: unknown }; | ||
| if (typeof candidate.message === "string") { | ||
| return candidate.message; | ||
| } | ||
| if (candidate.error && typeof candidate.error === "object") { | ||
| const nested = candidate.error as { message?: unknown }; | ||
| if (typeof nested.message === "string") { | ||
| return nested.message; | ||
| } | ||
| } | ||
| if (candidate.result && typeof candidate.result === "object") { | ||
| const nested = candidate.result as { message?: unknown }; | ||
| if (typeof nested.message === "string") { | ||
| return nested.message; | ||
| } | ||
| } | ||
| try { | ||
| const json = JSON.stringify(error); | ||
| if (json && json !== "{}") { | ||
| return json; | ||
| } | ||
| } catch { | ||
| // ignore | ||
| } | ||
| } | ||
| return String(error); | ||
| } | ||
| // ============================================================================= | ||
@@ -420,3 +456,3 @@ // DbWorkerHost Class | ||
| } catch (error: unknown) { | ||
| const errorMessage = error instanceof Error ? error.message : String(error); | ||
| const errorMessage = extractErrorMessage(error); | ||
| const sqlError = new Error(`SQLite error: ${errorMessage}`); | ||
@@ -714,3 +750,8 @@ if (error instanceof Error) { | ||
| if (storageBackend === "localStorage") { | ||
| throw new Error(`localStorage database creation failed: ${e instanceof Error ? e.message : String(e)}`); | ||
| const message = `localStorage database creation failed: ${e instanceof Error ? e.message : String(e)}`; | ||
| const error = new Error(message); | ||
| if (e instanceof Error) { | ||
| error.cause = e; | ||
| } | ||
| throw error; | ||
| } | ||
@@ -1002,6 +1043,18 @@ } | ||
| // Prefer custom workerUrl if provided (for bundler integration) | ||
| const worker = this.createWorkerFromConfig() | ||
| ?? (vfsName === "opfs-sahpool" | ||
| const configuredWorker = this.createWorkerFromConfig(); | ||
| if (configuredWorker) { | ||
| return { worker: configuredWorker }; | ||
| } | ||
| // For loadStrategy="global", avoid implicitly pulling worker code from a CDN. | ||
| // Users are expected to provide `sqlite3Worker1Promiser` globally, and it can | ||
| // use its default worker loading behavior based on how SQLite WASM is hosted. | ||
| if (this.config.loadStrategy === "global") { | ||
| return undefined; | ||
| } | ||
| const worker = | ||
| vfsName === "opfs-sahpool" | ||
| ? this.createWorkerFromCdn({ installSahpool: true }) | ||
| : this.createWorkerFromCdn({ installSahpool: false })); | ||
| : this.createWorkerFromCdn({ installSahpool: false }); | ||
| return worker ? { worker } : undefined; | ||
@@ -1008,0 +1061,0 @@ })() |
| /** | ||
| * Debug Logger for Multi-Tab SQLite | ||
| * Debug Logger for Multi-Tab SQLite (browser) | ||
| * | ||
| * Uses the `debug` package for conditional logging. | ||
| * Enable logs by setting localStorage.debug or DEBUG env var: | ||
| * We intentionally avoid depending on the `debug` package here because it is | ||
| * CommonJS-only and would require bundler interop to work in native browser ESM. | ||
| * | ||
| * Enable logs by setting `localStorage.debug`: | ||
| * - `unisqlite:*` - Enable all unisqlite logs | ||
@@ -12,14 +13,8 @@ * - `unisqlite:host` - Host election logs only | ||
| * - `unisqlite:wasm` - SQLite WASM loading and database creation logs | ||
| * - `unisqlite:node` - Node.js adapter logs | ||
| * - `unisqlite:visibility` - Visibility manager logs | ||
| * | ||
| * In browser console: | ||
| * localStorage.debug = 'unisqlite:*' | ||
| * | ||
| * In Node.js: | ||
| * DEBUG=unisqlite:* node app.js | ||
| * localStorage.debug = "unisqlite:*" | ||
| */ | ||
| import debug from "debug"; | ||
| const NAMESPACE = "unisqlite"; | ||
@@ -30,2 +25,5 @@ | ||
| let enabledPattern: string = ""; | ||
| let enabledRegexes: RegExp[] = []; | ||
| /** | ||
@@ -40,2 +38,59 @@ * Logger interface with log, warn, and error methods | ||
| function safeReadLocalStorage(key: string): string | null { | ||
| try { | ||
| return typeof localStorage !== "undefined" ? localStorage.getItem(key) : null; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
| function safeWriteLocalStorage(key: string, value: string | null): void { | ||
| try { | ||
| if (typeof localStorage === "undefined") { | ||
| return; | ||
| } | ||
| if (value === null) { | ||
| localStorage.removeItem(key); | ||
| return; | ||
| } | ||
| localStorage.setItem(key, value); | ||
| } catch { | ||
| // Ignore storage failures (e.g. blocked third-party context). | ||
| } | ||
| } | ||
| function compileDebugPatterns(pattern: string): RegExp[] { | ||
| const tokens = pattern | ||
| .split(/[\s,]+/g) | ||
| .map((t) => t.trim()) | ||
| .filter(Boolean); | ||
| return tokens.map((token) => { | ||
| const escaped = token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*"); | ||
| return new RegExp(`^${escaped}$`); | ||
| }); | ||
| } | ||
| function isEnabled(namespace: string): boolean { | ||
| return enabledRegexes.some((re) => re.test(namespace)); | ||
| } | ||
| function formatPrefix(namespace: string): string { | ||
| return `[${namespace}]`; | ||
| } | ||
| function createConsoleLogger(namespace: string, level: "log" | "warn" | "error"): (...args: unknown[]) => void { | ||
| if (level === "error") { | ||
| return (...args: unknown[]) => console.error(formatPrefix(namespace), ...args); | ||
| } | ||
| const out = level === "warn" ? console.warn.bind(console) : console.log.bind(console); | ||
| return (...args: unknown[]) => { | ||
| if (!isEnabled(namespace)) { | ||
| return; | ||
| } | ||
| out(formatPrefix(namespace), ...args); | ||
| }; | ||
| } | ||
| /** | ||
@@ -45,13 +100,10 @@ * Create a namespaced debug logger with log/warn/error methods | ||
| function createModuleLogger(name: string): Logger { | ||
| const logDebug = debug(`${NAMESPACE}:${name}`); | ||
| const warnDebug = debug(`${NAMESPACE}:${name}:warn`); | ||
| const errorDebug = debug(`${NAMESPACE}:${name}:error`); | ||
| const logNamespace = `${NAMESPACE}:${name}`; | ||
| const warnNamespace = `${NAMESPACE}:${name}:warn`; | ||
| const errorNamespace = `${NAMESPACE}:${name}:error`; | ||
| // Enable error logs by default (they go to stderr) | ||
| errorDebug.enabled = true; | ||
| return { | ||
| log: logDebug as (...args: unknown[]) => void, | ||
| warn: warnDebug as (...args: unknown[]) => void, | ||
| error: errorDebug as (...args: unknown[]) => void, | ||
| log: createConsoleLogger(logNamespace, "log"), | ||
| warn: createConsoleLogger(warnNamespace, "warn"), | ||
| error: createConsoleLogger(errorNamespace, "error"), | ||
| }; | ||
@@ -74,2 +126,11 @@ } | ||
| function refreshEnabledPatternFromStorage(): void { | ||
| const stored = safeReadLocalStorage("debug") ?? ""; | ||
| if (stored === enabledPattern) { | ||
| return; | ||
| } | ||
| enabledPattern = stored; | ||
| enabledRegexes = compileDebugPatterns(stored); | ||
| } | ||
| /** | ||
@@ -92,3 +153,4 @@ * Enable debug logging programmatically | ||
| export function enableDebug(namespaces: string = "unisqlite:*"): void { | ||
| debug.enable(namespaces); | ||
| safeWriteLocalStorage("debug", namespaces); | ||
| refreshEnabledPatternFromStorage(); | ||
| } | ||
@@ -100,3 +162,6 @@ | ||
| export function disableDebug(): void { | ||
| debug.disable(); | ||
| safeWriteLocalStorage("debug", null); | ||
| refreshEnabledPatternFromStorage(); | ||
| } | ||
| refreshEnabledPatternFromStorage(); |
@@ -9,3 +9,3 @@ /** | ||
| import type { SQLiteParams, SQLiteValue, QueryResult, RunResult } from "../../types.js"; | ||
| import type { SQLiteParams } from "../../types.js"; | ||
@@ -12,0 +12,0 @@ /** Unique identifier for each browser tab */ |
+2
-0
| export type { | ||
| ChangeEvent, | ||
| ConnectionType, | ||
| ConnectionRole, | ||
| ConnectionRoleChangeListener, | ||
| QueryResult, | ||
@@ -5,0 +7,0 @@ RunResult, |
+23
-0
@@ -6,2 +6,6 @@ export type SQLiteValue = string | number | boolean | null | Uint8Array; | ||
| export type ConnectionRole = "host" | "participant" | "unknown"; | ||
| export type ConnectionRoleChangeListener = (role: ConnectionRole) => void; | ||
| export interface SQLiteWasmConfig { | ||
@@ -208,2 +212,21 @@ /** | ||
| getConnectionType(): ConnectionType; | ||
| /** | ||
| * Connection role for multi-tab browser adapters. | ||
| * | ||
| * - `host`: This tab owns the underlying database instance and can accept writes. | ||
| * - `participant`: This tab forwards requests to the host. | ||
| * - `unknown`: Initializing/closing or host not ready yet. | ||
| * | ||
| * Non-browser adapters typically behave like a stable `host`. | ||
| */ | ||
| getRole?: () => ConnectionRole; | ||
| /** | ||
| * Subscribe to role changes (multi-tab browser adapters). | ||
| * Returns an unsubscribe function. | ||
| */ | ||
| subscribeRoleChange?: (listener: ConnectionRoleChangeListener) => () => void; | ||
| /** | ||
| * Wait until the connection reaches the desired role (multi-tab browser adapters). | ||
| */ | ||
| waitForRole?: (role: ConnectionRole, timeoutMs?: number) => Promise<void>; | ||
| close(): Promise<void>; | ||
@@ -210,0 +233,0 @@ } |
| var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return s}}); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
714494
4.62%6145
4.15%10
-9.09%554
11.02%33
-2.94%84
-1.18%