diff --git a/src/client.ts b/src/client.ts index dd3f440..f58941e 100644 --- a/src/client.ts +++ b/src/client.ts @@ -22,7 +22,9 @@ import type { StatementInput, Transaction, DeleteMessage, + DatabasePath, } from './types.js'; +import { SQLocalProcessor } from './processor.js'; import { sqlTag } from './lib/sql-tag.js'; import { convertRowsToObjects } from './lib/convert-rows-to-objects.js'; import { normalizeStatement } from './lib/normalize-statement.js'; @@ -34,8 +36,8 @@ import { mutationLock } from './lib/mutation-lock.js'; export class SQLocal { protected config: ClientConfig; protected clientKey: QueryKey; - protected worker?: Worker; - protected isWorkerDestroyed: boolean = false; + protected processor?: SQLocalProcessor | Worker; + protected isDestroyed: boolean = false; protected bypassMutationLock: boolean = false; protected userCallbacks = new Map(); protected queriesInProgress = new Map< @@ -49,9 +51,9 @@ export class SQLocal { protected proxy?: WorkerProxy; protected reinitChannel: BroadcastChannel; - constructor(databasePath: string); + constructor(databasePath: DatabasePath); constructor(config: ClientConfig); - constructor(config: string | ClientConfig) { + constructor(config: DatabasePath | ClientConfig) { const clientConfig = typeof config === 'string' ? { databasePath: config } : config; this.config = clientConfig; @@ -65,23 +67,23 @@ export class SQLocal { ); if (typeof globalThis.Worker !== 'undefined') { - this.worker = new Worker(new URL('./worker', import.meta.url), { + this.processor = new Worker(new URL('./worker', import.meta.url), { type: 'module', }); - this.worker.addEventListener('message', this.processMessageEvent); - - this.proxy = coincident(this.worker) as WorkerProxy; - this.worker.postMessage({ - type: 'config', - config: processorConfig, - } satisfies ConfigMessage); + this.processor.addEventListener('message', this.processMessageEvent); + this.proxy = coincident(this.processor) as WorkerProxy; } + + this.processor?.postMessage({ + type: 'config', + config: processorConfig, + } satisfies ConfigMessage); } protected processMessageEvent = ( - event: MessageEvent + event: OutputMessage | MessageEvent ): void => { - const message = event.data; + const message = event instanceof MessageEvent ? event.data : event; const queries = this.queriesInProgress; switch (message.type) { @@ -135,13 +137,13 @@ export class SQLocal { message.type === 'delete', this.config, async () => { - if (!this.worker) { + if (!this.processor) { throw new Error( 'This SQLocal client is not connected to a database. This is likely due to the client being initialized in a server-side environment.' ); } - if (this.isWorkerDestroyed === true) { + if (this.isDestroyed === true) { throw new Error( 'This SQLocal client has been destroyed. You will need to initialize a new client in order to make further queries.' ); @@ -151,7 +153,7 @@ export class SQLocal { switch (message.type) { case 'import': - this.worker.postMessage( + this.processor.postMessage( { ...message, queryKey, @@ -160,7 +162,7 @@ export class SQLocal { ); break; default: - this.worker.postMessage({ + this.processor.postMessage({ ...message, queryKey, } satisfies @@ -355,14 +357,20 @@ export class SQLocal { funcName: string, func: ScalarUserFunction['func'] ): Promise => { + const key = `_sqlocal_func_${funcName}`; + + if (this.proxy === globalThis) { + this.proxy[key] = func; + } + await this.createQuery({ type: 'function', functionName: funcName, functionType: 'scalar', }); - if (this.proxy) { - this.proxy[`_sqlocal_func_${funcName}`] = func; + if (this.proxy && this.proxy !== globalThis) { + this.proxy[key] = func; } }; @@ -455,11 +463,15 @@ export class SQLocal { destroy = async (): Promise => { await this.createQuery({ type: 'destroy' }); - this.worker?.removeEventListener('message', this.processMessageEvent); + + if (this.processor instanceof Worker) { + this.processor.removeEventListener('message', this.processMessageEvent); + this.processor.terminate(); + } + this.queriesInProgress.clear(); this.userCallbacks.clear(); this.reinitChannel.close(); - this.worker?.terminate(); - this.isWorkerDestroyed = true; + this.isDestroyed = true; }; } diff --git a/src/processor.ts b/src/processor.ts index cb2ffec..1f083bc 100644 --- a/src/processor.ts +++ b/src/processor.ts @@ -41,8 +41,9 @@ export class SQLocalProcessor { onmessage?: (message: OutputMessage, transfer: Transferable[]) => void; - constructor(worker: typeof globalThis) { - this.proxy = coincident(worker) as WorkerProxy; + constructor(sameContext: boolean) { + const proxy = sameContext ? globalThis : coincident(globalThis); + this.proxy = proxy as WorkerProxy; this.init(); } @@ -102,11 +103,10 @@ export class SQLocalProcessor { }; postMessage = async ( - message: InputMessage | MessageEvent + event: InputMessage | MessageEvent, + _transfer?: Transferable ): Promise => { - if (message instanceof MessageEvent) { - message = message.data; - } + const message = event instanceof MessageEvent ? event.data : event; await this.initMutex.lock(); diff --git a/src/types.ts b/src/types.ts index 5992211..2f12a3d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -49,22 +49,24 @@ export type RawResultData = { // Database status +export type DatabasePath = string; + export type ClientConfig = { - databasePath: string; + databasePath: DatabasePath; readOnly?: boolean; verbose?: boolean; onConnect?: () => void; }; export type ProcessorConfig = { - databasePath?: string; + databasePath?: DatabasePath; readOnly?: boolean; verbose?: boolean; clientKey?: QueryKey; }; export type DatabaseInfo = { - databasePath?: string; + databasePath?: DatabasePath; databaseSizeBytes?: number; storageType?: Sqlite3StorageType; persisted?: boolean; @@ -75,7 +77,7 @@ export type DatabaseInfo = { export type Message = InputMessage | OutputMessage; export type QueryKey = string; export type OmitQueryKey = T extends Message ? Omit : never; -export type WorkerProxy = ProxyHandler & +export type WorkerProxy = (typeof globalThis | ProxyHandler) & Record any>; export type InputMessage = diff --git a/src/worker.ts b/src/worker.ts index 5077fcd..882f2b6 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -1,6 +1,6 @@ import { SQLocalProcessor } from './processor.js'; -const processor = new SQLocalProcessor(self); +const processor = new SQLocalProcessor(false); self.onmessage = (message) => { processor.postMessage(message);