@electric-sql/pglite-socket
Advanced tools
| import{createServer as m}from"net";var b=6e4,c=class{constructor(s,e=!1){this.queue=[];this.processing=!1;this.lastHandlerId=null;this.db=s,this.debug=e}log(s,...e){this.debug&&console.log(`[QueryQueueManager] ${s}`,...e)}async enqueue(s,e,i){return new Promise((t,r)=>{let o={handlerId:s,message:e,resolve:t,reject:r,timestamp:Date.now(),onData:i};this.queue.push(o),this.log(`enqueued query from handler #${s}, queue size: ${this.queue.length}`),this.processing||this.processQueue()})}async processQueue(){if(!(this.processing||this.queue.length===0)){for(this.processing=!0;this.queue.length>0;){let s;if(this.db.isInTransaction()&&this.lastHandlerId){let t=this.queue.findIndex(r=>r.handlerId===this.lastHandlerId);t===-1?(this.log("transaction started, but no query from the same handler id found in queue",this.lastHandlerId),s=null):s=this.queue.splice(t,1)[0]}else s=this.queue.shift();if(!s)break;let e=Date.now()-s.timestamp;this.log(`processing query from handler #${s.handlerId} (waited ${e}ms)`);let i=0;try{await this.db.runExclusive(async()=>await this.db.execProtocolRawStream(s.message,{onRawData:t=>{i+=t.length,s.onData(t)}}))}catch(t){this.log(`query from handler #${s.handlerId} failed:`,t),s.reject(t);return}this.log(`query from handler #${s.handlerId} completed, ${i} bytes`),this.lastHandlerId=s.handlerId,s.resolve(i)}this.processing=!1,this.log("queue processing complete, queue length is",this.queue.length)}}getQueueLength(){return this.queue.length}clearQueueForHandler(s){let e=this.queue.length;this.queue=this.queue.filter(t=>t.handlerId===s?(t.reject(new Error("Handler disconnected")),!1):!0);let i=e-this.queue.length;i>0&&this.log(`cleared ${i} queries for handler #${s}`)}async clearTransactionIfNeeded(s){this.db.isInTransaction()&&this.lastHandlerId===s&&(await this.db.exec("ROLLBACK"),this.lastHandlerId=null,await this.processQueue())}},l=class l extends EventTarget{constructor(e){super();this.socket=null;this.active=!1;this.messageBuffer=Buffer.alloc(0);this.lastActivityTime=Date.now();this.queryQueue=e.queryQueue,this.closeOnDetach=e.closeOnDetach??!1,this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.id=l.nextHandlerId++,this.log("constructor: created new handler")}get handlerId(){return this.id}log(e,...i){this.debug&&console.log(`[PGLiteSocketHandler#${this.id}] ${e}`,...i)}async attach(e){if(this.log(`attach: attaching socket from ${e.remoteAddress}:${e.remotePort}`),this.socket)throw new Error("Socket already attached");return this.socket=e,this.active=!0,this.lastActivityTime=Date.now(),e.setNoDelay(!0),this.idleTimeout>0&&this.resetIdleTimer(),this.log("attach: setting up socket event handlers"),e.on("data",i=>{this.lastActivityTime=Date.now(),this.resetIdleTimer(),setImmediate(async()=>{try{await this.handleData(i)}catch(t){this.log("socket on data error: ",t),this.handleError(t)}})}),e.on("error",i=>{setImmediate(()=>this.handleError(i))}),e.on("close",()=>{setImmediate(()=>this.handleClose())}),this.log("attach: socket handler ready"),this}resetIdleTimer(){this.idleTimeout<=0||(this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>{let e=Date.now()-this.lastActivityTime;this.log(`idle timeout after ${e}ms`),this.handleError(new Error("Idle timeout"))},this.idleTimeout))}async detach(e){if(this.log(`detach: detaching socket, close=${e??this.closeOnDetach}`),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=void 0),this.queryQueue.clearQueueForHandler(this.id),await this.queryQueue.clearTransactionIfNeeded(this.id),!this.socket)return this.log("detach: no socket attached, nothing to do"),this;if(this.socket.removeAllListeners("data"),this.socket.removeAllListeners("error"),this.socket.removeAllListeners("close"),(e??this.closeOnDetach)&&this.socket.writable){this.log("detach: closing socket");try{this.socket.end(),this.socket.destroy()}catch(i){this.log("detach: error closing socket:",i)}}return this.socket=null,this.active=!1,this.messageBuffer=Buffer.alloc(0),this.log("detach: handler cleaned up"),this}get isAttached(){return this.socket!==null}async handleData(e){if(!this.socket||!this.active)return this.log("handleData: no active socket, ignoring data"),0;this.log(`handleData: received ${e.length} bytes`),this.messageBuffer=Buffer.concat([this.messageBuffer,e]),this.inspectData("incoming",e);try{let i=0;for(;this.messageBuffer.length>0;){let t=0,r=!1;if(this.messageBuffer.length>=4){let n=this.messageBuffer.readInt32BE(0);if(this.messageBuffer.length>=8){let a=this.messageBuffer.readInt32BE(4);(a===196608||a===196608)&&(t=n,r=this.messageBuffer.length>=t)}!r&&this.messageBuffer.length>=5&&(t=1+this.messageBuffer.readInt32BE(1),r=this.messageBuffer.length>=t)}if(!r||t===0){this.log(`handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`);break}let o=this.messageBuffer.slice(0,t);if(this.messageBuffer=this.messageBuffer.slice(t),this.log(`handleData: processing message of ${o.length} bytes`),!this.active||!this.socket){this.log("handleData: socket no longer active, stopping processing");break}let h;if(await this.queryQueue.enqueue(this.id,new Uint8Array(o),n=>{this.log(`handleData: received ${n.length} bytes from PGlite`),this.inspectData("outgoing",n),n.length>0&&this.socket&&this.socket.writable&&this.active&&(this.log("handleData: writing response to socket"),this.socket?.writable?this.socket.write(Buffer.from(n),a=>{a?(this.log("handleData: error writing to socket:",a),h=a):this.log(`handleData: socket sent: ${n.length} bytes`)}):this.log("handleData: socket no longer writable")),i+=n.length}),h)throw h}return this.dispatchEvent(new CustomEvent("data",{detail:{incoming:e.length,outgoing:i}})),i}catch(i){throw this.log("handleData: error processing data:",i),i}}handleError(e){if(!this.active){this.log("handleError: handler not active, ignoring error");return}e.message?.includes("ECONNRESET")?this.log("handleError: client disconnected (ECONNRESET) - normal behavior"):e.message?.includes("Idle timeout")?this.log("handleError: connection idle timeout"):this.log("handleError:",e),this.active=!1,this.dispatchEvent(new CustomEvent("error",{detail:e})),this.detach(!0)}handleClose(){this.log("handleClose: socket closed"),this.active=!1,this.dispatchEvent(new CustomEvent("close")),this.detach(!1)}inspectData(e,i){if(this.inspect){console.log("-".repeat(75)),console.log(e==="incoming"?"-> incoming":"<- outgoing",i.length,"bytes");for(let t=0;t<i.length;t+=16){let r=Math.min(16,i.length-t),o="";for(let n=0;n<16;n++)if(n<r){let a=i[t+n];o+=a.toString(16).padStart(2,"0")+" "}else o+=" ";let h="";for(let n=0;n<r;n++){let a=i[t+n];h+=a>=32&&a<=126?String.fromCharCode(a):"."}console.log(`${t.toString(16).padStart(8,"0")} ${o} ${h}`)}}}};l.nextHandlerId=1;var d=l,u=class extends EventTarget{constructor(e){super();this.server=null;this.active=!1;this.handlers=new Set;this.db=e.db,e.path?this.path=e.path:(typeof e.port=="number"?this.port=e.port??e.port:this.port=5432,this.host=e.host||"127.0.0.1"),this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.maxConnections=e.maxConnections??1,this.queryQueue=new c(this.db,this.debug),this.log(`constructor: created server on ${this.getServerConn()}`),this.log(`constructor: max connections: ${this.maxConnections}`),this.idleTimeout>0&&this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)}log(e,...i){this.debug&&console.log(`[PGLiteSocketServer] ${e}`,...i)}async start(){if(this.log(`start: starting server on ${this.getServerConn()}`),this.server)throw new Error("Socket server already started");return await this.db.waitReady,this.active=!0,this.server=m(e=>{setImmediate(()=>this.handleConnection(e))}),this.server.maxConnections=this.maxConnections,new Promise((e,i)=>{if(!this.server)return i(new Error("Server not initialized"));if(this.server.on("error",t=>{this.log("start: server error:",t),this.dispatchEvent(new CustomEvent("error",{detail:t})),this.active||i(t)}),this.path)this.server.listen(this.path,()=>{this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{path:this.path}})),e()});else{let t=this.server;t.listen(this.port,this.host,()=>{let r=t.address();if(r===null||typeof r!="object")throw Error("Expected address info");this.port=r.port,this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{port:this.port,host:this.host}})),e()})}})}getServerConn(){return this.path?this.path:`${this.host}:${this.port}`}async stop(){this.log("stop: stopping server"),this.active=!1,this.log(`stop: detaching ${this.handlers.size} handlers`);for(let e of this.handlers)e.detach(!0);return this.handlers.clear(),this.server?new Promise(e=>{if(!this.server)return e();this.server.close(()=>{this.log("stop: server closed"),this.server=null,this.dispatchEvent(new CustomEvent("close")),e()})}):(this.log("stop: server not running, nothing to do"),Promise.resolve())}async handleConnection(e){let i={clientAddress:e.remoteAddress||"unknown",clientPort:e.remotePort||0};if(this.log(`handleConnection: new connection from ${i.clientAddress}:${i.clientPort}`),this.log(`handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`),!this.active){this.log("handleConnection: server not active, closing connection");try{e.end()}catch(r){this.log("handleConnection: error closing socket:",r)}return}if(this.handlers.size>=this.maxConnections){this.log("handleConnection: max connections reached, rejecting"),e.write(Buffer.from(`Too many connections | ||
| `)),e.end();return}let t=new d({queryQueue:this.queryQueue,closeOnDetach:!0,inspect:this.inspect,debug:this.debug,idleTimeout:this.idleTimeout});this.handlers.add(t),t.addEventListener("error",r=>{let o=r.detail;o?.message?.includes("ECONNRESET")?this.log(`handler #${t.handlerId}: client disconnected (ECONNRESET)`):o?.message?.includes("Idle timeout")?this.log(`handler #${t.handlerId}: idle timeout`):this.log(`handler #${t.handlerId}: error:`,o)}),t.addEventListener("close",()=>{this.log(`handler #${t.handlerId}: closed`),this.handlers.delete(t),this.log(`handleConnection: active connections: ${this.handlers.size}`)});try{await t.attach(e),this.dispatchEvent(new CustomEvent("connection",{detail:i}))}catch(r){this.log("handleConnection: error attaching socket:",r),this.handlers.delete(t),this.dispatchEvent(new CustomEvent("error",{detail:r}));try{e.end()}catch(o){this.log("handleConnection: error closing socket:",o)}}}getStats(){return{activeConnections:this.handlers.size,queuedQueries:this.queryQueue.getQueueLength(),maxConnections:this.maxConnections}}};export{b as a,d as b,u as c}; | ||
| //# sourceMappingURL=chunk-NSUMFCRM.js.map |
| {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { PGlite } from '@electric-sql/pglite'\nimport { type Server, type Socket, createServer } from 'net'\n\n// Connection queue timeout in milliseconds\nexport const CONNECTION_QUEUE_TIMEOUT = 60000 // 60 seconds\n\n/**\n * Represents a queued query waiting for PGlite access\n */\ninterface QueuedQuery {\n handlerId: number\n message: Uint8Array\n resolve: (resultSize: number) => void\n reject: (error: Error) => void\n timestamp: number\n onData: (data: Uint8Array) => void\n}\n\n/**\n * Global query queue manager\n * Ensures only one query executes at a time in PGlite\n */\nclass QueryQueueManager {\n private queue: QueuedQuery[] = []\n private processing = false\n private db: PGlite\n private debug: boolean\n private lastHandlerId: null | number = null\n\n constructor(db: PGlite, debug = false) {\n this.db = db\n this.debug = debug\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[QueryQueueManager] ${message}`, ...args)\n }\n }\n\n async enqueue(\n handlerId: number,\n message: Uint8Array,\n onData: (data: Uint8Array) => void,\n ): Promise<number> {\n return new Promise((resolve, reject) => {\n const query: QueuedQuery = {\n handlerId,\n message,\n resolve,\n reject,\n timestamp: Date.now(),\n onData,\n }\n\n this.queue.push(query)\n this.log(\n `enqueued query from handler #${handlerId}, queue size: ${this.queue.length}`,\n )\n\n // Process queue if not already processing\n if (!this.processing) {\n this.processQueue()\n }\n })\n }\n\n private async processQueue(): Promise<void> {\n if (this.processing || this.queue.length === 0) {\n return\n }\n\n this.processing = true\n\n while (this.queue.length > 0) {\n let query\n\n if (this.db.isInTransaction() && this.lastHandlerId) {\n const i = this.queue.findIndex(\n (q) => q.handlerId === this.lastHandlerId,\n )\n if (i === -1) {\n // we didn't find any other query from the same client!\n this.log(\n `transaction started, but no query from the same handler id found in queue`,\n this.lastHandlerId,\n )\n query = null\n } else {\n query = this.queue.splice(i, 1)[0]\n }\n } else {\n query = this.queue.shift()\n }\n if (!query) break\n\n const waitTime = Date.now() - query.timestamp\n this.log(\n `processing query from handler #${query.handlerId} (waited ${waitTime}ms)`,\n )\n\n let result = 0\n try {\n // Execute the query with exclusive access to PGlite\n await this.db.runExclusive(async () => {\n return await this.db.execProtocolRawStream(query.message, {\n onRawData: (data) => {\n result += data.length\n query.onData(data)\n },\n })\n })\n } catch (error) {\n this.log(`query from handler #${query.handlerId} failed:`, error)\n query.reject(error as Error)\n return\n }\n\n this.log(\n `query from handler #${query.handlerId} completed, ${result} bytes`,\n )\n this.lastHandlerId = query.handlerId\n query.resolve(result)\n }\n\n this.processing = false\n this.log(`queue processing complete, queue length is`, this.queue.length)\n }\n\n getQueueLength(): number {\n return this.queue.length\n }\n\n clearQueueForHandler(handlerId: number): void {\n const before = this.queue.length\n this.queue = this.queue.filter((q) => {\n if (q.handlerId === handlerId) {\n q.reject(new Error('Handler disconnected'))\n return false\n }\n return true\n })\n const removed = before - this.queue.length\n if (removed > 0) {\n this.log(`cleared ${removed} queries for handler #${handlerId}`)\n }\n }\n\n async clearTransactionIfNeeded(handlerId: number): Promise<void> {\n if (this.db.isInTransaction() && this.lastHandlerId === handlerId) {\n await this.db.exec('ROLLBACK')\n this.lastHandlerId = null\n await this.processQueue()\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketHandler\n */\nexport interface PGLiteSocketHandlerOptions {\n /** The query queue manager */\n queryQueue: QueryQueueManager\n /** Whether to close the socket when detached (default: false) */\n closeOnDetach?: boolean\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n}\n\n/**\n * Handler for a single socket connection to PGlite\n * Each connection can remain open and send multiple queries\n */\nexport class PGLiteSocketHandler extends EventTarget {\n private queryQueue: QueryQueueManager\n private socket: Socket | null = null\n private active = false\n private closeOnDetach: boolean\n private inspect: boolean\n private debug: boolean\n private readonly id: number\n private messageBuffer: Buffer = Buffer.alloc(0)\n private idleTimer?: NodeJS.Timeout\n private idleTimeout: number\n private lastActivityTime: number = Date.now()\n\n // Static counter for generating unique handler IDs\n private static nextHandlerId = 1\n\n constructor(options: PGLiteSocketHandlerOptions) {\n super()\n this.queryQueue = options.queryQueue\n this.closeOnDetach = options.closeOnDetach ?? false\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.id = PGLiteSocketHandler.nextHandlerId++\n\n this.log('constructor: created new handler')\n }\n\n public get handlerId(): number {\n return this.id\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketHandler#${this.id}] ${message}`, ...args)\n }\n }\n\n public async attach(socket: Socket): Promise<PGLiteSocketHandler> {\n this.log(\n `attach: attaching socket from ${socket.remoteAddress}:${socket.remotePort}`,\n )\n\n if (this.socket) {\n throw new Error('Socket already attached')\n }\n\n this.socket = socket\n this.active = true\n this.lastActivityTime = Date.now()\n\n // Set up socket options\n socket.setNoDelay(true)\n\n // Set up idle timeout if configured\n if (this.idleTimeout > 0) {\n this.resetIdleTimer()\n }\n\n // Setup event handlers\n this.log(`attach: setting up socket event handlers`)\n\n socket.on('data', (data) => {\n this.lastActivityTime = Date.now()\n this.resetIdleTimer()\n\n setImmediate(async () => {\n try {\n await this.handleData(data)\n } catch (err) {\n this.log('socket on data error: ', err)\n this.handleError(err as Error)\n }\n })\n })\n\n socket.on('error', (err) => {\n setImmediate(() => this.handleError(err))\n })\n\n socket.on('close', () => {\n setImmediate(() => this.handleClose())\n })\n\n this.log(`attach: socket handler ready`)\n return this\n }\n\n private resetIdleTimer(): void {\n if (this.idleTimeout <= 0) return\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n }\n\n this.idleTimer = setTimeout(() => {\n const idleTime = Date.now() - this.lastActivityTime\n this.log(`idle timeout after ${idleTime}ms`)\n this.handleError(new Error('Idle timeout'))\n }, this.idleTimeout)\n }\n\n public async detach(close?: boolean): Promise<PGLiteSocketHandler> {\n this.log(`detach: detaching socket, close=${close ?? this.closeOnDetach}`)\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n this.idleTimer = undefined\n }\n\n // Clear any pending queries for this handler\n this.queryQueue.clearQueueForHandler(this.id)\n\n await this.queryQueue.clearTransactionIfNeeded(this.id)\n\n if (!this.socket) {\n this.log(`detach: no socket attached, nothing to do`)\n return this\n }\n\n // Remove all listeners\n this.socket.removeAllListeners('data')\n this.socket.removeAllListeners('error')\n this.socket.removeAllListeners('close')\n\n // Close the socket if requested\n if (close ?? this.closeOnDetach) {\n if (this.socket.writable) {\n this.log(`detach: closing socket`)\n try {\n this.socket.end()\n this.socket.destroy()\n } catch (err) {\n this.log(`detach: error closing socket:`, err)\n }\n }\n }\n\n this.socket = null\n this.active = false\n this.messageBuffer = Buffer.alloc(0)\n\n this.log(`detach: handler cleaned up`)\n return this\n }\n\n public get isAttached(): boolean {\n return this.socket !== null\n }\n\n private async handleData(data: Buffer): Promise<number> {\n if (!this.socket || !this.active) {\n this.log(`handleData: no active socket, ignoring data`)\n return 0\n }\n\n this.log(`handleData: received ${data.length} bytes`)\n\n // Append to buffer for message reassembly\n this.messageBuffer = Buffer.concat([this.messageBuffer, data])\n\n // Print the incoming data to the console\n this.inspectData('incoming', data)\n\n try {\n let totalProcessed = 0\n\n while (this.messageBuffer.length > 0) {\n // Determine message length\n let messageLength = 0\n let isComplete = false\n\n // Handle startup message (no type byte, just length)\n if (this.messageBuffer.length >= 4) {\n const firstInt = this.messageBuffer.readInt32BE(0)\n\n if (this.messageBuffer.length >= 8) {\n const secondInt = this.messageBuffer.readInt32BE(4)\n // PostgreSQL 3.0 protocol version\n if (secondInt === 196608 || secondInt === 0x00030000) {\n messageLength = firstInt\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n // Regular message (type byte + length)\n if (!isComplete && this.messageBuffer.length >= 5) {\n const msgLength = this.messageBuffer.readInt32BE(1)\n messageLength = 1 + msgLength\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n if (!isComplete || messageLength === 0) {\n this.log(\n `handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`,\n )\n break\n }\n\n // Extract and process complete message\n const message = this.messageBuffer.slice(0, messageLength)\n this.messageBuffer = this.messageBuffer.slice(messageLength)\n\n this.log(`handleData: processing message of ${message.length} bytes`)\n\n // Check if socket is still active before processing\n if (!this.active || !this.socket) {\n this.log(`handleData: socket no longer active, stopping processing`)\n break\n }\n\n let socketWriteError: any = undefined\n // Queue the query for execution\n // This allows multiple connections to queue queries simultaneously\n await this.queryQueue.enqueue(\n this.id,\n new Uint8Array(message),\n (data) => {\n this.log(`handleData: received ${data.length} bytes from PGlite`)\n\n // Print the outgoing data to the console\n this.inspectData('outgoing', data)\n\n // Send response if available\n if (\n data.length > 0 &&\n this.socket &&\n this.socket.writable &&\n this.active\n ) {\n // await new Promise<number>((resolve, reject) => {\n this.log(`handleData: writing response to socket`)\n if (this.socket?.writable) {\n this.socket.write(Buffer.from(data), (err?: any) => {\n if (err) {\n this.log(`handleData: error writing to socket:`, err)\n socketWriteError = err\n } else {\n this.log(`handleData: socket sent: ${data.length} bytes`)\n }\n })\n } else {\n this.log(`handleData: socket no longer writable`)\n }\n }\n totalProcessed += data.length\n },\n )\n if (socketWriteError) throw socketWriteError\n }\n\n // Emit data event with byte sizes\n this.dispatchEvent(\n new CustomEvent('data', {\n detail: { incoming: data.length, outgoing: totalProcessed },\n }),\n )\n\n return totalProcessed\n } catch (err) {\n this.log(`handleData: error processing data:`, err)\n throw err\n }\n }\n\n private handleError(err: Error): void {\n if (!this.active) {\n this.log(`handleError: handler not active, ignoring error`)\n return\n }\n\n // ECONNRESET is expected behavior when clients disconnect\n if (err.message?.includes('ECONNRESET')) {\n this.log(\n `handleError: client disconnected (ECONNRESET) - normal behavior`,\n )\n } else if (err.message?.includes('Idle timeout')) {\n this.log(`handleError: connection idle timeout`)\n } else {\n this.log(`handleError:`, err)\n }\n\n this.active = false\n\n // Emit error event\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n\n // Clean up\n this.detach(true)\n }\n\n private handleClose(): void {\n this.log(`handleClose: socket closed`)\n this.active = false\n this.dispatchEvent(new CustomEvent('close'))\n this.detach(false)\n }\n\n private inspectData(\n direction: 'incoming' | 'outgoing',\n data: Buffer | Uint8Array,\n ): void {\n if (!this.inspect) return\n console.log('-'.repeat(75))\n if (direction === 'incoming') {\n console.log('-> incoming', data.length, 'bytes')\n } else {\n console.log('<- outgoing', data.length, 'bytes')\n }\n\n for (let offset = 0; offset < data.length; offset += 16) {\n const chunkSize = Math.min(16, data.length - offset)\n\n let hexPart = ''\n for (let i = 0; i < 16; i++) {\n if (i < chunkSize) {\n const byte = data[offset + i]\n hexPart += byte.toString(16).padStart(2, '0') + ' '\n } else {\n hexPart += ' '\n }\n }\n\n let asciiPart = ''\n for (let i = 0; i < chunkSize; i++) {\n const byte = data[offset + i]\n asciiPart += byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : '.'\n }\n\n console.log(\n `${offset.toString(16).padStart(8, '0')} ${hexPart} ${asciiPart}`,\n )\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketServer\n */\nexport interface PGLiteSocketServerOptions {\n /** The PGlite database instance */\n db: PGlite\n /** The port to listen on (default: 5432) */\n port?: number\n /** The host to bind to (default: 127.0.0.1) */\n host?: string\n /** Unix socket path to bind to (default: undefined) */\n path?: string\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n /** Maximum concurrent connections (default: 100) */\n maxConnections?: number\n}\n\n/**\n * PGLite Socket Server with support for multiple concurrent connections\n * Connections remain open and queries are queued at the query level\n */\nexport class PGLiteSocketServer extends EventTarget {\n readonly db: PGlite\n private server: Server | null = null\n private port?: number\n private host?: string\n private path?: string\n private active = false\n private inspect: boolean\n private debug: boolean\n private idleTimeout: number\n private maxConnections: number\n private handlers: Set<PGLiteSocketHandler> = new Set()\n private queryQueue: QueryQueueManager\n\n constructor(options: PGLiteSocketServerOptions) {\n super()\n this.db = options.db\n if (options.path) {\n this.path = options.path\n } else {\n if (typeof options.port === 'number') {\n // Keep port undefined on port 0, will be set by the OS when we start the server.\n this.port = options.port ?? options.port\n } else {\n this.port = 5432\n }\n this.host = options.host || '127.0.0.1'\n }\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.maxConnections = options.maxConnections ?? 1\n\n // Create the shared query queue\n this.queryQueue = new QueryQueueManager(this.db, this.debug)\n\n this.log(`constructor: created server on ${this.getServerConn()}`)\n this.log(`constructor: max connections: ${this.maxConnections}`)\n if (this.idleTimeout > 0) {\n this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)\n }\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketServer] ${message}`, ...args)\n }\n }\n\n public async start(): Promise<void> {\n this.log(`start: starting server on ${this.getServerConn()}`)\n\n if (this.server) {\n throw new Error('Socket server already started')\n }\n\n // Ensure PGlite is ready before accepting connections\n await this.db.waitReady\n\n this.active = true\n this.server = createServer((socket) => {\n setImmediate(() => this.handleConnection(socket))\n })\n\n this.server.maxConnections = this.maxConnections\n\n return new Promise<void>((resolve, reject) => {\n if (!this.server) return reject(new Error('Server not initialized'))\n\n this.server.on('error', (err) => {\n this.log(`start: server error:`, err)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n if (!this.active) {\n reject(err)\n }\n })\n\n if (this.path) {\n this.server.listen(this.path, () => {\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { path: this.path },\n }),\n )\n resolve()\n })\n } else {\n const server = this.server\n server.listen(this.port, this.host, () => {\n const address = server.address()\n // We are not using pipes, so return type should be AddressInfo\n if (address === null || typeof address !== 'object') {\n throw Error('Expected address info')\n }\n // Assign the new port number\n this.port = address.port\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { port: this.port, host: this.host },\n }),\n )\n resolve()\n })\n }\n })\n }\n\n public getServerConn(): string {\n if (this.path) return this.path\n return `${this.host}:${this.port}`\n }\n\n public async stop(): Promise<void> {\n this.log(`stop: stopping server`)\n\n this.active = false\n\n // Detach all handlers\n this.log(`stop: detaching ${this.handlers.size} handlers`)\n for (const handler of this.handlers) {\n handler.detach(true)\n }\n this.handlers.clear()\n\n if (!this.server) {\n this.log(`stop: server not running, nothing to do`)\n return Promise.resolve()\n }\n\n return new Promise<void>((resolve) => {\n if (!this.server) return resolve()\n\n this.server.close(() => {\n this.log(`stop: server closed`)\n this.server = null\n this.dispatchEvent(new CustomEvent('close'))\n resolve()\n })\n })\n }\n\n private async handleConnection(socket: Socket): Promise<void> {\n const clientInfo = {\n clientAddress: socket.remoteAddress || 'unknown',\n clientPort: socket.remotePort || 0,\n }\n\n this.log(\n `handleConnection: new connection from ${clientInfo.clientAddress}:${clientInfo.clientPort}`,\n )\n this.log(\n `handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`,\n )\n\n if (!this.active) {\n this.log(`handleConnection: server not active, closing connection`)\n try {\n socket.end()\n } catch (err) {\n this.log(`handleConnection: error closing socket:`, err)\n }\n return\n }\n\n // Check connection limit\n if (this.handlers.size >= this.maxConnections) {\n this.log(`handleConnection: max connections reached, rejecting`)\n socket.write(Buffer.from('Too many connections\\n'))\n socket.end()\n return\n }\n\n // Create a new handler for this connection\n const handler = new PGLiteSocketHandler({\n queryQueue: this.queryQueue,\n closeOnDetach: true,\n inspect: this.inspect,\n debug: this.debug,\n idleTimeout: this.idleTimeout,\n })\n\n // Track this handler\n this.handlers.add(handler)\n\n // Handle errors\n handler.addEventListener('error', (event) => {\n const error = (event as CustomEvent<Error>).detail\n\n if (error?.message?.includes('ECONNRESET')) {\n this.log(\n `handler #${handler.handlerId}: client disconnected (ECONNRESET)`,\n )\n } else if (error?.message?.includes('Idle timeout')) {\n this.log(`handler #${handler.handlerId}: idle timeout`)\n } else {\n this.log(`handler #${handler.handlerId}: error:`, error)\n }\n })\n\n // Handle close event\n handler.addEventListener('close', () => {\n this.log(`handler #${handler.handlerId}: closed`)\n this.handlers.delete(handler)\n this.log(`handleConnection: active connections: ${this.handlers.size}`)\n })\n\n try {\n await handler.attach(socket)\n this.dispatchEvent(new CustomEvent('connection', { detail: clientInfo }))\n } catch (err) {\n this.log(`handleConnection: error attaching socket:`, err)\n this.handlers.delete(handler)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n try {\n socket.end()\n } catch (closeErr) {\n this.log(`handleConnection: error closing socket:`, closeErr)\n }\n }\n }\n\n public getStats() {\n return {\n activeConnections: this.handlers.size,\n queuedQueries: this.queryQueue.getQueueLength(),\n maxConnections: this.maxConnections,\n }\n }\n}\n"],"mappings":"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","names":["createServer","CONNECTION_QUEUE_TIMEOUT","QueryQueueManager","db","debug","message","args","handlerId","onData","resolve","reject","query","i","q","waitTime","result","data","error","before","removed","_PGLiteSocketHandler","options","socket","err","idleTime","close","totalProcessed","messageLength","isComplete","firstInt","secondInt","socketWriteError","direction","offset","chunkSize","hexPart","byte","asciiPart","PGLiteSocketHandler","PGLiteSocketServer","server","address","handler","clientInfo","event","closeErr"]} |
+8
-0
| # @electric-sql/pglite-socket | ||
| ## 0.1.0 | ||
| ### Minor Changes | ||
| - Updated dependencies [d848955] | ||
| - @electric-sql/pglite-postgis@0.0.1 | ||
| - @electric-sql/pglite@0.4.0 | ||
| ## 0.0.22 | ||
@@ -4,0 +12,0 @@ |
+1
-1
@@ -1,3 +0,3 @@ | ||
| "use strict";var g=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var b=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var E=(l,s)=>{for(var e in s)g(l,e,{get:s[e],enumerable:!0})},w=(l,s,e,i)=>{if(s&&typeof s=="object"||typeof s=="function")for(let t of b(s))!y.call(l,t)&&t!==e&&g(l,t,{get:()=>s[t],enumerable:!(i=p(s,t))||i.enumerable});return l};var C=l=>w(g({},"__esModule",{value:!0}),l);var q={};E(q,{CONNECTION_QUEUE_TIMEOUT:()=>T,PGLiteSocketHandler:()=>c,PGLiteSocketServer:()=>f});module.exports=C(q);var v=require("net"),T=6e4,m=class{constructor(s,e=!1){this.queue=[];this.processing=!1;this.lastHandlerId=null;this.db=s,this.debug=e}log(s,...e){this.debug&&console.log(`[QueryQueueManager] ${s}`,...e)}async enqueue(s,e){return new Promise((i,t)=>{let r={handlerId:s,message:e,resolve:i,reject:t,timestamp:Date.now()};this.queue.push(r),this.log(`enqueued query from handler #${s}, queue size: ${this.queue.length}`),this.processing||this.processQueue()})}async processQueue(){if(!(this.processing||this.queue.length===0)){for(this.processing=!0;this.queue.length>0;){let s;if(this.db.isInTransaction()&&this.lastHandlerId){let i=this.queue.findIndex(t=>t.handlerId===this.lastHandlerId);i===-1?(this.log("transaction started, but no query from the same handler id found in queue",this.lastHandlerId),s=null):s=this.queue.splice(i,1)[0]}else s=this.queue.shift();if(!s)break;let e=Date.now()-s.timestamp;this.log(`processing query from handler #${s.handlerId} (waited ${e}ms)`);try{let i=await this.db.runExclusive(async()=>await this.db.execProtocolRaw(s.message));this.log(`query from handler #${s.handlerId} completed, ${i.length} bytes`),this.lastHandlerId=s.handlerId,s.resolve(i)}catch(i){this.log(`query from handler #${s.handlerId} failed:`,i),s.reject(i)}}this.processing=!1,this.log("queue processing complete, queue length is",this.queue.length)}}getQueueLength(){return this.queue.length}clearQueueForHandler(s){let e=this.queue.length;this.queue=this.queue.filter(t=>t.handlerId===s?(t.reject(new Error("Handler disconnected")),!1):!0);let i=e-this.queue.length;i>0&&this.log(`cleared ${i} queries for handler #${s}`)}async clearTransactionIfNeeded(s){this.db.isInTransaction()&&this.lastHandlerId===s&&(await this.db.exec("ROLLBACK"),this.lastHandlerId=null,await this.processQueue())}},d=class d extends EventTarget{constructor(e){super();this.socket=null;this.active=!1;this.messageBuffer=Buffer.alloc(0);this.lastActivityTime=Date.now();this.queryQueue=e.queryQueue,this.closeOnDetach=e.closeOnDetach??!1,this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.id=d.nextHandlerId++,this.log("constructor: created new handler")}get handlerId(){return this.id}log(e,...i){this.debug&&console.log(`[PGLiteSocketHandler#${this.id}] ${e}`,...i)}async attach(e){if(this.log(`attach: attaching socket from ${e.remoteAddress}:${e.remotePort}`),this.socket)throw new Error("Socket already attached");return this.socket=e,this.active=!0,this.lastActivityTime=Date.now(),e.setNoDelay(!0),this.idleTimeout>0&&this.resetIdleTimer(),this.log("attach: setting up socket event handlers"),e.on("data",i=>{this.lastActivityTime=Date.now(),this.resetIdleTimer(),setImmediate(async()=>{try{await this.handleData(i)}catch(t){this.log("socket on data error: ",t),this.handleError(t)}})}),e.on("error",i=>{setImmediate(()=>this.handleError(i))}),e.on("close",()=>{setImmediate(()=>this.handleClose())}),this.log("attach: socket handler ready"),this}resetIdleTimer(){this.idleTimeout<=0||(this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>{let e=Date.now()-this.lastActivityTime;this.log(`idle timeout after ${e}ms`),this.handleError(new Error("Idle timeout"))},this.idleTimeout))}async detach(e){if(this.log(`detach: detaching socket, close=${e??this.closeOnDetach}`),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=void 0),this.queryQueue.clearQueueForHandler(this.id),await this.queryQueue.clearTransactionIfNeeded(this.id),!this.socket)return this.log("detach: no socket attached, nothing to do"),this;if(this.socket.removeAllListeners("data"),this.socket.removeAllListeners("error"),this.socket.removeAllListeners("close"),(e??this.closeOnDetach)&&this.socket.writable){this.log("detach: closing socket");try{this.socket.end(),this.socket.destroy()}catch(i){this.log("detach: error closing socket:",i)}}return this.socket=null,this.active=!1,this.messageBuffer=Buffer.alloc(0),this.log("detach: handler cleaned up"),this}get isAttached(){return this.socket!==null}async handleData(e){if(!this.socket||!this.active)return this.log("handleData: no active socket, ignoring data"),0;this.log(`handleData: received ${e.length} bytes`),this.messageBuffer=Buffer.concat([this.messageBuffer,e]),this.inspectData("incoming",e);try{let i=0;for(;this.messageBuffer.length>0;){let t=0,r=!1;if(this.messageBuffer.length>=4){let n=this.messageBuffer.readInt32BE(0);if(this.messageBuffer.length>=8){let a=this.messageBuffer.readInt32BE(4);(a===196608||a===196608)&&(t=n,r=this.messageBuffer.length>=t)}!r&&this.messageBuffer.length>=5&&(t=1+this.messageBuffer.readInt32BE(1),r=this.messageBuffer.length>=t)}if(!r||t===0){this.log(`handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`);break}let o=this.messageBuffer.slice(0,t);if(this.messageBuffer=this.messageBuffer.slice(t),this.log(`handleData: processing message of ${o.length} bytes`),!this.active||!this.socket){this.log("handleData: socket no longer active, stopping processing");break}let h=await this.queryQueue.enqueue(this.id,new Uint8Array(o));this.log(`handleData: received ${h.length} bytes from PGlite`),this.inspectData("outgoing",h),h.length>0&&this.socket&&this.socket.writable&&this.active&&await new Promise((n,a)=>{this.log("handleData: writing response to socket"),this.socket?.writable?this.socket.write(Buffer.from(h),u=>{u?(this.log("handleData: error writing to socket:",u),a(u)):(this.log(`handleData: socket sent: ${h.length} bytes`),n(h.length))}):(this.log("handleData: socket no longer writable"),n(0))}).catch(n=>{throw this.log("handleData: failed to write to socket:",n),n}),i+=h.length}return this.dispatchEvent(new CustomEvent("data",{detail:{incoming:e.length,outgoing:i}})),i}catch(i){throw this.log("handleData: error processing data:",i),i}}handleError(e){if(!this.active){this.log("handleError: handler not active, ignoring error");return}e.message?.includes("ECONNRESET")?this.log("handleError: client disconnected (ECONNRESET) - normal behavior"):e.message?.includes("Idle timeout")?this.log("handleError: connection idle timeout"):this.log("handleError:",e),this.active=!1,this.dispatchEvent(new CustomEvent("error",{detail:e})),this.detach(!0)}handleClose(){this.log("handleClose: socket closed"),this.active=!1,this.dispatchEvent(new CustomEvent("close")),this.detach(!1)}inspectData(e,i){if(this.inspect){console.log("-".repeat(75)),console.log(e==="incoming"?"-> incoming":"<- outgoing",i.length,"bytes");for(let t=0;t<i.length;t+=16){let r=Math.min(16,i.length-t),o="";for(let n=0;n<16;n++)if(n<r){let a=i[t+n];o+=a.toString(16).padStart(2,"0")+" "}else o+=" ";let h="";for(let n=0;n<r;n++){let a=i[t+n];h+=a>=32&&a<=126?String.fromCharCode(a):"."}console.log(`${t.toString(16).padStart(8,"0")} ${o} ${h}`)}}}};d.nextHandlerId=1;var c=d,f=class extends EventTarget{constructor(e){super();this.server=null;this.active=!1;this.handlers=new Set;this.db=e.db,e.path?this.path=e.path:(typeof e.port=="number"?this.port=e.port??e.port:this.port=5432,this.host=e.host||"127.0.0.1"),this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.maxConnections=e.maxConnections??1,this.queryQueue=new m(this.db,this.debug),this.log(`constructor: created server on ${this.getServerConn()}`),this.log(`constructor: max connections: ${this.maxConnections}`),this.idleTimeout>0&&this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)}log(e,...i){this.debug&&console.log(`[PGLiteSocketServer] ${e}`,...i)}async start(){if(this.log(`start: starting server on ${this.getServerConn()}`),this.server)throw new Error("Socket server already started");return await this.db.waitReady,this.active=!0,this.server=(0,v.createServer)(e=>{setImmediate(()=>this.handleConnection(e))}),this.server.maxConnections=this.maxConnections,new Promise((e,i)=>{if(!this.server)return i(new Error("Server not initialized"));if(this.server.on("error",t=>{this.log("start: server error:",t),this.dispatchEvent(new CustomEvent("error",{detail:t})),this.active||i(t)}),this.path)this.server.listen(this.path,()=>{this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{path:this.path}})),e()});else{let t=this.server;t.listen(this.port,this.host,()=>{let r=t.address();if(r===null||typeof r!="object")throw Error("Expected address info");this.port=r.port,this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{port:this.port,host:this.host}})),e()})}})}getServerConn(){return this.path?this.path:`${this.host}:${this.port}`}async stop(){this.log("stop: stopping server"),this.active=!1,this.log(`stop: detaching ${this.handlers.size} handlers`);for(let e of this.handlers)e.detach(!0);return this.handlers.clear(),this.server?new Promise(e=>{if(!this.server)return e();this.server.close(()=>{this.log("stop: server closed"),this.server=null,this.dispatchEvent(new CustomEvent("close")),e()})}):(this.log("stop: server not running, nothing to do"),Promise.resolve())}async handleConnection(e){let i={clientAddress:e.remoteAddress||"unknown",clientPort:e.remotePort||0};if(this.log(`handleConnection: new connection from ${i.clientAddress}:${i.clientPort}`),this.log(`handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`),!this.active){this.log("handleConnection: server not active, closing connection");try{e.end()}catch(r){this.log("handleConnection: error closing socket:",r)}return}if(this.handlers.size>=this.maxConnections){this.log("handleConnection: max connections reached, rejecting"),e.write(Buffer.from(`Too many connections | ||
| "use strict";var u=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var y=(h,s)=>{for(var e in s)u(h,e,{get:s[e],enumerable:!0})},E=(h,s,e,i)=>{if(s&&typeof s=="object"||typeof s=="function")for(let t of p(s))!b.call(h,t)&&t!==e&&u(h,t,{get:()=>s[t],enumerable:!(i=v(s,t))||i.enumerable});return h};var w=h=>E(u({},"__esModule",{value:!0}),h);var T={};y(T,{CONNECTION_QUEUE_TIMEOUT:()=>C,PGLiteSocketHandler:()=>c,PGLiteSocketServer:()=>m});module.exports=w(T);var f=require("net"),C=6e4,g=class{constructor(s,e=!1){this.queue=[];this.processing=!1;this.lastHandlerId=null;this.db=s,this.debug=e}log(s,...e){this.debug&&console.log(`[QueryQueueManager] ${s}`,...e)}async enqueue(s,e,i){return new Promise((t,r)=>{let o={handlerId:s,message:e,resolve:t,reject:r,timestamp:Date.now(),onData:i};this.queue.push(o),this.log(`enqueued query from handler #${s}, queue size: ${this.queue.length}`),this.processing||this.processQueue()})}async processQueue(){if(!(this.processing||this.queue.length===0)){for(this.processing=!0;this.queue.length>0;){let s;if(this.db.isInTransaction()&&this.lastHandlerId){let t=this.queue.findIndex(r=>r.handlerId===this.lastHandlerId);t===-1?(this.log("transaction started, but no query from the same handler id found in queue",this.lastHandlerId),s=null):s=this.queue.splice(t,1)[0]}else s=this.queue.shift();if(!s)break;let e=Date.now()-s.timestamp;this.log(`processing query from handler #${s.handlerId} (waited ${e}ms)`);let i=0;try{await this.db.runExclusive(async()=>await this.db.execProtocolRawStream(s.message,{onRawData:t=>{i+=t.length,s.onData(t)}}))}catch(t){this.log(`query from handler #${s.handlerId} failed:`,t),s.reject(t);return}this.log(`query from handler #${s.handlerId} completed, ${i} bytes`),this.lastHandlerId=s.handlerId,s.resolve(i)}this.processing=!1,this.log("queue processing complete, queue length is",this.queue.length)}}getQueueLength(){return this.queue.length}clearQueueForHandler(s){let e=this.queue.length;this.queue=this.queue.filter(t=>t.handlerId===s?(t.reject(new Error("Handler disconnected")),!1):!0);let i=e-this.queue.length;i>0&&this.log(`cleared ${i} queries for handler #${s}`)}async clearTransactionIfNeeded(s){this.db.isInTransaction()&&this.lastHandlerId===s&&(await this.db.exec("ROLLBACK"),this.lastHandlerId=null,await this.processQueue())}},d=class d extends EventTarget{constructor(e){super();this.socket=null;this.active=!1;this.messageBuffer=Buffer.alloc(0);this.lastActivityTime=Date.now();this.queryQueue=e.queryQueue,this.closeOnDetach=e.closeOnDetach??!1,this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.id=d.nextHandlerId++,this.log("constructor: created new handler")}get handlerId(){return this.id}log(e,...i){this.debug&&console.log(`[PGLiteSocketHandler#${this.id}] ${e}`,...i)}async attach(e){if(this.log(`attach: attaching socket from ${e.remoteAddress}:${e.remotePort}`),this.socket)throw new Error("Socket already attached");return this.socket=e,this.active=!0,this.lastActivityTime=Date.now(),e.setNoDelay(!0),this.idleTimeout>0&&this.resetIdleTimer(),this.log("attach: setting up socket event handlers"),e.on("data",i=>{this.lastActivityTime=Date.now(),this.resetIdleTimer(),setImmediate(async()=>{try{await this.handleData(i)}catch(t){this.log("socket on data error: ",t),this.handleError(t)}})}),e.on("error",i=>{setImmediate(()=>this.handleError(i))}),e.on("close",()=>{setImmediate(()=>this.handleClose())}),this.log("attach: socket handler ready"),this}resetIdleTimer(){this.idleTimeout<=0||(this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>{let e=Date.now()-this.lastActivityTime;this.log(`idle timeout after ${e}ms`),this.handleError(new Error("Idle timeout"))},this.idleTimeout))}async detach(e){if(this.log(`detach: detaching socket, close=${e??this.closeOnDetach}`),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=void 0),this.queryQueue.clearQueueForHandler(this.id),await this.queryQueue.clearTransactionIfNeeded(this.id),!this.socket)return this.log("detach: no socket attached, nothing to do"),this;if(this.socket.removeAllListeners("data"),this.socket.removeAllListeners("error"),this.socket.removeAllListeners("close"),(e??this.closeOnDetach)&&this.socket.writable){this.log("detach: closing socket");try{this.socket.end(),this.socket.destroy()}catch(i){this.log("detach: error closing socket:",i)}}return this.socket=null,this.active=!1,this.messageBuffer=Buffer.alloc(0),this.log("detach: handler cleaned up"),this}get isAttached(){return this.socket!==null}async handleData(e){if(!this.socket||!this.active)return this.log("handleData: no active socket, ignoring data"),0;this.log(`handleData: received ${e.length} bytes`),this.messageBuffer=Buffer.concat([this.messageBuffer,e]),this.inspectData("incoming",e);try{let i=0;for(;this.messageBuffer.length>0;){let t=0,r=!1;if(this.messageBuffer.length>=4){let n=this.messageBuffer.readInt32BE(0);if(this.messageBuffer.length>=8){let a=this.messageBuffer.readInt32BE(4);(a===196608||a===196608)&&(t=n,r=this.messageBuffer.length>=t)}!r&&this.messageBuffer.length>=5&&(t=1+this.messageBuffer.readInt32BE(1),r=this.messageBuffer.length>=t)}if(!r||t===0){this.log(`handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`);break}let o=this.messageBuffer.slice(0,t);if(this.messageBuffer=this.messageBuffer.slice(t),this.log(`handleData: processing message of ${o.length} bytes`),!this.active||!this.socket){this.log("handleData: socket no longer active, stopping processing");break}let l;if(await this.queryQueue.enqueue(this.id,new Uint8Array(o),n=>{this.log(`handleData: received ${n.length} bytes from PGlite`),this.inspectData("outgoing",n),n.length>0&&this.socket&&this.socket.writable&&this.active&&(this.log("handleData: writing response to socket"),this.socket?.writable?this.socket.write(Buffer.from(n),a=>{a?(this.log("handleData: error writing to socket:",a),l=a):this.log(`handleData: socket sent: ${n.length} bytes`)}):this.log("handleData: socket no longer writable")),i+=n.length}),l)throw l}return this.dispatchEvent(new CustomEvent("data",{detail:{incoming:e.length,outgoing:i}})),i}catch(i){throw this.log("handleData: error processing data:",i),i}}handleError(e){if(!this.active){this.log("handleError: handler not active, ignoring error");return}e.message?.includes("ECONNRESET")?this.log("handleError: client disconnected (ECONNRESET) - normal behavior"):e.message?.includes("Idle timeout")?this.log("handleError: connection idle timeout"):this.log("handleError:",e),this.active=!1,this.dispatchEvent(new CustomEvent("error",{detail:e})),this.detach(!0)}handleClose(){this.log("handleClose: socket closed"),this.active=!1,this.dispatchEvent(new CustomEvent("close")),this.detach(!1)}inspectData(e,i){if(this.inspect){console.log("-".repeat(75)),console.log(e==="incoming"?"-> incoming":"<- outgoing",i.length,"bytes");for(let t=0;t<i.length;t+=16){let r=Math.min(16,i.length-t),o="";for(let n=0;n<16;n++)if(n<r){let a=i[t+n];o+=a.toString(16).padStart(2,"0")+" "}else o+=" ";let l="";for(let n=0;n<r;n++){let a=i[t+n];l+=a>=32&&a<=126?String.fromCharCode(a):"."}console.log(`${t.toString(16).padStart(8,"0")} ${o} ${l}`)}}}};d.nextHandlerId=1;var c=d,m=class extends EventTarget{constructor(e){super();this.server=null;this.active=!1;this.handlers=new Set;this.db=e.db,e.path?this.path=e.path:(typeof e.port=="number"?this.port=e.port??e.port:this.port=5432,this.host=e.host||"127.0.0.1"),this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.maxConnections=e.maxConnections??1,this.queryQueue=new g(this.db,this.debug),this.log(`constructor: created server on ${this.getServerConn()}`),this.log(`constructor: max connections: ${this.maxConnections}`),this.idleTimeout>0&&this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)}log(e,...i){this.debug&&console.log(`[PGLiteSocketServer] ${e}`,...i)}async start(){if(this.log(`start: starting server on ${this.getServerConn()}`),this.server)throw new Error("Socket server already started");return await this.db.waitReady,this.active=!0,this.server=(0,f.createServer)(e=>{setImmediate(()=>this.handleConnection(e))}),this.server.maxConnections=this.maxConnections,new Promise((e,i)=>{if(!this.server)return i(new Error("Server not initialized"));if(this.server.on("error",t=>{this.log("start: server error:",t),this.dispatchEvent(new CustomEvent("error",{detail:t})),this.active||i(t)}),this.path)this.server.listen(this.path,()=>{this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{path:this.path}})),e()});else{let t=this.server;t.listen(this.port,this.host,()=>{let r=t.address();if(r===null||typeof r!="object")throw Error("Expected address info");this.port=r.port,this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{port:this.port,host:this.host}})),e()})}})}getServerConn(){return this.path?this.path:`${this.host}:${this.port}`}async stop(){this.log("stop: stopping server"),this.active=!1,this.log(`stop: detaching ${this.handlers.size} handlers`);for(let e of this.handlers)e.detach(!0);return this.handlers.clear(),this.server?new Promise(e=>{if(!this.server)return e();this.server.close(()=>{this.log("stop: server closed"),this.server=null,this.dispatchEvent(new CustomEvent("close")),e()})}):(this.log("stop: server not running, nothing to do"),Promise.resolve())}async handleConnection(e){let i={clientAddress:e.remoteAddress||"unknown",clientPort:e.remotePort||0};if(this.log(`handleConnection: new connection from ${i.clientAddress}:${i.clientPort}`),this.log(`handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`),!this.active){this.log("handleConnection: server not active, closing connection");try{e.end()}catch(r){this.log("handleConnection: error closing socket:",r)}return}if(this.handlers.size>=this.maxConnections){this.log("handleConnection: max connections reached, rejecting"),e.write(Buffer.from(`Too many connections | ||
| `)),e.end();return}let t=new c({queryQueue:this.queryQueue,closeOnDetach:!0,inspect:this.inspect,debug:this.debug,idleTimeout:this.idleTimeout});this.handlers.add(t),t.addEventListener("error",r=>{let o=r.detail;o?.message?.includes("ECONNRESET")?this.log(`handler #${t.handlerId}: client disconnected (ECONNRESET)`):o?.message?.includes("Idle timeout")?this.log(`handler #${t.handlerId}: idle timeout`):this.log(`handler #${t.handlerId}: error:`,o)}),t.addEventListener("close",()=>{this.log(`handler #${t.handlerId}: closed`),this.handlers.delete(t),this.log(`handleConnection: active connections: ${this.handlers.size}`)});try{await t.attach(e),this.dispatchEvent(new CustomEvent("connection",{detail:i}))}catch(r){this.log("handleConnection: error attaching socket:",r),this.handlers.delete(t),this.dispatchEvent(new CustomEvent("error",{detail:r}));try{e.end()}catch(o){this.log("handleConnection: error closing socket:",o)}}}getStats(){return{activeConnections:this.handlers.size,queuedQueries:this.queryQueue.getQueueLength(),maxConnections:this.maxConnections}}};0&&(module.exports={CONNECTION_QUEUE_TIMEOUT,PGLiteSocketHandler,PGLiteSocketServer}); | ||
| //# sourceMappingURL=index.cjs.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { PGlite } from '@electric-sql/pglite'\nimport { type Server, type Socket, createServer } from 'net'\n\n// Connection queue timeout in milliseconds\nexport const CONNECTION_QUEUE_TIMEOUT = 60000 // 60 seconds\n\n/**\n * Represents a queued query waiting for PGlite access\n */\ninterface QueuedQuery {\n handlerId: number\n message: Uint8Array\n resolve: (result: Uint8Array) => void\n reject: (error: Error) => void\n timestamp: number\n}\n\n/**\n * Global query queue manager\n * Ensures only one query executes at a time in PGlite\n */\nclass QueryQueueManager {\n private queue: QueuedQuery[] = []\n private processing = false\n private db: PGlite\n private debug: boolean\n private lastHandlerId: null | number = null\n\n constructor(db: PGlite, debug = false) {\n this.db = db\n this.debug = debug\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[QueryQueueManager] ${message}`, ...args)\n }\n }\n\n async enqueue(handlerId: number, message: Uint8Array): Promise<Uint8Array> {\n return new Promise((resolve, reject) => {\n const query: QueuedQuery = {\n handlerId,\n message,\n resolve,\n reject,\n timestamp: Date.now(),\n }\n\n this.queue.push(query)\n this.log(\n `enqueued query from handler #${handlerId}, queue size: ${this.queue.length}`,\n )\n\n // Process queue if not already processing\n if (!this.processing) {\n this.processQueue()\n }\n })\n }\n\n private async processQueue(): Promise<void> {\n if (this.processing || this.queue.length === 0) {\n return\n }\n\n this.processing = true\n\n while (this.queue.length > 0) {\n let query\n\n if (this.db.isInTransaction() && this.lastHandlerId) {\n const i = this.queue.findIndex(\n (q) => q.handlerId === this.lastHandlerId,\n )\n if (i === -1) {\n // we didn't find any other query from the same client!\n this.log(\n `transaction started, but no query from the same handler id found in queue`,\n this.lastHandlerId,\n )\n query = null\n } else {\n query = this.queue.splice(i, 1)[0]\n }\n } else {\n query = this.queue.shift()\n }\n if (!query) break\n\n const waitTime = Date.now() - query.timestamp\n this.log(\n `processing query from handler #${query.handlerId} (waited ${waitTime}ms)`,\n )\n\n try {\n // Execute the query with exclusive access to PGlite\n const result = await this.db.runExclusive(async () => {\n return await this.db.execProtocolRaw(query.message)\n })\n\n this.log(\n `query from handler #${query.handlerId} completed, ${result.length} bytes`,\n )\n this.lastHandlerId = query.handlerId\n query.resolve(result)\n } catch (error) {\n this.log(`query from handler #${query.handlerId} failed:`, error)\n query.reject(error as Error)\n }\n }\n\n this.processing = false\n this.log(`queue processing complete, queue length is`, this.queue.length)\n }\n\n getQueueLength(): number {\n return this.queue.length\n }\n\n clearQueueForHandler(handlerId: number): void {\n const before = this.queue.length\n this.queue = this.queue.filter((q) => {\n if (q.handlerId === handlerId) {\n q.reject(new Error('Handler disconnected'))\n return false\n }\n return true\n })\n const removed = before - this.queue.length\n if (removed > 0) {\n this.log(`cleared ${removed} queries for handler #${handlerId}`)\n }\n }\n\n async clearTransactionIfNeeded(handlerId: number): Promise<void> {\n if (this.db.isInTransaction() && this.lastHandlerId === handlerId) {\n await this.db.exec('ROLLBACK')\n this.lastHandlerId = null\n await this.processQueue()\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketHandler\n */\nexport interface PGLiteSocketHandlerOptions {\n /** The query queue manager */\n queryQueue: QueryQueueManager\n /** Whether to close the socket when detached (default: false) */\n closeOnDetach?: boolean\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n}\n\n/**\n * Handler for a single socket connection to PGlite\n * Each connection can remain open and send multiple queries\n */\nexport class PGLiteSocketHandler extends EventTarget {\n private queryQueue: QueryQueueManager\n private socket: Socket | null = null\n private active = false\n private closeOnDetach: boolean\n private inspect: boolean\n private debug: boolean\n private readonly id: number\n private messageBuffer: Buffer = Buffer.alloc(0)\n private idleTimer?: NodeJS.Timeout\n private idleTimeout: number\n private lastActivityTime: number = Date.now()\n\n // Static counter for generating unique handler IDs\n private static nextHandlerId = 1\n\n constructor(options: PGLiteSocketHandlerOptions) {\n super()\n this.queryQueue = options.queryQueue\n this.closeOnDetach = options.closeOnDetach ?? false\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.id = PGLiteSocketHandler.nextHandlerId++\n\n this.log('constructor: created new handler')\n }\n\n public get handlerId(): number {\n return this.id\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketHandler#${this.id}] ${message}`, ...args)\n }\n }\n\n public async attach(socket: Socket): Promise<PGLiteSocketHandler> {\n this.log(\n `attach: attaching socket from ${socket.remoteAddress}:${socket.remotePort}`,\n )\n\n if (this.socket) {\n throw new Error('Socket already attached')\n }\n\n this.socket = socket\n this.active = true\n this.lastActivityTime = Date.now()\n\n // Set up socket options\n socket.setNoDelay(true)\n\n // Set up idle timeout if configured\n if (this.idleTimeout > 0) {\n this.resetIdleTimer()\n }\n\n // Setup event handlers\n this.log(`attach: setting up socket event handlers`)\n\n socket.on('data', (data) => {\n this.lastActivityTime = Date.now()\n this.resetIdleTimer()\n\n setImmediate(async () => {\n try {\n await this.handleData(data)\n } catch (err) {\n this.log('socket on data error: ', err)\n this.handleError(err as Error)\n }\n })\n })\n\n socket.on('error', (err) => {\n setImmediate(() => this.handleError(err))\n })\n\n socket.on('close', () => {\n setImmediate(() => this.handleClose())\n })\n\n this.log(`attach: socket handler ready`)\n return this\n }\n\n private resetIdleTimer(): void {\n if (this.idleTimeout <= 0) return\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n }\n\n this.idleTimer = setTimeout(() => {\n const idleTime = Date.now() - this.lastActivityTime\n this.log(`idle timeout after ${idleTime}ms`)\n this.handleError(new Error('Idle timeout'))\n }, this.idleTimeout)\n }\n\n public async detach(close?: boolean): Promise<PGLiteSocketHandler> {\n this.log(`detach: detaching socket, close=${close ?? this.closeOnDetach}`)\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n this.idleTimer = undefined\n }\n\n // Clear any pending queries for this handler\n this.queryQueue.clearQueueForHandler(this.id)\n\n await this.queryQueue.clearTransactionIfNeeded(this.id)\n\n if (!this.socket) {\n this.log(`detach: no socket attached, nothing to do`)\n return this\n }\n\n // Remove all listeners\n this.socket.removeAllListeners('data')\n this.socket.removeAllListeners('error')\n this.socket.removeAllListeners('close')\n\n // Close the socket if requested\n if (close ?? this.closeOnDetach) {\n if (this.socket.writable) {\n this.log(`detach: closing socket`)\n try {\n this.socket.end()\n this.socket.destroy()\n } catch (err) {\n this.log(`detach: error closing socket:`, err)\n }\n }\n }\n\n this.socket = null\n this.active = false\n this.messageBuffer = Buffer.alloc(0)\n\n this.log(`detach: handler cleaned up`)\n return this\n }\n\n public get isAttached(): boolean {\n return this.socket !== null\n }\n\n private async handleData(data: Buffer): Promise<number> {\n if (!this.socket || !this.active) {\n this.log(`handleData: no active socket, ignoring data`)\n return 0\n }\n\n this.log(`handleData: received ${data.length} bytes`)\n\n // Append to buffer for message reassembly\n this.messageBuffer = Buffer.concat([this.messageBuffer, data])\n\n // Print the incoming data to the console\n this.inspectData('incoming', data)\n\n try {\n let totalProcessed = 0\n\n while (this.messageBuffer.length > 0) {\n // Determine message length\n let messageLength = 0\n let isComplete = false\n\n // Handle startup message (no type byte, just length)\n if (this.messageBuffer.length >= 4) {\n const firstInt = this.messageBuffer.readInt32BE(0)\n\n if (this.messageBuffer.length >= 8) {\n const secondInt = this.messageBuffer.readInt32BE(4)\n // PostgreSQL 3.0 protocol version\n if (secondInt === 196608 || secondInt === 0x00030000) {\n messageLength = firstInt\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n // Regular message (type byte + length)\n if (!isComplete && this.messageBuffer.length >= 5) {\n const msgLength = this.messageBuffer.readInt32BE(1)\n messageLength = 1 + msgLength\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n if (!isComplete || messageLength === 0) {\n this.log(\n `handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`,\n )\n break\n }\n\n // Extract and process complete message\n const message = this.messageBuffer.slice(0, messageLength)\n this.messageBuffer = this.messageBuffer.slice(messageLength)\n\n this.log(`handleData: processing message of ${message.length} bytes`)\n\n // Check if socket is still active before processing\n if (!this.active || !this.socket) {\n this.log(`handleData: socket no longer active, stopping processing`)\n break\n }\n\n // Queue the query for execution\n // This allows multiple connections to queue queries simultaneously\n const result = await this.queryQueue.enqueue(\n this.id,\n new Uint8Array(message),\n )\n\n this.log(`handleData: received ${result.length} bytes from PGlite`)\n\n // Print the outgoing data to the console\n this.inspectData('outgoing', result)\n\n // Send response if available\n if (\n result.length > 0 &&\n this.socket &&\n this.socket.writable &&\n this.active\n ) {\n await new Promise<number>((resolve, reject) => {\n this.log(`handleData: writing response to socket`)\n if (this.socket?.writable) {\n this.socket.write(Buffer.from(result), (err?: any) => {\n if (err) {\n this.log(`handleData: error writing to socket:`, err)\n reject(err)\n } else {\n this.log(`handleData: socket sent: ${result.length} bytes`)\n resolve(result.length)\n }\n })\n } else {\n this.log(`handleData: socket no longer writable`)\n resolve(0)\n }\n }).catch((writeErr) => {\n this.log(`handleData: failed to write to socket:`, writeErr)\n throw writeErr\n })\n }\n\n totalProcessed += result.length\n }\n\n // Emit data event with byte sizes\n this.dispatchEvent(\n new CustomEvent('data', {\n detail: { incoming: data.length, outgoing: totalProcessed },\n }),\n )\n\n return totalProcessed\n } catch (err) {\n this.log(`handleData: error processing data:`, err)\n throw err\n }\n }\n\n private handleError(err: Error): void {\n if (!this.active) {\n this.log(`handleError: handler not active, ignoring error`)\n return\n }\n\n // ECONNRESET is expected behavior when clients disconnect\n if (err.message?.includes('ECONNRESET')) {\n this.log(\n `handleError: client disconnected (ECONNRESET) - normal behavior`,\n )\n } else if (err.message?.includes('Idle timeout')) {\n this.log(`handleError: connection idle timeout`)\n } else {\n this.log(`handleError:`, err)\n }\n\n this.active = false\n\n // Emit error event\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n\n // Clean up\n this.detach(true)\n }\n\n private handleClose(): void {\n this.log(`handleClose: socket closed`)\n this.active = false\n this.dispatchEvent(new CustomEvent('close'))\n this.detach(false)\n }\n\n private inspectData(\n direction: 'incoming' | 'outgoing',\n data: Buffer | Uint8Array,\n ): void {\n if (!this.inspect) return\n console.log('-'.repeat(75))\n if (direction === 'incoming') {\n console.log('-> incoming', data.length, 'bytes')\n } else {\n console.log('<- outgoing', data.length, 'bytes')\n }\n\n for (let offset = 0; offset < data.length; offset += 16) {\n const chunkSize = Math.min(16, data.length - offset)\n\n let hexPart = ''\n for (let i = 0; i < 16; i++) {\n if (i < chunkSize) {\n const byte = data[offset + i]\n hexPart += byte.toString(16).padStart(2, '0') + ' '\n } else {\n hexPart += ' '\n }\n }\n\n let asciiPart = ''\n for (let i = 0; i < chunkSize; i++) {\n const byte = data[offset + i]\n asciiPart += byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : '.'\n }\n\n console.log(\n `${offset.toString(16).padStart(8, '0')} ${hexPart} ${asciiPart}`,\n )\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketServer\n */\nexport interface PGLiteSocketServerOptions {\n /** The PGlite database instance */\n db: PGlite\n /** The port to listen on (default: 5432) */\n port?: number\n /** The host to bind to (default: 127.0.0.1) */\n host?: string\n /** Unix socket path to bind to (default: undefined) */\n path?: string\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n /** Maximum concurrent connections (default: 100) */\n maxConnections?: number\n}\n\n/**\n * PGLite Socket Server with support for multiple concurrent connections\n * Connections remain open and queries are queued at the query level\n */\nexport class PGLiteSocketServer extends EventTarget {\n readonly db: PGlite\n private server: Server | null = null\n private port?: number\n private host?: string\n private path?: string\n private active = false\n private inspect: boolean\n private debug: boolean\n private idleTimeout: number\n private maxConnections: number\n private handlers: Set<PGLiteSocketHandler> = new Set()\n private queryQueue: QueryQueueManager\n\n constructor(options: PGLiteSocketServerOptions) {\n super()\n this.db = options.db\n if (options.path) {\n this.path = options.path\n } else {\n if (typeof options.port === 'number') {\n // Keep port undefined on port 0, will be set by the OS when we start the server.\n this.port = options.port ?? options.port\n } else {\n this.port = 5432\n }\n this.host = options.host || '127.0.0.1'\n }\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.maxConnections = options.maxConnections ?? 1\n\n // Create the shared query queue\n this.queryQueue = new QueryQueueManager(this.db, this.debug)\n\n this.log(`constructor: created server on ${this.getServerConn()}`)\n this.log(`constructor: max connections: ${this.maxConnections}`)\n if (this.idleTimeout > 0) {\n this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)\n }\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketServer] ${message}`, ...args)\n }\n }\n\n public async start(): Promise<void> {\n this.log(`start: starting server on ${this.getServerConn()}`)\n\n if (this.server) {\n throw new Error('Socket server already started')\n }\n\n // Ensure PGlite is ready before accepting connections\n await this.db.waitReady\n\n this.active = true\n this.server = createServer((socket) => {\n setImmediate(() => this.handleConnection(socket))\n })\n\n this.server.maxConnections = this.maxConnections\n\n return new Promise<void>((resolve, reject) => {\n if (!this.server) return reject(new Error('Server not initialized'))\n\n this.server.on('error', (err) => {\n this.log(`start: server error:`, err)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n if (!this.active) {\n reject(err)\n }\n })\n\n if (this.path) {\n this.server.listen(this.path, () => {\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { path: this.path },\n }),\n )\n resolve()\n })\n } else {\n const server = this.server\n server.listen(this.port, this.host, () => {\n const address = server.address()\n // We are not using pipes, so return type should be AddressInfo\n if (address === null || typeof address !== 'object') {\n throw Error('Expected address info')\n }\n // Assign the new port number\n this.port = address.port\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { port: this.port, host: this.host },\n }),\n )\n resolve()\n })\n }\n })\n }\n\n public getServerConn(): string {\n if (this.path) return this.path\n return `${this.host}:${this.port}`\n }\n\n public async stop(): Promise<void> {\n this.log(`stop: stopping server`)\n\n this.active = false\n\n // Detach all handlers\n this.log(`stop: detaching ${this.handlers.size} handlers`)\n for (const handler of this.handlers) {\n handler.detach(true)\n }\n this.handlers.clear()\n\n if (!this.server) {\n this.log(`stop: server not running, nothing to do`)\n return Promise.resolve()\n }\n\n return new Promise<void>((resolve) => {\n if (!this.server) return resolve()\n\n this.server.close(() => {\n this.log(`stop: server closed`)\n this.server = null\n this.dispatchEvent(new CustomEvent('close'))\n resolve()\n })\n })\n }\n\n private async handleConnection(socket: Socket): Promise<void> {\n const clientInfo = {\n clientAddress: socket.remoteAddress || 'unknown',\n clientPort: socket.remotePort || 0,\n }\n\n this.log(\n `handleConnection: new connection from ${clientInfo.clientAddress}:${clientInfo.clientPort}`,\n )\n this.log(\n `handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`,\n )\n\n if (!this.active) {\n this.log(`handleConnection: server not active, closing connection`)\n try {\n socket.end()\n } catch (err) {\n this.log(`handleConnection: error closing socket:`, err)\n }\n return\n }\n\n // Check connection limit\n if (this.handlers.size >= this.maxConnections) {\n this.log(`handleConnection: max connections reached, rejecting`)\n socket.write(Buffer.from('Too many connections\\n'))\n socket.end()\n return\n }\n\n // Create a new handler for this connection\n const handler = new PGLiteSocketHandler({\n queryQueue: this.queryQueue,\n closeOnDetach: true,\n inspect: this.inspect,\n debug: this.debug,\n idleTimeout: this.idleTimeout,\n })\n\n // Track this handler\n this.handlers.add(handler)\n\n // Handle errors\n handler.addEventListener('error', (event) => {\n const error = (event as CustomEvent<Error>).detail\n\n if (error?.message?.includes('ECONNRESET')) {\n this.log(\n `handler #${handler.handlerId}: client disconnected (ECONNRESET)`,\n )\n } else if (error?.message?.includes('Idle timeout')) {\n this.log(`handler #${handler.handlerId}: idle timeout`)\n } else {\n this.log(`handler #${handler.handlerId}: error:`, error)\n }\n })\n\n // Handle close event\n handler.addEventListener('close', () => {\n this.log(`handler #${handler.handlerId}: closed`)\n this.handlers.delete(handler)\n this.log(`handleConnection: active connections: ${this.handlers.size}`)\n })\n\n try {\n await handler.attach(socket)\n this.dispatchEvent(new CustomEvent('connection', { detail: clientInfo }))\n } catch (err) {\n this.log(`handleConnection: error attaching socket:`, err)\n this.handlers.delete(handler)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n try {\n socket.end()\n } catch (closeErr) {\n this.log(`handleConnection: error closing socket:`, closeErr)\n }\n }\n }\n\n public getStats() {\n return {\n activeConnections: this.handlers.size,\n queuedQueries: this.queryQueue.getQueueLength(),\n maxConnections: this.maxConnections,\n }\n }\n}\n"],"mappings":"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","names":["src_exports","__export","CONNECTION_QUEUE_TIMEOUT","PGLiteSocketHandler","PGLiteSocketServer","__toCommonJS","import_net","CONNECTION_QUEUE_TIMEOUT","QueryQueueManager","db","debug","message","args","handlerId","resolve","reject","query","q","waitTime","result","error","before","removed","_PGLiteSocketHandler","options","socket","data","err","idleTime","close","totalProcessed","messageLength","isComplete","firstInt","secondInt","writeErr","direction","offset","chunkSize","hexPart","i","byte","asciiPart","PGLiteSocketHandler","PGLiteSocketServer","server","address","handler","clientInfo","event","closeErr"]} | ||
| {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { PGlite } from '@electric-sql/pglite'\nimport { type Server, type Socket, createServer } from 'net'\n\n// Connection queue timeout in milliseconds\nexport const CONNECTION_QUEUE_TIMEOUT = 60000 // 60 seconds\n\n/**\n * Represents a queued query waiting for PGlite access\n */\ninterface QueuedQuery {\n handlerId: number\n message: Uint8Array\n resolve: (resultSize: number) => void\n reject: (error: Error) => void\n timestamp: number\n onData: (data: Uint8Array) => void\n}\n\n/**\n * Global query queue manager\n * Ensures only one query executes at a time in PGlite\n */\nclass QueryQueueManager {\n private queue: QueuedQuery[] = []\n private processing = false\n private db: PGlite\n private debug: boolean\n private lastHandlerId: null | number = null\n\n constructor(db: PGlite, debug = false) {\n this.db = db\n this.debug = debug\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[QueryQueueManager] ${message}`, ...args)\n }\n }\n\n async enqueue(\n handlerId: number,\n message: Uint8Array,\n onData: (data: Uint8Array) => void,\n ): Promise<number> {\n return new Promise((resolve, reject) => {\n const query: QueuedQuery = {\n handlerId,\n message,\n resolve,\n reject,\n timestamp: Date.now(),\n onData,\n }\n\n this.queue.push(query)\n this.log(\n `enqueued query from handler #${handlerId}, queue size: ${this.queue.length}`,\n )\n\n // Process queue if not already processing\n if (!this.processing) {\n this.processQueue()\n }\n })\n }\n\n private async processQueue(): Promise<void> {\n if (this.processing || this.queue.length === 0) {\n return\n }\n\n this.processing = true\n\n while (this.queue.length > 0) {\n let query\n\n if (this.db.isInTransaction() && this.lastHandlerId) {\n const i = this.queue.findIndex(\n (q) => q.handlerId === this.lastHandlerId,\n )\n if (i === -1) {\n // we didn't find any other query from the same client!\n this.log(\n `transaction started, but no query from the same handler id found in queue`,\n this.lastHandlerId,\n )\n query = null\n } else {\n query = this.queue.splice(i, 1)[0]\n }\n } else {\n query = this.queue.shift()\n }\n if (!query) break\n\n const waitTime = Date.now() - query.timestamp\n this.log(\n `processing query from handler #${query.handlerId} (waited ${waitTime}ms)`,\n )\n\n let result = 0\n try {\n // Execute the query with exclusive access to PGlite\n await this.db.runExclusive(async () => {\n return await this.db.execProtocolRawStream(query.message, {\n onRawData: (data) => {\n result += data.length\n query.onData(data)\n },\n })\n })\n } catch (error) {\n this.log(`query from handler #${query.handlerId} failed:`, error)\n query.reject(error as Error)\n return\n }\n\n this.log(\n `query from handler #${query.handlerId} completed, ${result} bytes`,\n )\n this.lastHandlerId = query.handlerId\n query.resolve(result)\n }\n\n this.processing = false\n this.log(`queue processing complete, queue length is`, this.queue.length)\n }\n\n getQueueLength(): number {\n return this.queue.length\n }\n\n clearQueueForHandler(handlerId: number): void {\n const before = this.queue.length\n this.queue = this.queue.filter((q) => {\n if (q.handlerId === handlerId) {\n q.reject(new Error('Handler disconnected'))\n return false\n }\n return true\n })\n const removed = before - this.queue.length\n if (removed > 0) {\n this.log(`cleared ${removed} queries for handler #${handlerId}`)\n }\n }\n\n async clearTransactionIfNeeded(handlerId: number): Promise<void> {\n if (this.db.isInTransaction() && this.lastHandlerId === handlerId) {\n await this.db.exec('ROLLBACK')\n this.lastHandlerId = null\n await this.processQueue()\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketHandler\n */\nexport interface PGLiteSocketHandlerOptions {\n /** The query queue manager */\n queryQueue: QueryQueueManager\n /** Whether to close the socket when detached (default: false) */\n closeOnDetach?: boolean\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n}\n\n/**\n * Handler for a single socket connection to PGlite\n * Each connection can remain open and send multiple queries\n */\nexport class PGLiteSocketHandler extends EventTarget {\n private queryQueue: QueryQueueManager\n private socket: Socket | null = null\n private active = false\n private closeOnDetach: boolean\n private inspect: boolean\n private debug: boolean\n private readonly id: number\n private messageBuffer: Buffer = Buffer.alloc(0)\n private idleTimer?: NodeJS.Timeout\n private idleTimeout: number\n private lastActivityTime: number = Date.now()\n\n // Static counter for generating unique handler IDs\n private static nextHandlerId = 1\n\n constructor(options: PGLiteSocketHandlerOptions) {\n super()\n this.queryQueue = options.queryQueue\n this.closeOnDetach = options.closeOnDetach ?? false\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.id = PGLiteSocketHandler.nextHandlerId++\n\n this.log('constructor: created new handler')\n }\n\n public get handlerId(): number {\n return this.id\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketHandler#${this.id}] ${message}`, ...args)\n }\n }\n\n public async attach(socket: Socket): Promise<PGLiteSocketHandler> {\n this.log(\n `attach: attaching socket from ${socket.remoteAddress}:${socket.remotePort}`,\n )\n\n if (this.socket) {\n throw new Error('Socket already attached')\n }\n\n this.socket = socket\n this.active = true\n this.lastActivityTime = Date.now()\n\n // Set up socket options\n socket.setNoDelay(true)\n\n // Set up idle timeout if configured\n if (this.idleTimeout > 0) {\n this.resetIdleTimer()\n }\n\n // Setup event handlers\n this.log(`attach: setting up socket event handlers`)\n\n socket.on('data', (data) => {\n this.lastActivityTime = Date.now()\n this.resetIdleTimer()\n\n setImmediate(async () => {\n try {\n await this.handleData(data)\n } catch (err) {\n this.log('socket on data error: ', err)\n this.handleError(err as Error)\n }\n })\n })\n\n socket.on('error', (err) => {\n setImmediate(() => this.handleError(err))\n })\n\n socket.on('close', () => {\n setImmediate(() => this.handleClose())\n })\n\n this.log(`attach: socket handler ready`)\n return this\n }\n\n private resetIdleTimer(): void {\n if (this.idleTimeout <= 0) return\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n }\n\n this.idleTimer = setTimeout(() => {\n const idleTime = Date.now() - this.lastActivityTime\n this.log(`idle timeout after ${idleTime}ms`)\n this.handleError(new Error('Idle timeout'))\n }, this.idleTimeout)\n }\n\n public async detach(close?: boolean): Promise<PGLiteSocketHandler> {\n this.log(`detach: detaching socket, close=${close ?? this.closeOnDetach}`)\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n this.idleTimer = undefined\n }\n\n // Clear any pending queries for this handler\n this.queryQueue.clearQueueForHandler(this.id)\n\n await this.queryQueue.clearTransactionIfNeeded(this.id)\n\n if (!this.socket) {\n this.log(`detach: no socket attached, nothing to do`)\n return this\n }\n\n // Remove all listeners\n this.socket.removeAllListeners('data')\n this.socket.removeAllListeners('error')\n this.socket.removeAllListeners('close')\n\n // Close the socket if requested\n if (close ?? this.closeOnDetach) {\n if (this.socket.writable) {\n this.log(`detach: closing socket`)\n try {\n this.socket.end()\n this.socket.destroy()\n } catch (err) {\n this.log(`detach: error closing socket:`, err)\n }\n }\n }\n\n this.socket = null\n this.active = false\n this.messageBuffer = Buffer.alloc(0)\n\n this.log(`detach: handler cleaned up`)\n return this\n }\n\n public get isAttached(): boolean {\n return this.socket !== null\n }\n\n private async handleData(data: Buffer): Promise<number> {\n if (!this.socket || !this.active) {\n this.log(`handleData: no active socket, ignoring data`)\n return 0\n }\n\n this.log(`handleData: received ${data.length} bytes`)\n\n // Append to buffer for message reassembly\n this.messageBuffer = Buffer.concat([this.messageBuffer, data])\n\n // Print the incoming data to the console\n this.inspectData('incoming', data)\n\n try {\n let totalProcessed = 0\n\n while (this.messageBuffer.length > 0) {\n // Determine message length\n let messageLength = 0\n let isComplete = false\n\n // Handle startup message (no type byte, just length)\n if (this.messageBuffer.length >= 4) {\n const firstInt = this.messageBuffer.readInt32BE(0)\n\n if (this.messageBuffer.length >= 8) {\n const secondInt = this.messageBuffer.readInt32BE(4)\n // PostgreSQL 3.0 protocol version\n if (secondInt === 196608 || secondInt === 0x00030000) {\n messageLength = firstInt\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n // Regular message (type byte + length)\n if (!isComplete && this.messageBuffer.length >= 5) {\n const msgLength = this.messageBuffer.readInt32BE(1)\n messageLength = 1 + msgLength\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n if (!isComplete || messageLength === 0) {\n this.log(\n `handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`,\n )\n break\n }\n\n // Extract and process complete message\n const message = this.messageBuffer.slice(0, messageLength)\n this.messageBuffer = this.messageBuffer.slice(messageLength)\n\n this.log(`handleData: processing message of ${message.length} bytes`)\n\n // Check if socket is still active before processing\n if (!this.active || !this.socket) {\n this.log(`handleData: socket no longer active, stopping processing`)\n break\n }\n\n let socketWriteError: any = undefined\n // Queue the query for execution\n // This allows multiple connections to queue queries simultaneously\n await this.queryQueue.enqueue(\n this.id,\n new Uint8Array(message),\n (data) => {\n this.log(`handleData: received ${data.length} bytes from PGlite`)\n\n // Print the outgoing data to the console\n this.inspectData('outgoing', data)\n\n // Send response if available\n if (\n data.length > 0 &&\n this.socket &&\n this.socket.writable &&\n this.active\n ) {\n // await new Promise<number>((resolve, reject) => {\n this.log(`handleData: writing response to socket`)\n if (this.socket?.writable) {\n this.socket.write(Buffer.from(data), (err?: any) => {\n if (err) {\n this.log(`handleData: error writing to socket:`, err)\n socketWriteError = err\n } else {\n this.log(`handleData: socket sent: ${data.length} bytes`)\n }\n })\n } else {\n this.log(`handleData: socket no longer writable`)\n }\n }\n totalProcessed += data.length\n },\n )\n if (socketWriteError) throw socketWriteError\n }\n\n // Emit data event with byte sizes\n this.dispatchEvent(\n new CustomEvent('data', {\n detail: { incoming: data.length, outgoing: totalProcessed },\n }),\n )\n\n return totalProcessed\n } catch (err) {\n this.log(`handleData: error processing data:`, err)\n throw err\n }\n }\n\n private handleError(err: Error): void {\n if (!this.active) {\n this.log(`handleError: handler not active, ignoring error`)\n return\n }\n\n // ECONNRESET is expected behavior when clients disconnect\n if (err.message?.includes('ECONNRESET')) {\n this.log(\n `handleError: client disconnected (ECONNRESET) - normal behavior`,\n )\n } else if (err.message?.includes('Idle timeout')) {\n this.log(`handleError: connection idle timeout`)\n } else {\n this.log(`handleError:`, err)\n }\n\n this.active = false\n\n // Emit error event\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n\n // Clean up\n this.detach(true)\n }\n\n private handleClose(): void {\n this.log(`handleClose: socket closed`)\n this.active = false\n this.dispatchEvent(new CustomEvent('close'))\n this.detach(false)\n }\n\n private inspectData(\n direction: 'incoming' | 'outgoing',\n data: Buffer | Uint8Array,\n ): void {\n if (!this.inspect) return\n console.log('-'.repeat(75))\n if (direction === 'incoming') {\n console.log('-> incoming', data.length, 'bytes')\n } else {\n console.log('<- outgoing', data.length, 'bytes')\n }\n\n for (let offset = 0; offset < data.length; offset += 16) {\n const chunkSize = Math.min(16, data.length - offset)\n\n let hexPart = ''\n for (let i = 0; i < 16; i++) {\n if (i < chunkSize) {\n const byte = data[offset + i]\n hexPart += byte.toString(16).padStart(2, '0') + ' '\n } else {\n hexPart += ' '\n }\n }\n\n let asciiPart = ''\n for (let i = 0; i < chunkSize; i++) {\n const byte = data[offset + i]\n asciiPart += byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : '.'\n }\n\n console.log(\n `${offset.toString(16).padStart(8, '0')} ${hexPart} ${asciiPart}`,\n )\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketServer\n */\nexport interface PGLiteSocketServerOptions {\n /** The PGlite database instance */\n db: PGlite\n /** The port to listen on (default: 5432) */\n port?: number\n /** The host to bind to (default: 127.0.0.1) */\n host?: string\n /** Unix socket path to bind to (default: undefined) */\n path?: string\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n /** Maximum concurrent connections (default: 100) */\n maxConnections?: number\n}\n\n/**\n * PGLite Socket Server with support for multiple concurrent connections\n * Connections remain open and queries are queued at the query level\n */\nexport class PGLiteSocketServer extends EventTarget {\n readonly db: PGlite\n private server: Server | null = null\n private port?: number\n private host?: string\n private path?: string\n private active = false\n private inspect: boolean\n private debug: boolean\n private idleTimeout: number\n private maxConnections: number\n private handlers: Set<PGLiteSocketHandler> = new Set()\n private queryQueue: QueryQueueManager\n\n constructor(options: PGLiteSocketServerOptions) {\n super()\n this.db = options.db\n if (options.path) {\n this.path = options.path\n } else {\n if (typeof options.port === 'number') {\n // Keep port undefined on port 0, will be set by the OS when we start the server.\n this.port = options.port ?? options.port\n } else {\n this.port = 5432\n }\n this.host = options.host || '127.0.0.1'\n }\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.maxConnections = options.maxConnections ?? 1\n\n // Create the shared query queue\n this.queryQueue = new QueryQueueManager(this.db, this.debug)\n\n this.log(`constructor: created server on ${this.getServerConn()}`)\n this.log(`constructor: max connections: ${this.maxConnections}`)\n if (this.idleTimeout > 0) {\n this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)\n }\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketServer] ${message}`, ...args)\n }\n }\n\n public async start(): Promise<void> {\n this.log(`start: starting server on ${this.getServerConn()}`)\n\n if (this.server) {\n throw new Error('Socket server already started')\n }\n\n // Ensure PGlite is ready before accepting connections\n await this.db.waitReady\n\n this.active = true\n this.server = createServer((socket) => {\n setImmediate(() => this.handleConnection(socket))\n })\n\n this.server.maxConnections = this.maxConnections\n\n return new Promise<void>((resolve, reject) => {\n if (!this.server) return reject(new Error('Server not initialized'))\n\n this.server.on('error', (err) => {\n this.log(`start: server error:`, err)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n if (!this.active) {\n reject(err)\n }\n })\n\n if (this.path) {\n this.server.listen(this.path, () => {\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { path: this.path },\n }),\n )\n resolve()\n })\n } else {\n const server = this.server\n server.listen(this.port, this.host, () => {\n const address = server.address()\n // We are not using pipes, so return type should be AddressInfo\n if (address === null || typeof address !== 'object') {\n throw Error('Expected address info')\n }\n // Assign the new port number\n this.port = address.port\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { port: this.port, host: this.host },\n }),\n )\n resolve()\n })\n }\n })\n }\n\n public getServerConn(): string {\n if (this.path) return this.path\n return `${this.host}:${this.port}`\n }\n\n public async stop(): Promise<void> {\n this.log(`stop: stopping server`)\n\n this.active = false\n\n // Detach all handlers\n this.log(`stop: detaching ${this.handlers.size} handlers`)\n for (const handler of this.handlers) {\n handler.detach(true)\n }\n this.handlers.clear()\n\n if (!this.server) {\n this.log(`stop: server not running, nothing to do`)\n return Promise.resolve()\n }\n\n return new Promise<void>((resolve) => {\n if (!this.server) return resolve()\n\n this.server.close(() => {\n this.log(`stop: server closed`)\n this.server = null\n this.dispatchEvent(new CustomEvent('close'))\n resolve()\n })\n })\n }\n\n private async handleConnection(socket: Socket): Promise<void> {\n const clientInfo = {\n clientAddress: socket.remoteAddress || 'unknown',\n clientPort: socket.remotePort || 0,\n }\n\n this.log(\n `handleConnection: new connection from ${clientInfo.clientAddress}:${clientInfo.clientPort}`,\n )\n this.log(\n `handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`,\n )\n\n if (!this.active) {\n this.log(`handleConnection: server not active, closing connection`)\n try {\n socket.end()\n } catch (err) {\n this.log(`handleConnection: error closing socket:`, err)\n }\n return\n }\n\n // Check connection limit\n if (this.handlers.size >= this.maxConnections) {\n this.log(`handleConnection: max connections reached, rejecting`)\n socket.write(Buffer.from('Too many connections\\n'))\n socket.end()\n return\n }\n\n // Create a new handler for this connection\n const handler = new PGLiteSocketHandler({\n queryQueue: this.queryQueue,\n closeOnDetach: true,\n inspect: this.inspect,\n debug: this.debug,\n idleTimeout: this.idleTimeout,\n })\n\n // Track this handler\n this.handlers.add(handler)\n\n // Handle errors\n handler.addEventListener('error', (event) => {\n const error = (event as CustomEvent<Error>).detail\n\n if (error?.message?.includes('ECONNRESET')) {\n this.log(\n `handler #${handler.handlerId}: client disconnected (ECONNRESET)`,\n )\n } else if (error?.message?.includes('Idle timeout')) {\n this.log(`handler #${handler.handlerId}: idle timeout`)\n } else {\n this.log(`handler #${handler.handlerId}: error:`, error)\n }\n })\n\n // Handle close event\n handler.addEventListener('close', () => {\n this.log(`handler #${handler.handlerId}: closed`)\n this.handlers.delete(handler)\n this.log(`handleConnection: active connections: ${this.handlers.size}`)\n })\n\n try {\n await handler.attach(socket)\n this.dispatchEvent(new CustomEvent('connection', { detail: clientInfo }))\n } catch (err) {\n this.log(`handleConnection: error attaching socket:`, err)\n this.handlers.delete(handler)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n try {\n socket.end()\n } catch (closeErr) {\n this.log(`handleConnection: error closing socket:`, closeErr)\n }\n }\n }\n\n public getStats() {\n return {\n activeConnections: this.handlers.size,\n queuedQueries: this.queryQueue.getQueueLength(),\n maxConnections: this.maxConnections,\n }\n }\n}\n"],"mappings":"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","names":["src_exports","__export","CONNECTION_QUEUE_TIMEOUT","PGLiteSocketHandler","PGLiteSocketServer","__toCommonJS","import_net","CONNECTION_QUEUE_TIMEOUT","QueryQueueManager","db","debug","message","args","handlerId","onData","resolve","reject","query","i","q","waitTime","result","data","error","before","removed","_PGLiteSocketHandler","options","socket","err","idleTime","close","totalProcessed","messageLength","isComplete","firstInt","secondInt","socketWriteError","direction","offset","chunkSize","hexPart","byte","asciiPart","PGLiteSocketHandler","PGLiteSocketServer","server","address","handler","clientInfo","event","closeErr"]} |
+1
-1
@@ -17,3 +17,3 @@ import { PGlite } from '@electric-sql/pglite'; | ||
| private log; | ||
| enqueue(handlerId: number, message: Uint8Array): Promise<Uint8Array>; | ||
| enqueue(handlerId: number, message: Uint8Array, onData: (data: Uint8Array) => void): Promise<number>; | ||
| private processQueue; | ||
@@ -20,0 +20,0 @@ getQueueLength(): number; |
+1
-1
@@ -17,3 +17,3 @@ import { PGlite } from '@electric-sql/pglite'; | ||
| private log; | ||
| enqueue(handlerId: number, message: Uint8Array): Promise<Uint8Array>; | ||
| enqueue(handlerId: number, message: Uint8Array, onData: (data: Uint8Array) => void): Promise<number>; | ||
| private processQueue; | ||
@@ -20,0 +20,0 @@ getQueueLength(): number; |
+1
-1
@@ -1,2 +0,2 @@ | ||
| import{a,b,c}from"./chunk-JGAU6COP.js";export{a as CONNECTION_QUEUE_TIMEOUT,b as PGLiteSocketHandler,c as PGLiteSocketServer}; | ||
| import{a,b,c}from"./chunk-NSUMFCRM.js";export{a as CONNECTION_QUEUE_TIMEOUT,b as PGLiteSocketHandler,c as PGLiteSocketServer}; | ||
| //# sourceMappingURL=index.js.map |
| #!/usr/bin/env node | ||
| "use strict";var y=require("@electric-sql/pglite");var b=require("net");var p=class{constructor(s,e=!1){this.queue=[];this.processing=!1;this.lastHandlerId=null;this.db=s,this.debug=e}log(s,...e){this.debug&&console.log(`[QueryQueueManager] ${s}`,...e)}async enqueue(s,e){return new Promise((t,i)=>{let r={handlerId:s,message:e,resolve:t,reject:i,timestamp:Date.now()};this.queue.push(r),this.log(`enqueued query from handler #${s}, queue size: ${this.queue.length}`),this.processing||this.processQueue()})}async processQueue(){if(!(this.processing||this.queue.length===0)){for(this.processing=!0;this.queue.length>0;){let s;if(this.db.isInTransaction()&&this.lastHandlerId){let t=this.queue.findIndex(i=>i.handlerId===this.lastHandlerId);t===-1?(this.log("transaction started, but no query from the same handler id found in queue",this.lastHandlerId),s=null):s=this.queue.splice(t,1)[0]}else s=this.queue.shift();if(!s)break;let e=Date.now()-s.timestamp;this.log(`processing query from handler #${s.handlerId} (waited ${e}ms)`);try{let t=await this.db.runExclusive(async()=>await this.db.execProtocolRaw(s.message));this.log(`query from handler #${s.handlerId} completed, ${t.length} bytes`),this.lastHandlerId=s.handlerId,s.resolve(t)}catch(t){this.log(`query from handler #${s.handlerId} failed:`,t),s.reject(t)}}this.processing=!1,this.log("queue processing complete, queue length is",this.queue.length)}}getQueueLength(){return this.queue.length}clearQueueForHandler(s){let e=this.queue.length;this.queue=this.queue.filter(i=>i.handlerId===s?(i.reject(new Error("Handler disconnected")),!1):!0);let t=e-this.queue.length;t>0&&this.log(`cleared ${t} queries for handler #${s}`)}async clearTransactionIfNeeded(s){this.db.isInTransaction()&&this.lastHandlerId===s&&(await this.db.exec("ROLLBACK"),this.lastHandlerId=null,await this.processQueue())}},u=class u extends EventTarget{constructor(e){super();this.socket=null;this.active=!1;this.messageBuffer=Buffer.alloc(0);this.lastActivityTime=Date.now();this.queryQueue=e.queryQueue,this.closeOnDetach=e.closeOnDetach??!1,this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.id=u.nextHandlerId++,this.log("constructor: created new handler")}get handlerId(){return this.id}log(e,...t){this.debug&&console.log(`[PGLiteSocketHandler#${this.id}] ${e}`,...t)}async attach(e){if(this.log(`attach: attaching socket from ${e.remoteAddress}:${e.remotePort}`),this.socket)throw new Error("Socket already attached");return this.socket=e,this.active=!0,this.lastActivityTime=Date.now(),e.setNoDelay(!0),this.idleTimeout>0&&this.resetIdleTimer(),this.log("attach: setting up socket event handlers"),e.on("data",t=>{this.lastActivityTime=Date.now(),this.resetIdleTimer(),setImmediate(async()=>{try{await this.handleData(t)}catch(i){this.log("socket on data error: ",i),this.handleError(i)}})}),e.on("error",t=>{setImmediate(()=>this.handleError(t))}),e.on("close",()=>{setImmediate(()=>this.handleClose())}),this.log("attach: socket handler ready"),this}resetIdleTimer(){this.idleTimeout<=0||(this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>{let e=Date.now()-this.lastActivityTime;this.log(`idle timeout after ${e}ms`),this.handleError(new Error("Idle timeout"))},this.idleTimeout))}async detach(e){if(this.log(`detach: detaching socket, close=${e??this.closeOnDetach}`),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=void 0),this.queryQueue.clearQueueForHandler(this.id),await this.queryQueue.clearTransactionIfNeeded(this.id),!this.socket)return this.log("detach: no socket attached, nothing to do"),this;if(this.socket.removeAllListeners("data"),this.socket.removeAllListeners("error"),this.socket.removeAllListeners("close"),(e??this.closeOnDetach)&&this.socket.writable){this.log("detach: closing socket");try{this.socket.end(),this.socket.destroy()}catch(t){this.log("detach: error closing socket:",t)}}return this.socket=null,this.active=!1,this.messageBuffer=Buffer.alloc(0),this.log("detach: handler cleaned up"),this}get isAttached(){return this.socket!==null}async handleData(e){if(!this.socket||!this.active)return this.log("handleData: no active socket, ignoring data"),0;this.log(`handleData: received ${e.length} bytes`),this.messageBuffer=Buffer.concat([this.messageBuffer,e]),this.inspectData("incoming",e);try{let t=0;for(;this.messageBuffer.length>0;){let i=0,r=!1;if(this.messageBuffer.length>=4){let o=this.messageBuffer.readInt32BE(0);if(this.messageBuffer.length>=8){let l=this.messageBuffer.readInt32BE(4);(l===196608||l===196608)&&(i=o,r=this.messageBuffer.length>=i)}!r&&this.messageBuffer.length>=5&&(i=1+this.messageBuffer.readInt32BE(1),r=this.messageBuffer.length>=i)}if(!r||i===0){this.log(`handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`);break}let n=this.messageBuffer.slice(0,i);if(this.messageBuffer=this.messageBuffer.slice(i),this.log(`handleData: processing message of ${n.length} bytes`),!this.active||!this.socket){this.log("handleData: socket no longer active, stopping processing");break}let a=await this.queryQueue.enqueue(this.id,new Uint8Array(n));this.log(`handleData: received ${a.length} bytes from PGlite`),this.inspectData("outgoing",a),a.length>0&&this.socket&&this.socket.writable&&this.active&&await new Promise((o,l)=>{this.log("handleData: writing response to socket"),this.socket?.writable?this.socket.write(Buffer.from(a),m=>{m?(this.log("handleData: error writing to socket:",m),l(m)):(this.log(`handleData: socket sent: ${a.length} bytes`),o(a.length))}):(this.log("handleData: socket no longer writable"),o(0))}).catch(o=>{throw this.log("handleData: failed to write to socket:",o),o}),t+=a.length}return this.dispatchEvent(new CustomEvent("data",{detail:{incoming:e.length,outgoing:t}})),t}catch(t){throw this.log("handleData: error processing data:",t),t}}handleError(e){if(!this.active){this.log("handleError: handler not active, ignoring error");return}e.message?.includes("ECONNRESET")?this.log("handleError: client disconnected (ECONNRESET) - normal behavior"):e.message?.includes("Idle timeout")?this.log("handleError: connection idle timeout"):this.log("handleError:",e),this.active=!1,this.dispatchEvent(new CustomEvent("error",{detail:e})),this.detach(!0)}handleClose(){this.log("handleClose: socket closed"),this.active=!1,this.dispatchEvent(new CustomEvent("close")),this.detach(!1)}inspectData(e,t){if(this.inspect){console.log("-".repeat(75)),console.log(e==="incoming"?"-> incoming":"<- outgoing",t.length,"bytes");for(let i=0;i<t.length;i+=16){let r=Math.min(16,t.length-i),n="";for(let o=0;o<16;o++)if(o<r){let l=t[i+o];n+=l.toString(16).padStart(2,"0")+" "}else n+=" ";let a="";for(let o=0;o<r;o++){let l=t[i+o];a+=l>=32&&l<=126?String.fromCharCode(l):"."}console.log(`${i.toString(16).padStart(8,"0")} ${n} ${a}`)}}}};u.nextHandlerId=1;var f=u,d=class extends EventTarget{constructor(e){super();this.server=null;this.active=!1;this.handlers=new Set;this.db=e.db,e.path?this.path=e.path:(typeof e.port=="number"?this.port=e.port??e.port:this.port=5432,this.host=e.host||"127.0.0.1"),this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.maxConnections=e.maxConnections??1,this.queryQueue=new p(this.db,this.debug),this.log(`constructor: created server on ${this.getServerConn()}`),this.log(`constructor: max connections: ${this.maxConnections}`),this.idleTimeout>0&&this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)}log(e,...t){this.debug&&console.log(`[PGLiteSocketServer] ${e}`,...t)}async start(){if(this.log(`start: starting server on ${this.getServerConn()}`),this.server)throw new Error("Socket server already started");return await this.db.waitReady,this.active=!0,this.server=(0,b.createServer)(e=>{setImmediate(()=>this.handleConnection(e))}),this.server.maxConnections=this.maxConnections,new Promise((e,t)=>{if(!this.server)return t(new Error("Server not initialized"));if(this.server.on("error",i=>{this.log("start: server error:",i),this.dispatchEvent(new CustomEvent("error",{detail:i})),this.active||t(i)}),this.path)this.server.listen(this.path,()=>{this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{path:this.path}})),e()});else{let i=this.server;i.listen(this.port,this.host,()=>{let r=i.address();if(r===null||typeof r!="object")throw Error("Expected address info");this.port=r.port,this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{port:this.port,host:this.host}})),e()})}})}getServerConn(){return this.path?this.path:`${this.host}:${this.port}`}async stop(){this.log("stop: stopping server"),this.active=!1,this.log(`stop: detaching ${this.handlers.size} handlers`);for(let e of this.handlers)e.detach(!0);return this.handlers.clear(),this.server?new Promise(e=>{if(!this.server)return e();this.server.close(()=>{this.log("stop: server closed"),this.server=null,this.dispatchEvent(new CustomEvent("close")),e()})}):(this.log("stop: server not running, nothing to do"),Promise.resolve())}async handleConnection(e){let t={clientAddress:e.remoteAddress||"unknown",clientPort:e.remotePort||0};if(this.log(`handleConnection: new connection from ${t.clientAddress}:${t.clientPort}`),this.log(`handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`),!this.active){this.log("handleConnection: server not active, closing connection");try{e.end()}catch(r){this.log("handleConnection: error closing socket:",r)}return}if(this.handlers.size>=this.maxConnections){this.log("handleConnection: max connections reached, rejecting"),e.write(Buffer.from(`Too many connections | ||
| `)),e.end();return}let i=new f({queryQueue:this.queryQueue,closeOnDetach:!0,inspect:this.inspect,debug:this.debug,idleTimeout:this.idleTimeout});this.handlers.add(i),i.addEventListener("error",r=>{let n=r.detail;n?.message?.includes("ECONNRESET")?this.log(`handler #${i.handlerId}: client disconnected (ECONNRESET)`):n?.message?.includes("Idle timeout")?this.log(`handler #${i.handlerId}: idle timeout`):this.log(`handler #${i.handlerId}: error:`,n)}),i.addEventListener("close",()=>{this.log(`handler #${i.handlerId}: closed`),this.handlers.delete(i),this.log(`handleConnection: active connections: ${this.handlers.size}`)});try{await i.attach(e),this.dispatchEvent(new CustomEvent("connection",{detail:t}))}catch(r){this.log("handleConnection: error attaching socket:",r),this.handlers.delete(i),this.dispatchEvent(new CustomEvent("error",{detail:r}));try{e.end()}catch(n){this.log("handleConnection: error closing socket:",n)}}}getStats(){return{activeConnections:this.handlers.size,queuedQueries:this.queryQueue.getQueueLength(),maxConnections:this.maxConnections}}};var w=require("util"),E=require("child_process"),h=(0,w.parseArgs)({options:{db:{type:"string",short:"d",default:"memory://",help:"Database path (relative or absolute). Use memory:// for in-memory database."},port:{type:"string",short:"p",default:"5432",help:"Port to listen on"},host:{type:"string",short:"h",default:"127.0.0.1",help:"Host to bind to"},path:{type:"string",short:"u",default:void 0,help:"unix socket to bind to. Takes precedence over host:port"},debug:{type:"string",short:"v",default:"0",help:"Debug level (0-5)"},extensions:{type:"string",short:"e",default:void 0,help:"Comma-separated list of extensions to load (e.g., vector,pgcrypto)"},run:{type:"string",short:"r",default:void 0,help:"Command to run after server starts"},"include-database-url":{type:"boolean",default:!1,help:"Include DATABASE_URL in the environment of the subprocess"},"shutdown-timeout":{type:"string",default:"5000",help:"Timeout in milliseconds for graceful subprocess shutdown (default: 5000)"},"max-connections":{type:"string",short:"m",default:"1",help:"Maximum concurrent connections (default: 1)"},help:{type:"boolean",short:"?",default:!1,help:"Show help"}}}),C=`PGlite Socket Server | ||
| "use strict";var b=require("@electric-sql/pglite");var v=require("net");var m=class{constructor(i,e=!1){this.queue=[];this.processing=!1;this.lastHandlerId=null;this.db=i,this.debug=e}log(i,...e){this.debug&&console.log(`[QueryQueueManager] ${i}`,...e)}async enqueue(i,e,t){return new Promise((s,r)=>{let n={handlerId:i,message:e,resolve:s,reject:r,timestamp:Date.now(),onData:t};this.queue.push(n),this.log(`enqueued query from handler #${i}, queue size: ${this.queue.length}`),this.processing||this.processQueue()})}async processQueue(){if(!(this.processing||this.queue.length===0)){for(this.processing=!0;this.queue.length>0;){let i;if(this.db.isInTransaction()&&this.lastHandlerId){let s=this.queue.findIndex(r=>r.handlerId===this.lastHandlerId);s===-1?(this.log("transaction started, but no query from the same handler id found in queue",this.lastHandlerId),i=null):i=this.queue.splice(s,1)[0]}else i=this.queue.shift();if(!i)break;let e=Date.now()-i.timestamp;this.log(`processing query from handler #${i.handlerId} (waited ${e}ms)`);let t=0;try{await this.db.runExclusive(async()=>await this.db.execProtocolRawStream(i.message,{onRawData:s=>{t+=s.length,i.onData(s)}}))}catch(s){this.log(`query from handler #${i.handlerId} failed:`,s),i.reject(s);return}this.log(`query from handler #${i.handlerId} completed, ${t} bytes`),this.lastHandlerId=i.handlerId,i.resolve(t)}this.processing=!1,this.log("queue processing complete, queue length is",this.queue.length)}}getQueueLength(){return this.queue.length}clearQueueForHandler(i){let e=this.queue.length;this.queue=this.queue.filter(s=>s.handlerId===i?(s.reject(new Error("Handler disconnected")),!1):!0);let t=e-this.queue.length;t>0&&this.log(`cleared ${t} queries for handler #${i}`)}async clearTransactionIfNeeded(i){this.db.isInTransaction()&&this.lastHandlerId===i&&(await this.db.exec("ROLLBACK"),this.lastHandlerId=null,await this.processQueue())}},u=class u extends EventTarget{constructor(e){super();this.socket=null;this.active=!1;this.messageBuffer=Buffer.alloc(0);this.lastActivityTime=Date.now();this.queryQueue=e.queryQueue,this.closeOnDetach=e.closeOnDetach??!1,this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.id=u.nextHandlerId++,this.log("constructor: created new handler")}get handlerId(){return this.id}log(e,...t){this.debug&&console.log(`[PGLiteSocketHandler#${this.id}] ${e}`,...t)}async attach(e){if(this.log(`attach: attaching socket from ${e.remoteAddress}:${e.remotePort}`),this.socket)throw new Error("Socket already attached");return this.socket=e,this.active=!0,this.lastActivityTime=Date.now(),e.setNoDelay(!0),this.idleTimeout>0&&this.resetIdleTimer(),this.log("attach: setting up socket event handlers"),e.on("data",t=>{this.lastActivityTime=Date.now(),this.resetIdleTimer(),setImmediate(async()=>{try{await this.handleData(t)}catch(s){this.log("socket on data error: ",s),this.handleError(s)}})}),e.on("error",t=>{setImmediate(()=>this.handleError(t))}),e.on("close",()=>{setImmediate(()=>this.handleClose())}),this.log("attach: socket handler ready"),this}resetIdleTimer(){this.idleTimeout<=0||(this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>{let e=Date.now()-this.lastActivityTime;this.log(`idle timeout after ${e}ms`),this.handleError(new Error("Idle timeout"))},this.idleTimeout))}async detach(e){if(this.log(`detach: detaching socket, close=${e??this.closeOnDetach}`),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=void 0),this.queryQueue.clearQueueForHandler(this.id),await this.queryQueue.clearTransactionIfNeeded(this.id),!this.socket)return this.log("detach: no socket attached, nothing to do"),this;if(this.socket.removeAllListeners("data"),this.socket.removeAllListeners("error"),this.socket.removeAllListeners("close"),(e??this.closeOnDetach)&&this.socket.writable){this.log("detach: closing socket");try{this.socket.end(),this.socket.destroy()}catch(t){this.log("detach: error closing socket:",t)}}return this.socket=null,this.active=!1,this.messageBuffer=Buffer.alloc(0),this.log("detach: handler cleaned up"),this}get isAttached(){return this.socket!==null}async handleData(e){if(!this.socket||!this.active)return this.log("handleData: no active socket, ignoring data"),0;this.log(`handleData: received ${e.length} bytes`),this.messageBuffer=Buffer.concat([this.messageBuffer,e]),this.inspectData("incoming",e);try{let t=0;for(;this.messageBuffer.length>0;){let s=0,r=!1;if(this.messageBuffer.length>=4){let o=this.messageBuffer.readInt32BE(0);if(this.messageBuffer.length>=8){let a=this.messageBuffer.readInt32BE(4);(a===196608||a===196608)&&(s=o,r=this.messageBuffer.length>=s)}!r&&this.messageBuffer.length>=5&&(s=1+this.messageBuffer.readInt32BE(1),r=this.messageBuffer.length>=s)}if(!r||s===0){this.log(`handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`);break}let n=this.messageBuffer.slice(0,s);if(this.messageBuffer=this.messageBuffer.slice(s),this.log(`handleData: processing message of ${n.length} bytes`),!this.active||!this.socket){this.log("handleData: socket no longer active, stopping processing");break}let c;if(await this.queryQueue.enqueue(this.id,new Uint8Array(n),o=>{this.log(`handleData: received ${o.length} bytes from PGlite`),this.inspectData("outgoing",o),o.length>0&&this.socket&&this.socket.writable&&this.active&&(this.log("handleData: writing response to socket"),this.socket?.writable?this.socket.write(Buffer.from(o),a=>{a?(this.log("handleData: error writing to socket:",a),c=a):this.log(`handleData: socket sent: ${o.length} bytes`)}):this.log("handleData: socket no longer writable")),t+=o.length}),c)throw c}return this.dispatchEvent(new CustomEvent("data",{detail:{incoming:e.length,outgoing:t}})),t}catch(t){throw this.log("handleData: error processing data:",t),t}}handleError(e){if(!this.active){this.log("handleError: handler not active, ignoring error");return}e.message?.includes("ECONNRESET")?this.log("handleError: client disconnected (ECONNRESET) - normal behavior"):e.message?.includes("Idle timeout")?this.log("handleError: connection idle timeout"):this.log("handleError:",e),this.active=!1,this.dispatchEvent(new CustomEvent("error",{detail:e})),this.detach(!0)}handleClose(){this.log("handleClose: socket closed"),this.active=!1,this.dispatchEvent(new CustomEvent("close")),this.detach(!1)}inspectData(e,t){if(this.inspect){console.log("-".repeat(75)),console.log(e==="incoming"?"-> incoming":"<- outgoing",t.length,"bytes");for(let s=0;s<t.length;s+=16){let r=Math.min(16,t.length-s),n="";for(let o=0;o<16;o++)if(o<r){let a=t[s+o];n+=a.toString(16).padStart(2,"0")+" "}else n+=" ";let c="";for(let o=0;o<r;o++){let a=t[s+o];c+=a>=32&&a<=126?String.fromCharCode(a):"."}console.log(`${s.toString(16).padStart(8,"0")} ${n} ${c}`)}}}};u.nextHandlerId=1;var p=u,d=class extends EventTarget{constructor(e){super();this.server=null;this.active=!1;this.handlers=new Set;this.db=e.db,e.path?this.path=e.path:(typeof e.port=="number"?this.port=e.port??e.port:this.port=5432,this.host=e.host||"127.0.0.1"),this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.maxConnections=e.maxConnections??1,this.queryQueue=new m(this.db,this.debug),this.log(`constructor: created server on ${this.getServerConn()}`),this.log(`constructor: max connections: ${this.maxConnections}`),this.idleTimeout>0&&this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)}log(e,...t){this.debug&&console.log(`[PGLiteSocketServer] ${e}`,...t)}async start(){if(this.log(`start: starting server on ${this.getServerConn()}`),this.server)throw new Error("Socket server already started");return await this.db.waitReady,this.active=!0,this.server=(0,v.createServer)(e=>{setImmediate(()=>this.handleConnection(e))}),this.server.maxConnections=this.maxConnections,new Promise((e,t)=>{if(!this.server)return t(new Error("Server not initialized"));if(this.server.on("error",s=>{this.log("start: server error:",s),this.dispatchEvent(new CustomEvent("error",{detail:s})),this.active||t(s)}),this.path)this.server.listen(this.path,()=>{this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{path:this.path}})),e()});else{let s=this.server;s.listen(this.port,this.host,()=>{let r=s.address();if(r===null||typeof r!="object")throw Error("Expected address info");this.port=r.port,this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{port:this.port,host:this.host}})),e()})}})}getServerConn(){return this.path?this.path:`${this.host}:${this.port}`}async stop(){this.log("stop: stopping server"),this.active=!1,this.log(`stop: detaching ${this.handlers.size} handlers`);for(let e of this.handlers)e.detach(!0);return this.handlers.clear(),this.server?new Promise(e=>{if(!this.server)return e();this.server.close(()=>{this.log("stop: server closed"),this.server=null,this.dispatchEvent(new CustomEvent("close")),e()})}):(this.log("stop: server not running, nothing to do"),Promise.resolve())}async handleConnection(e){let t={clientAddress:e.remoteAddress||"unknown",clientPort:e.remotePort||0};if(this.log(`handleConnection: new connection from ${t.clientAddress}:${t.clientPort}`),this.log(`handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`),!this.active){this.log("handleConnection: server not active, closing connection");try{e.end()}catch(r){this.log("handleConnection: error closing socket:",r)}return}if(this.handlers.size>=this.maxConnections){this.log("handleConnection: max connections reached, rejecting"),e.write(Buffer.from(`Too many connections | ||
| `)),e.end();return}let s=new p({queryQueue:this.queryQueue,closeOnDetach:!0,inspect:this.inspect,debug:this.debug,idleTimeout:this.idleTimeout});this.handlers.add(s),s.addEventListener("error",r=>{let n=r.detail;n?.message?.includes("ECONNRESET")?this.log(`handler #${s.handlerId}: client disconnected (ECONNRESET)`):n?.message?.includes("Idle timeout")?this.log(`handler #${s.handlerId}: idle timeout`):this.log(`handler #${s.handlerId}: error:`,n)}),s.addEventListener("close",()=>{this.log(`handler #${s.handlerId}: closed`),this.handlers.delete(s),this.log(`handleConnection: active connections: ${this.handlers.size}`)});try{await s.attach(e),this.dispatchEvent(new CustomEvent("connection",{detail:t}))}catch(r){this.log("handleConnection: error attaching socket:",r),this.handlers.delete(s),this.dispatchEvent(new CustomEvent("error",{detail:r}));try{e.end()}catch(n){this.log("handleConnection: error closing socket:",n)}}}getStats(){return{activeConnections:this.handlers.size,queuedQueries:this.queryQueue.getQueueLength(),maxConnections:this.maxConnections}}};var y=require("util"),w=require("child_process"),l=(0,y.parseArgs)({options:{db:{type:"string",short:"d",default:"memory://",help:"Database path (relative or absolute). Use memory:// for in-memory database."},port:{type:"string",short:"p",default:"5432",help:"Port to listen on"},host:{type:"string",short:"h",default:"127.0.0.1",help:"Host to bind to"},path:{type:"string",short:"u",default:void 0,help:"unix socket to bind to. Takes precedence over host:port"},debug:{type:"string",short:"v",default:"0",help:"Debug level (0-5)"},extensions:{type:"string",short:"e",default:void 0,help:"Comma-separated list of extensions to load (e.g., vector,pgcrypto,postgis etc.)"},run:{type:"string",short:"r",default:void 0,help:"Command to run after server starts"},"include-database-url":{type:"boolean",default:!1,help:"Include DATABASE_URL in the environment of the subprocess"},"shutdown-timeout":{type:"string",default:"5000",help:"Timeout in milliseconds for graceful subprocess shutdown (default: 5000)"},"max-connections":{type:"string",short:"m",default:"1",help:"Maximum concurrent connections (default: 1)"},help:{type:"boolean",short:"?",default:!1,help:"Show help"}}}),x=`PGlite Socket Server | ||
| Usage: pglite-server [options] | ||
@@ -19,4 +19,4 @@ | ||
| -m, --max-connections=N Maximum concurrent connections (default is no concurrency: 1) | ||
| `,g=class{constructor(s){this.db=null;this.server=null;this.subprocessManager=null;this.config=s}static parseConfig(){let s=h.values.extensions;return{dbPath:h.values.db,port:parseInt(h.values.port,10),host:h.values.host,path:h.values.path,debugLevel:parseInt(h.values.debug,10),extensionNames:s?s.split(",").map(e=>e.trim()):void 0,runCommand:h.values.run,includeDatabaseUrl:h.values["include-database-url"],shutdownTimeout:parseInt(h.values["shutdown-timeout"],10),maxConnections:parseInt(h.values["max-connections"],10)}}createDatabaseUrl(){let{host:s,port:e,path:t}=this.config;if(t){let i=t.endsWith("/.s.PGSQL.5432")?t.slice(0,-13):t;return`postgresql://postgres:postgres@/postgres?host=${encodeURIComponent(i)}`}else return`postgresql://postgres:postgres@${s}:${e}/postgres`}async importExtensions(){if(!this.config.extensionNames?.length)return;let s={},e=["vector","live","pg_hashids","pg_ivm","pg_uuidv7","pgtap"];for(let t of this.config.extensionNames){let i=null;try{if(t.includes(":")){let[r,n]=t.split(":");if(!r||!n)throw new Error(`Invalid extension format '${t}'. Expected: package/path:exportedName`);i=(await import(r))[n],i&&(s[n]=i,console.log(`Imported extension '${n}' from '${r}'`))}else if(e.includes(t))i=(await import(`@electric-sql/pglite/${t}`))[t],i&&(s[t]=i,console.log(`Imported extension: ${t}`));else{try{i=(await import(`@electric-sql/pglite/contrib/${t}`))[t]}catch{i=(await import(`@electric-sql/pglite-${t}`))[t]}i&&(s[t]=i,console.log(`Imported extension: ${t}`))}}catch(r){throw console.error(`Failed to import extension '${t}':`,r),new Error(`Failed to import extension '${t}'`)}}return Object.keys(s).length>0?s:void 0}async initializeDatabase(){console.log(`Initializing PGLite with database: ${this.config.dbPath}`),console.log(`Debug level: ${this.config.debugLevel}`);let s=await this.importExtensions();this.db=new y.PGlite(this.config.dbPath,{debug:this.config.debugLevel,extensions:s}),await this.db.waitReady,console.log("PGlite database initialized")}setupServerEventHandlers(){if(!this.server||!this.subprocessManager)throw new Error("Server or subprocess manager not initialized");this.server.addEventListener("listening",s=>{let e=s.detail;if(console.log(`PGLiteSocketServer listening on ${JSON.stringify(e)}`),this.config.runCommand&&this.subprocessManager){let t=this.createDatabaseUrl();this.subprocessManager.spawn(this.config.runCommand,t,this.config.includeDatabaseUrl)}}),this.server.addEventListener("connection",s=>{let{clientAddress:e,clientPort:t}=s.detail;console.log(`Client connected from ${e}:${t}`)}),this.server.addEventListener("error",s=>{let e=s.detail;console.error("Socket server error:",e)})}setupSignalHandlers(){process.on("SIGINT",()=>this.shutdown()),process.on("SIGTERM",()=>this.shutdown())}async start(){try{if(await this.initializeDatabase(),!this.db)throw new Error("Database initialization failed");this.server=new d({db:this.db,port:this.config.port,host:this.config.host,path:this.config.path,inspect:this.config.debugLevel>0,maxConnections:this.config.maxConnections}),this.subprocessManager=new v(s=>{this.shutdown(s)}),this.setupServerEventHandlers(),this.setupSignalHandlers(),await this.server.start()}catch(s){throw console.error("Failed to start PGLiteSocketServer:",s),s}}async shutdown(s=0){console.log(` | ||
| Shutting down PGLiteSocketServer...`),this.subprocessManager&&this.subprocessManager.terminate(this.config.shutdownTimeout),this.server&&await this.server.stop(),this.db&&await this.db.close(),console.log("Server stopped"),process.exit(s)}},v=class{constructor(s){this.childProcess=null;this.onExit=s}get process(){return this.childProcess}spawn(s,e,t){console.log(`Running command: ${s}`);let i={...process.env};t&&(i.DATABASE_URL=e,console.log(`Setting DATABASE_URL=${e}`));let r=s.trim().split(/\s+/);this.childProcess=(0,E.spawn)(r[0],r.slice(1),{env:i,stdio:"inherit"}),this.childProcess.on("error",n=>{console.error("Error running command:",n),console.log("Subprocess failed to start, shutting down..."),this.onExit(1)}),this.childProcess.on("close",n=>{console.log(`Command exited with code ${n}`),this.childProcess=null,n!==null&&n!==0&&(console.log(`Child process failed with exit code ${n}, shutting down...`),this.onExit(n))})}terminate(s){this.childProcess&&(console.log("Terminating child process..."),this.childProcess.kill("SIGTERM"),setTimeout(()=>{this.childProcess&&!this.childProcess.killed&&(console.log("Force killing child process..."),this.childProcess.kill("SIGKILL"))},s))}};async function S(){h.values.help&&(console.log(C),process.exit(0));try{let c=g.parseConfig();await new g(c).start()}catch(c){console.error("Unhandled error:",c),process.exit(1)}}S(); | ||
| `,g=class{constructor(i){this.db=null;this.server=null;this.subprocessManager=null;this.config=i}static parseConfig(){let i=l.values.extensions;return{dbPath:l.values.db,port:parseInt(l.values.port,10),host:l.values.host,path:l.values.path,debugLevel:parseInt(l.values.debug,10),extensionNames:i?i.split(",").map(e=>e.trim()):void 0,runCommand:l.values.run,includeDatabaseUrl:l.values["include-database-url"],shutdownTimeout:parseInt(l.values["shutdown-timeout"],10),maxConnections:parseInt(l.values["max-connections"],10)}}createDatabaseUrl(){let{host:i,port:e,path:t}=this.config;if(t){let s=t.endsWith("/.s.PGSQL.5432")?t.slice(0,-13):t;return`postgresql://postgres:postgres@/postgres?host=${encodeURIComponent(s)}`}else return`postgresql://postgres:postgres@${i}:${e}/postgres`}async importExtensions(){if(!this.config.extensionNames?.length)return;let i={},e=["vector","live","pg_hashids","pg_ivm","pg_uuidv7","pgtap","age","pg_textsearch"];for(let t of this.config.extensionNames){let s=null;try{if(t.includes(":")){let[r,n]=t.split(":");if(!r||!n)throw new Error(`Invalid extension format '${t}'. Expected: package/path:exportedName`);s=(await import(r))[n],s&&(i[n]=s,console.log(`Imported extension '${n}' from '${r}'`))}else if(e.includes(t))s=(await import(`@electric-sql/pglite/${t}`))[t],s&&(i[t]=s,console.log(`Imported extension: ${t}`));else{try{s=(await import(`@electric-sql/pglite/contrib/${t}`))[t]}catch{s=(await import(`@electric-sql/pglite-${t}`))[t]}s&&(i[t]=s,console.log(`Imported extension: ${t}`))}}catch(r){throw console.error(`Failed to import extension '${t}':`,r),new Error(`Failed to import extension '${t}'`)}}return Object.keys(i).length>0?i:void 0}async initializeDatabase(){console.log(`Initializing PGLite with database: ${this.config.dbPath}`),console.log(`Debug level: ${this.config.debugLevel}`);let i=await this.importExtensions();this.db=new b.PGlite(this.config.dbPath,{debug:this.config.debugLevel,extensions:i}),await this.db.waitReady,console.log("PGlite database initialized")}setupServerEventHandlers(){if(!this.server||!this.subprocessManager)throw new Error("Server or subprocess manager not initialized");this.server.addEventListener("listening",i=>{let e=i.detail;if(console.log(`PGLiteSocketServer listening on ${JSON.stringify(e)}`),this.config.runCommand&&this.subprocessManager){let t=this.createDatabaseUrl();this.subprocessManager.spawn(this.config.runCommand,t,this.config.includeDatabaseUrl)}}),this.server.addEventListener("connection",i=>{let{clientAddress:e,clientPort:t}=i.detail;console.log(`Client connected from ${e}:${t}`)}),this.server.addEventListener("error",i=>{let e=i.detail;console.error("Socket server error:",e)})}setupSignalHandlers(){process.on("SIGINT",()=>this.shutdown()),process.on("SIGTERM",()=>this.shutdown())}async start(){try{if(await this.initializeDatabase(),!this.db)throw new Error("Database initialization failed");this.server=new d({db:this.db,port:this.config.port,host:this.config.host,path:this.config.path,inspect:this.config.debugLevel>0,maxConnections:this.config.maxConnections}),this.subprocessManager=new f(i=>{this.shutdown(i)}),this.setupServerEventHandlers(),this.setupSignalHandlers(),await this.server.start()}catch(i){throw console.error("Failed to start PGLiteSocketServer:",i),i}}async shutdown(i=0){console.log(` | ||
| Shutting down PGLiteSocketServer...`),this.subprocessManager&&this.subprocessManager.terminate(this.config.shutdownTimeout),this.server&&await this.server.stop(),this.db&&await this.db.close(),console.log("Server stopped"),process.exit(i)}},f=class{constructor(i){this.childProcess=null;this.onExit=i}get process(){return this.childProcess}spawn(i,e,t){console.log(`Running command: ${i}`);let s={...process.env};t&&(s.DATABASE_URL=e,console.log(`Setting DATABASE_URL=${e}`));let r=i.trim().split(/\s+/);this.childProcess=(0,w.spawn)(r[0],r.slice(1),{env:s,stdio:"inherit"}),this.childProcess.on("error",n=>{console.error("Error running command:",n),console.log("Subprocess failed to start, shutting down..."),this.onExit(1)}),this.childProcess.on("close",n=>{console.log(`Command exited with code ${n}`),this.childProcess=null,n!==null&&n!==0&&(console.log(`Child process failed with exit code ${n}, shutting down...`),this.onExit(n))})}terminate(i){this.childProcess&&(console.log("Terminating child process..."),this.childProcess.kill("SIGTERM"),setTimeout(()=>{this.childProcess&&!this.childProcess.killed&&(console.log("Force killing child process..."),this.childProcess.kill("SIGKILL"))},i))}};async function C(){l.values.help&&(console.log(x),process.exit(0));try{let h=g.parseConfig();await new g(h).start()}catch(h){console.error("Unhandled error:",h),process.exit(1)}}C(); | ||
| //# sourceMappingURL=server.cjs.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../src/scripts/server.ts","../../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { PGlite, DebugLevel } from '@electric-sql/pglite'\nimport type { Extension, Extensions } from '@electric-sql/pglite'\nimport { PGLiteSocketServer } from '../index'\nimport { parseArgs } from 'node:util'\nimport { spawn, ChildProcess } from 'node:child_process'\n\n// Define command line argument options\nconst args = parseArgs({\n options: {\n db: {\n type: 'string',\n short: 'd',\n default: 'memory://',\n help: 'Database path (relative or absolute). Use memory:// for in-memory database.',\n },\n port: {\n type: 'string',\n short: 'p',\n default: '5432',\n help: 'Port to listen on',\n },\n host: {\n type: 'string',\n short: 'h',\n default: '127.0.0.1',\n help: 'Host to bind to',\n },\n path: {\n type: 'string',\n short: 'u',\n default: undefined,\n help: 'unix socket to bind to. Takes precedence over host:port',\n },\n debug: {\n type: 'string',\n short: 'v',\n default: '0',\n help: 'Debug level (0-5)',\n },\n extensions: {\n type: 'string',\n short: 'e',\n default: undefined,\n help: 'Comma-separated list of extensions to load (e.g., vector,pgcrypto)',\n },\n run: {\n type: 'string',\n short: 'r',\n default: undefined,\n help: 'Command to run after server starts',\n },\n 'include-database-url': {\n type: 'boolean',\n default: false,\n help: 'Include DATABASE_URL in the environment of the subprocess',\n },\n 'shutdown-timeout': {\n type: 'string',\n default: '5000',\n help: 'Timeout in milliseconds for graceful subprocess shutdown (default: 5000)',\n },\n 'max-connections': {\n type: 'string',\n short: 'm',\n default: '1',\n help: 'Maximum concurrent connections (default: 1)',\n },\n help: {\n type: 'boolean',\n short: '?',\n default: false,\n help: 'Show help',\n },\n },\n})\n\nconst help = `PGlite Socket Server\nUsage: pglite-server [options]\n\nOptions:\n -d, --db=PATH Database path (default: memory://)\n -p, --port=PORT Port to listen on (default: 5432)\n -h, --host=HOST Host to bind to (default: 127.0.0.1)\n -u, --path=UNIX Unix socket to bind to (default: undefined). Takes precedence over host:port\n -v, --debug=LEVEL Debug level 0-5 (default: 0)\n -e, --extensions=LIST Comma-separated list of extensions to load\n Formats: vector, pgcrypto (built-in/contrib)\n @org/package/path:exportedName (npm package)\n -r, --run=COMMAND Command to run after server starts\n --include-database-url Include DATABASE_URL in subprocess environment\n --shutdown-timeout=MS Timeout for graceful subprocess shutdown in ms (default: 5000)\n -m, --max-connections=N Maximum concurrent connections (default is no concurrency: 1)\n`\n\ninterface ServerConfig {\n dbPath: string\n port: number\n host: string\n path?: string\n debugLevel: DebugLevel\n extensionNames?: string[]\n runCommand?: string\n includeDatabaseUrl: boolean\n shutdownTimeout: number\n maxConnections: number\n}\n\nclass PGLiteServerRunner {\n private config: ServerConfig\n private db: PGlite | null = null\n private server: PGLiteSocketServer | null = null\n private subprocessManager: SubprocessManager | null = null\n\n constructor(config: ServerConfig) {\n this.config = config\n }\n\n static parseConfig(): ServerConfig {\n const extensionsArg = args.values.extensions as string | undefined\n return {\n dbPath: args.values.db as string,\n port: parseInt(args.values.port as string, 10),\n host: args.values.host as string,\n path: args.values.path as string,\n debugLevel: parseInt(args.values.debug as string, 10) as DebugLevel,\n extensionNames: extensionsArg\n ? extensionsArg.split(',').map((e) => e.trim())\n : undefined,\n runCommand: args.values.run as string,\n includeDatabaseUrl: args.values['include-database-url'] as boolean,\n shutdownTimeout: parseInt(args.values['shutdown-timeout'] as string, 10),\n maxConnections: parseInt(args.values['max-connections'] as string, 10),\n }\n }\n\n private createDatabaseUrl(): string {\n const { host, port, path } = this.config\n\n if (path) {\n // Unix socket connection\n const socketDir = path.endsWith('/.s.PGSQL.5432')\n ? path.slice(0, -13)\n : path\n return `postgresql://postgres:postgres@/postgres?host=${encodeURIComponent(socketDir)}`\n } else {\n // TCP connection\n return `postgresql://postgres:postgres@${host}:${port}/postgres`\n }\n }\n\n private async importExtensions(): Promise<Extensions | undefined> {\n if (!this.config.extensionNames?.length) {\n return undefined\n }\n\n const extensions: Extensions = {}\n\n // Built-in extensions that are not in contrib\n const builtInExtensions = [\n 'vector',\n 'live',\n 'pg_hashids',\n 'pg_ivm',\n 'pg_uuidv7',\n 'pgtap',\n ]\n\n for (const name of this.config.extensionNames) {\n let ext: Extension | null = null\n\n try {\n // Check if this is a custom package path (contains ':')\n // Format: @org/package/path:exportedName or package/path:exportedName\n if (name.includes(':')) {\n const [packagePath, exportName] = name.split(':')\n if (!packagePath || !exportName) {\n throw new Error(\n `Invalid extension format '${name}'. Expected: package/path:exportedName`,\n )\n }\n const mod = await import(packagePath)\n ext = mod[exportName] as Extension\n if (ext) {\n extensions[exportName] = ext\n console.log(\n `Imported extension '${exportName}' from '${packagePath}'`,\n )\n }\n } else if (builtInExtensions.includes(name)) {\n // Built-in extension (e.g., @electric-sql/pglite/vector)\n const mod = await import(`@electric-sql/pglite/${name}`)\n ext = mod[name] as Extension\n if (ext) {\n extensions[name] = ext\n console.log(`Imported extension: ${name}`)\n }\n } else {\n // Try contrib first (e.g., @electric-sql/pglite/contrib/pgcrypto)\n try {\n const mod = await import(`@electric-sql/pglite/contrib/${name}`)\n ext = mod[name] as Extension\n } catch {\n // Fall back to external package (e.g., @electric-sql/pglite-<extension>)\n const mod = await import(`@electric-sql/pglite-${name}`)\n ext = mod[name] as Extension\n }\n if (ext) {\n extensions[name] = ext\n console.log(`Imported extension: ${name}`)\n }\n }\n } catch (error) {\n console.error(`Failed to import extension '${name}':`, error)\n throw new Error(`Failed to import extension '${name}'`)\n }\n }\n\n return Object.keys(extensions).length > 0 ? extensions : undefined\n }\n\n private async initializeDatabase(): Promise<void> {\n console.log(`Initializing PGLite with database: ${this.config.dbPath}`)\n console.log(`Debug level: ${this.config.debugLevel}`)\n\n const extensions = await this.importExtensions()\n\n this.db = new PGlite(this.config.dbPath, {\n debug: this.config.debugLevel,\n extensions,\n })\n await this.db.waitReady\n console.log('PGlite database initialized')\n }\n\n private setupServerEventHandlers(): void {\n if (!this.server || !this.subprocessManager) {\n throw new Error('Server or subprocess manager not initialized')\n }\n\n this.server.addEventListener('listening', (event) => {\n const detail = (\n event as CustomEvent<{ port: number; host: string } | { host: string }>\n ).detail\n console.log(`PGLiteSocketServer listening on ${JSON.stringify(detail)}`)\n\n // Run the command after server starts listening\n if (this.config.runCommand && this.subprocessManager) {\n const databaseUrl = this.createDatabaseUrl()\n this.subprocessManager.spawn(\n this.config.runCommand,\n databaseUrl,\n this.config.includeDatabaseUrl,\n )\n }\n })\n\n this.server.addEventListener('connection', (event) => {\n const { clientAddress, clientPort } = (\n event as CustomEvent<{ clientAddress: string; clientPort: number }>\n ).detail\n console.log(`Client connected from ${clientAddress}:${clientPort}`)\n })\n\n this.server.addEventListener('error', (event) => {\n const error = (event as CustomEvent<Error>).detail\n console.error('Socket server error:', error)\n })\n }\n\n private setupSignalHandlers(): void {\n process.on('SIGINT', () => this.shutdown())\n process.on('SIGTERM', () => this.shutdown())\n }\n\n async start(): Promise<void> {\n try {\n // Initialize database\n await this.initializeDatabase()\n\n if (!this.db) {\n throw new Error('Database initialization failed')\n }\n\n // Create and setup the socket server\n this.server = new PGLiteSocketServer({\n db: this.db,\n port: this.config.port,\n host: this.config.host,\n path: this.config.path,\n inspect: this.config.debugLevel > 0,\n maxConnections: this.config.maxConnections,\n })\n\n // Create subprocess manager\n this.subprocessManager = new SubprocessManager((exitCode) => {\n this.shutdown(exitCode)\n })\n\n // Setup event handlers\n this.setupServerEventHandlers()\n this.setupSignalHandlers()\n\n // Start the server\n await this.server.start()\n } catch (error) {\n console.error('Failed to start PGLiteSocketServer:', error)\n throw error\n }\n }\n\n async shutdown(exitCode: number = 0): Promise<void> {\n console.log('\\nShutting down PGLiteSocketServer...')\n\n // Terminate subprocess if running\n if (this.subprocessManager) {\n this.subprocessManager.terminate(this.config.shutdownTimeout)\n }\n\n // Stop server\n if (this.server) {\n await this.server.stop()\n }\n\n // Close database\n if (this.db) {\n await this.db.close()\n }\n\n console.log('Server stopped')\n process.exit(exitCode)\n }\n}\n\nclass SubprocessManager {\n private childProcess: ChildProcess | null = null\n private onExit: (code: number) => void\n\n constructor(onExit: (code: number) => void) {\n this.onExit = onExit\n }\n\n get process(): ChildProcess | null {\n return this.childProcess\n }\n\n spawn(\n command: string,\n databaseUrl: string,\n includeDatabaseUrl: boolean,\n ): void {\n console.log(`Running command: ${command}`)\n\n // Prepare environment variables\n const env = { ...process.env }\n if (includeDatabaseUrl) {\n env.DATABASE_URL = databaseUrl\n console.log(`Setting DATABASE_URL=${databaseUrl}`)\n }\n\n // Parse and spawn the command\n const commandParts = command.trim().split(/\\s+/)\n this.childProcess = spawn(commandParts[0], commandParts.slice(1), {\n env,\n stdio: 'inherit',\n })\n\n this.childProcess.on('error', (error) => {\n console.error('Error running command:', error)\n // If subprocess fails to start, shutdown the server\n console.log('Subprocess failed to start, shutting down...')\n this.onExit(1)\n })\n\n this.childProcess.on('close', (code) => {\n console.log(`Command exited with code ${code}`)\n this.childProcess = null\n\n // If child process exits with non-zero code, notify parent\n if (code !== null && code !== 0) {\n console.log(\n `Child process failed with exit code ${code}, shutting down...`,\n )\n this.onExit(code)\n }\n })\n }\n\n terminate(timeout: number): void {\n if (this.childProcess) {\n console.log('Terminating child process...')\n this.childProcess.kill('SIGTERM')\n\n // Give it a moment to exit gracefully, then force kill if needed\n setTimeout(() => {\n if (this.childProcess && !this.childProcess.killed) {\n console.log('Force killing child process...')\n this.childProcess.kill('SIGKILL')\n }\n }, timeout)\n }\n }\n}\n\n// Main execution\nasync function main() {\n // Show help and exit if requested\n if (args.values.help) {\n console.log(help)\n process.exit(0)\n }\n\n try {\n const config = PGLiteServerRunner.parseConfig()\n const serverRunner = new PGLiteServerRunner(config)\n await serverRunner.start()\n } catch (error) {\n console.error('Unhandled error:', error)\n process.exit(1)\n }\n}\n\n// Run the main function\nmain()\n","import type { PGlite } from '@electric-sql/pglite'\nimport { type Server, type Socket, createServer } from 'net'\n\n// Connection queue timeout in milliseconds\nexport const CONNECTION_QUEUE_TIMEOUT = 60000 // 60 seconds\n\n/**\n * Represents a queued query waiting for PGlite access\n */\ninterface QueuedQuery {\n handlerId: number\n message: Uint8Array\n resolve: (result: Uint8Array) => void\n reject: (error: Error) => void\n timestamp: number\n}\n\n/**\n * Global query queue manager\n * Ensures only one query executes at a time in PGlite\n */\nclass QueryQueueManager {\n private queue: QueuedQuery[] = []\n private processing = false\n private db: PGlite\n private debug: boolean\n private lastHandlerId: null | number = null\n\n constructor(db: PGlite, debug = false) {\n this.db = db\n this.debug = debug\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[QueryQueueManager] ${message}`, ...args)\n }\n }\n\n async enqueue(handlerId: number, message: Uint8Array): Promise<Uint8Array> {\n return new Promise((resolve, reject) => {\n const query: QueuedQuery = {\n handlerId,\n message,\n resolve,\n reject,\n timestamp: Date.now(),\n }\n\n this.queue.push(query)\n this.log(\n `enqueued query from handler #${handlerId}, queue size: ${this.queue.length}`,\n )\n\n // Process queue if not already processing\n if (!this.processing) {\n this.processQueue()\n }\n })\n }\n\n private async processQueue(): Promise<void> {\n if (this.processing || this.queue.length === 0) {\n return\n }\n\n this.processing = true\n\n while (this.queue.length > 0) {\n let query\n\n if (this.db.isInTransaction() && this.lastHandlerId) {\n const i = this.queue.findIndex(\n (q) => q.handlerId === this.lastHandlerId,\n )\n if (i === -1) {\n // we didn't find any other query from the same client!\n this.log(\n `transaction started, but no query from the same handler id found in queue`,\n this.lastHandlerId,\n )\n query = null\n } else {\n query = this.queue.splice(i, 1)[0]\n }\n } else {\n query = this.queue.shift()\n }\n if (!query) break\n\n const waitTime = Date.now() - query.timestamp\n this.log(\n `processing query from handler #${query.handlerId} (waited ${waitTime}ms)`,\n )\n\n try {\n // Execute the query with exclusive access to PGlite\n const result = await this.db.runExclusive(async () => {\n return await this.db.execProtocolRaw(query.message)\n })\n\n this.log(\n `query from handler #${query.handlerId} completed, ${result.length} bytes`,\n )\n this.lastHandlerId = query.handlerId\n query.resolve(result)\n } catch (error) {\n this.log(`query from handler #${query.handlerId} failed:`, error)\n query.reject(error as Error)\n }\n }\n\n this.processing = false\n this.log(`queue processing complete, queue length is`, this.queue.length)\n }\n\n getQueueLength(): number {\n return this.queue.length\n }\n\n clearQueueForHandler(handlerId: number): void {\n const before = this.queue.length\n this.queue = this.queue.filter((q) => {\n if (q.handlerId === handlerId) {\n q.reject(new Error('Handler disconnected'))\n return false\n }\n return true\n })\n const removed = before - this.queue.length\n if (removed > 0) {\n this.log(`cleared ${removed} queries for handler #${handlerId}`)\n }\n }\n\n async clearTransactionIfNeeded(handlerId: number): Promise<void> {\n if (this.db.isInTransaction() && this.lastHandlerId === handlerId) {\n await this.db.exec('ROLLBACK')\n this.lastHandlerId = null\n await this.processQueue()\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketHandler\n */\nexport interface PGLiteSocketHandlerOptions {\n /** The query queue manager */\n queryQueue: QueryQueueManager\n /** Whether to close the socket when detached (default: false) */\n closeOnDetach?: boolean\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n}\n\n/**\n * Handler for a single socket connection to PGlite\n * Each connection can remain open and send multiple queries\n */\nexport class PGLiteSocketHandler extends EventTarget {\n private queryQueue: QueryQueueManager\n private socket: Socket | null = null\n private active = false\n private closeOnDetach: boolean\n private inspect: boolean\n private debug: boolean\n private readonly id: number\n private messageBuffer: Buffer = Buffer.alloc(0)\n private idleTimer?: NodeJS.Timeout\n private idleTimeout: number\n private lastActivityTime: number = Date.now()\n\n // Static counter for generating unique handler IDs\n private static nextHandlerId = 1\n\n constructor(options: PGLiteSocketHandlerOptions) {\n super()\n this.queryQueue = options.queryQueue\n this.closeOnDetach = options.closeOnDetach ?? false\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.id = PGLiteSocketHandler.nextHandlerId++\n\n this.log('constructor: created new handler')\n }\n\n public get handlerId(): number {\n return this.id\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketHandler#${this.id}] ${message}`, ...args)\n }\n }\n\n public async attach(socket: Socket): Promise<PGLiteSocketHandler> {\n this.log(\n `attach: attaching socket from ${socket.remoteAddress}:${socket.remotePort}`,\n )\n\n if (this.socket) {\n throw new Error('Socket already attached')\n }\n\n this.socket = socket\n this.active = true\n this.lastActivityTime = Date.now()\n\n // Set up socket options\n socket.setNoDelay(true)\n\n // Set up idle timeout if configured\n if (this.idleTimeout > 0) {\n this.resetIdleTimer()\n }\n\n // Setup event handlers\n this.log(`attach: setting up socket event handlers`)\n\n socket.on('data', (data) => {\n this.lastActivityTime = Date.now()\n this.resetIdleTimer()\n\n setImmediate(async () => {\n try {\n await this.handleData(data)\n } catch (err) {\n this.log('socket on data error: ', err)\n this.handleError(err as Error)\n }\n })\n })\n\n socket.on('error', (err) => {\n setImmediate(() => this.handleError(err))\n })\n\n socket.on('close', () => {\n setImmediate(() => this.handleClose())\n })\n\n this.log(`attach: socket handler ready`)\n return this\n }\n\n private resetIdleTimer(): void {\n if (this.idleTimeout <= 0) return\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n }\n\n this.idleTimer = setTimeout(() => {\n const idleTime = Date.now() - this.lastActivityTime\n this.log(`idle timeout after ${idleTime}ms`)\n this.handleError(new Error('Idle timeout'))\n }, this.idleTimeout)\n }\n\n public async detach(close?: boolean): Promise<PGLiteSocketHandler> {\n this.log(`detach: detaching socket, close=${close ?? this.closeOnDetach}`)\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n this.idleTimer = undefined\n }\n\n // Clear any pending queries for this handler\n this.queryQueue.clearQueueForHandler(this.id)\n\n await this.queryQueue.clearTransactionIfNeeded(this.id)\n\n if (!this.socket) {\n this.log(`detach: no socket attached, nothing to do`)\n return this\n }\n\n // Remove all listeners\n this.socket.removeAllListeners('data')\n this.socket.removeAllListeners('error')\n this.socket.removeAllListeners('close')\n\n // Close the socket if requested\n if (close ?? this.closeOnDetach) {\n if (this.socket.writable) {\n this.log(`detach: closing socket`)\n try {\n this.socket.end()\n this.socket.destroy()\n } catch (err) {\n this.log(`detach: error closing socket:`, err)\n }\n }\n }\n\n this.socket = null\n this.active = false\n this.messageBuffer = Buffer.alloc(0)\n\n this.log(`detach: handler cleaned up`)\n return this\n }\n\n public get isAttached(): boolean {\n return this.socket !== null\n }\n\n private async handleData(data: Buffer): Promise<number> {\n if (!this.socket || !this.active) {\n this.log(`handleData: no active socket, ignoring data`)\n return 0\n }\n\n this.log(`handleData: received ${data.length} bytes`)\n\n // Append to buffer for message reassembly\n this.messageBuffer = Buffer.concat([this.messageBuffer, data])\n\n // Print the incoming data to the console\n this.inspectData('incoming', data)\n\n try {\n let totalProcessed = 0\n\n while (this.messageBuffer.length > 0) {\n // Determine message length\n let messageLength = 0\n let isComplete = false\n\n // Handle startup message (no type byte, just length)\n if (this.messageBuffer.length >= 4) {\n const firstInt = this.messageBuffer.readInt32BE(0)\n\n if (this.messageBuffer.length >= 8) {\n const secondInt = this.messageBuffer.readInt32BE(4)\n // PostgreSQL 3.0 protocol version\n if (secondInt === 196608 || secondInt === 0x00030000) {\n messageLength = firstInt\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n // Regular message (type byte + length)\n if (!isComplete && this.messageBuffer.length >= 5) {\n const msgLength = this.messageBuffer.readInt32BE(1)\n messageLength = 1 + msgLength\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n if (!isComplete || messageLength === 0) {\n this.log(\n `handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`,\n )\n break\n }\n\n // Extract and process complete message\n const message = this.messageBuffer.slice(0, messageLength)\n this.messageBuffer = this.messageBuffer.slice(messageLength)\n\n this.log(`handleData: processing message of ${message.length} bytes`)\n\n // Check if socket is still active before processing\n if (!this.active || !this.socket) {\n this.log(`handleData: socket no longer active, stopping processing`)\n break\n }\n\n // Queue the query for execution\n // This allows multiple connections to queue queries simultaneously\n const result = await this.queryQueue.enqueue(\n this.id,\n new Uint8Array(message),\n )\n\n this.log(`handleData: received ${result.length} bytes from PGlite`)\n\n // Print the outgoing data to the console\n this.inspectData('outgoing', result)\n\n // Send response if available\n if (\n result.length > 0 &&\n this.socket &&\n this.socket.writable &&\n this.active\n ) {\n await new Promise<number>((resolve, reject) => {\n this.log(`handleData: writing response to socket`)\n if (this.socket?.writable) {\n this.socket.write(Buffer.from(result), (err?: any) => {\n if (err) {\n this.log(`handleData: error writing to socket:`, err)\n reject(err)\n } else {\n this.log(`handleData: socket sent: ${result.length} bytes`)\n resolve(result.length)\n }\n })\n } else {\n this.log(`handleData: socket no longer writable`)\n resolve(0)\n }\n }).catch((writeErr) => {\n this.log(`handleData: failed to write to socket:`, writeErr)\n throw writeErr\n })\n }\n\n totalProcessed += result.length\n }\n\n // Emit data event with byte sizes\n this.dispatchEvent(\n new CustomEvent('data', {\n detail: { incoming: data.length, outgoing: totalProcessed },\n }),\n )\n\n return totalProcessed\n } catch (err) {\n this.log(`handleData: error processing data:`, err)\n throw err\n }\n }\n\n private handleError(err: Error): void {\n if (!this.active) {\n this.log(`handleError: handler not active, ignoring error`)\n return\n }\n\n // ECONNRESET is expected behavior when clients disconnect\n if (err.message?.includes('ECONNRESET')) {\n this.log(\n `handleError: client disconnected (ECONNRESET) - normal behavior`,\n )\n } else if (err.message?.includes('Idle timeout')) {\n this.log(`handleError: connection idle timeout`)\n } else {\n this.log(`handleError:`, err)\n }\n\n this.active = false\n\n // Emit error event\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n\n // Clean up\n this.detach(true)\n }\n\n private handleClose(): void {\n this.log(`handleClose: socket closed`)\n this.active = false\n this.dispatchEvent(new CustomEvent('close'))\n this.detach(false)\n }\n\n private inspectData(\n direction: 'incoming' | 'outgoing',\n data: Buffer | Uint8Array,\n ): void {\n if (!this.inspect) return\n console.log('-'.repeat(75))\n if (direction === 'incoming') {\n console.log('-> incoming', data.length, 'bytes')\n } else {\n console.log('<- outgoing', data.length, 'bytes')\n }\n\n for (let offset = 0; offset < data.length; offset += 16) {\n const chunkSize = Math.min(16, data.length - offset)\n\n let hexPart = ''\n for (let i = 0; i < 16; i++) {\n if (i < chunkSize) {\n const byte = data[offset + i]\n hexPart += byte.toString(16).padStart(2, '0') + ' '\n } else {\n hexPart += ' '\n }\n }\n\n let asciiPart = ''\n for (let i = 0; i < chunkSize; i++) {\n const byte = data[offset + i]\n asciiPart += byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : '.'\n }\n\n console.log(\n `${offset.toString(16).padStart(8, '0')} ${hexPart} ${asciiPart}`,\n )\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketServer\n */\nexport interface PGLiteSocketServerOptions {\n /** The PGlite database instance */\n db: PGlite\n /** The port to listen on (default: 5432) */\n port?: number\n /** The host to bind to (default: 127.0.0.1) */\n host?: string\n /** Unix socket path to bind to (default: undefined) */\n path?: string\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n /** Maximum concurrent connections (default: 100) */\n maxConnections?: number\n}\n\n/**\n * PGLite Socket Server with support for multiple concurrent connections\n * Connections remain open and queries are queued at the query level\n */\nexport class PGLiteSocketServer extends EventTarget {\n readonly db: PGlite\n private server: Server | null = null\n private port?: number\n private host?: string\n private path?: string\n private active = false\n private inspect: boolean\n private debug: boolean\n private idleTimeout: number\n private maxConnections: number\n private handlers: Set<PGLiteSocketHandler> = new Set()\n private queryQueue: QueryQueueManager\n\n constructor(options: PGLiteSocketServerOptions) {\n super()\n this.db = options.db\n if (options.path) {\n this.path = options.path\n } else {\n if (typeof options.port === 'number') {\n // Keep port undefined on port 0, will be set by the OS when we start the server.\n this.port = options.port ?? options.port\n } else {\n this.port = 5432\n }\n this.host = options.host || '127.0.0.1'\n }\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.maxConnections = options.maxConnections ?? 1\n\n // Create the shared query queue\n this.queryQueue = new QueryQueueManager(this.db, this.debug)\n\n this.log(`constructor: created server on ${this.getServerConn()}`)\n this.log(`constructor: max connections: ${this.maxConnections}`)\n if (this.idleTimeout > 0) {\n this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)\n }\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketServer] ${message}`, ...args)\n }\n }\n\n public async start(): Promise<void> {\n this.log(`start: starting server on ${this.getServerConn()}`)\n\n if (this.server) {\n throw new Error('Socket server already started')\n }\n\n // Ensure PGlite is ready before accepting connections\n await this.db.waitReady\n\n this.active = true\n this.server = createServer((socket) => {\n setImmediate(() => this.handleConnection(socket))\n })\n\n this.server.maxConnections = this.maxConnections\n\n return new Promise<void>((resolve, reject) => {\n if (!this.server) return reject(new Error('Server not initialized'))\n\n this.server.on('error', (err) => {\n this.log(`start: server error:`, err)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n if (!this.active) {\n reject(err)\n }\n })\n\n if (this.path) {\n this.server.listen(this.path, () => {\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { path: this.path },\n }),\n )\n resolve()\n })\n } else {\n const server = this.server\n server.listen(this.port, this.host, () => {\n const address = server.address()\n // We are not using pipes, so return type should be AddressInfo\n if (address === null || typeof address !== 'object') {\n throw Error('Expected address info')\n }\n // Assign the new port number\n this.port = address.port\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { port: this.port, host: this.host },\n }),\n )\n resolve()\n })\n }\n })\n }\n\n public getServerConn(): string {\n if (this.path) return this.path\n return `${this.host}:${this.port}`\n }\n\n public async stop(): Promise<void> {\n this.log(`stop: stopping server`)\n\n this.active = false\n\n // Detach all handlers\n this.log(`stop: detaching ${this.handlers.size} handlers`)\n for (const handler of this.handlers) {\n handler.detach(true)\n }\n this.handlers.clear()\n\n if (!this.server) {\n this.log(`stop: server not running, nothing to do`)\n return Promise.resolve()\n }\n\n return new Promise<void>((resolve) => {\n if (!this.server) return resolve()\n\n this.server.close(() => {\n this.log(`stop: server closed`)\n this.server = null\n this.dispatchEvent(new CustomEvent('close'))\n resolve()\n })\n })\n }\n\n private async handleConnection(socket: Socket): Promise<void> {\n const clientInfo = {\n clientAddress: socket.remoteAddress || 'unknown',\n clientPort: socket.remotePort || 0,\n }\n\n this.log(\n `handleConnection: new connection from ${clientInfo.clientAddress}:${clientInfo.clientPort}`,\n )\n this.log(\n `handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`,\n )\n\n if (!this.active) {\n this.log(`handleConnection: server not active, closing connection`)\n try {\n socket.end()\n } catch (err) {\n this.log(`handleConnection: error closing socket:`, err)\n }\n return\n }\n\n // Check connection limit\n if (this.handlers.size >= this.maxConnections) {\n this.log(`handleConnection: max connections reached, rejecting`)\n socket.write(Buffer.from('Too many connections\\n'))\n socket.end()\n return\n }\n\n // Create a new handler for this connection\n const handler = new PGLiteSocketHandler({\n queryQueue: this.queryQueue,\n closeOnDetach: true,\n inspect: this.inspect,\n debug: this.debug,\n idleTimeout: this.idleTimeout,\n })\n\n // Track this handler\n this.handlers.add(handler)\n\n // Handle errors\n handler.addEventListener('error', (event) => {\n const error = (event as CustomEvent<Error>).detail\n\n if (error?.message?.includes('ECONNRESET')) {\n this.log(\n `handler #${handler.handlerId}: client disconnected (ECONNRESET)`,\n )\n } else if (error?.message?.includes('Idle timeout')) {\n this.log(`handler #${handler.handlerId}: idle timeout`)\n } else {\n this.log(`handler #${handler.handlerId}: error:`, error)\n }\n })\n\n // Handle close event\n handler.addEventListener('close', () => {\n this.log(`handler #${handler.handlerId}: closed`)\n this.handlers.delete(handler)\n this.log(`handleConnection: active connections: ${this.handlers.size}`)\n })\n\n try {\n await handler.attach(socket)\n this.dispatchEvent(new CustomEvent('connection', { detail: clientInfo }))\n } catch (err) {\n this.log(`handleConnection: error attaching socket:`, err)\n this.handlers.delete(handler)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n try {\n socket.end()\n } catch (closeErr) {\n this.log(`handleConnection: error closing socket:`, closeErr)\n }\n }\n }\n\n public getStats() {\n return {\n activeConnections: this.handlers.size,\n queuedQueries: this.queryQueue.getQueueLength(),\n maxConnections: this.maxConnections,\n }\n }\n}\n"],"mappings":"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","names":["import_pglite","import_net","QueryQueueManager","db","debug","message","args","handlerId","resolve","reject","query","i","q","waitTime","result","error","before","removed","_PGLiteSocketHandler","options","socket","data","err","idleTime","close","totalProcessed","messageLength","isComplete","firstInt","secondInt","writeErr","direction","offset","chunkSize","hexPart","byte","asciiPart","PGLiteSocketHandler","PGLiteSocketServer","server","address","handler","clientInfo","event","closeErr","import_node_util","import_node_child_process","args","help","PGLiteServerRunner","config","extensionsArg","host","port","path","socketDir","extensions","builtInExtensions","name","ext","packagePath","exportName","error","event","detail","databaseUrl","clientAddress","clientPort","PGLiteSocketServer","SubprocessManager","exitCode","onExit","command","includeDatabaseUrl","env","commandParts","code","timeout","main"]} | ||
| {"version":3,"sources":["../../src/scripts/server.ts","../../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { PGlite, DebugLevel } from '@electric-sql/pglite'\nimport type { Extension, Extensions } from '@electric-sql/pglite'\nimport { PGLiteSocketServer } from '../index'\nimport { parseArgs } from 'node:util'\nimport { spawn, ChildProcess } from 'node:child_process'\n\n// Define command line argument options\nconst args = parseArgs({\n options: {\n db: {\n type: 'string',\n short: 'd',\n default: 'memory://',\n help: 'Database path (relative or absolute). Use memory:// for in-memory database.',\n },\n port: {\n type: 'string',\n short: 'p',\n default: '5432',\n help: 'Port to listen on',\n },\n host: {\n type: 'string',\n short: 'h',\n default: '127.0.0.1',\n help: 'Host to bind to',\n },\n path: {\n type: 'string',\n short: 'u',\n default: undefined,\n help: 'unix socket to bind to. Takes precedence over host:port',\n },\n debug: {\n type: 'string',\n short: 'v',\n default: '0',\n help: 'Debug level (0-5)',\n },\n extensions: {\n type: 'string',\n short: 'e',\n default: undefined,\n help: 'Comma-separated list of extensions to load (e.g., vector,pgcrypto,postgis etc.)',\n },\n run: {\n type: 'string',\n short: 'r',\n default: undefined,\n help: 'Command to run after server starts',\n },\n 'include-database-url': {\n type: 'boolean',\n default: false,\n help: 'Include DATABASE_URL in the environment of the subprocess',\n },\n 'shutdown-timeout': {\n type: 'string',\n default: '5000',\n help: 'Timeout in milliseconds for graceful subprocess shutdown (default: 5000)',\n },\n 'max-connections': {\n type: 'string',\n short: 'm',\n default: '1',\n help: 'Maximum concurrent connections (default: 1)',\n },\n help: {\n type: 'boolean',\n short: '?',\n default: false,\n help: 'Show help',\n },\n },\n})\n\nconst help = `PGlite Socket Server\nUsage: pglite-server [options]\n\nOptions:\n -d, --db=PATH Database path (default: memory://)\n -p, --port=PORT Port to listen on (default: 5432)\n -h, --host=HOST Host to bind to (default: 127.0.0.1)\n -u, --path=UNIX Unix socket to bind to (default: undefined). Takes precedence over host:port\n -v, --debug=LEVEL Debug level 0-5 (default: 0)\n -e, --extensions=LIST Comma-separated list of extensions to load\n Formats: vector, pgcrypto (built-in/contrib)\n @org/package/path:exportedName (npm package)\n -r, --run=COMMAND Command to run after server starts\n --include-database-url Include DATABASE_URL in subprocess environment\n --shutdown-timeout=MS Timeout for graceful subprocess shutdown in ms (default: 5000)\n -m, --max-connections=N Maximum concurrent connections (default is no concurrency: 1)\n`\n\ninterface ServerConfig {\n dbPath: string\n port: number\n host: string\n path?: string\n debugLevel: DebugLevel\n extensionNames?: string[]\n runCommand?: string\n includeDatabaseUrl: boolean\n shutdownTimeout: number\n maxConnections: number\n}\n\nclass PGLiteServerRunner {\n private config: ServerConfig\n private db: PGlite | null = null\n private server: PGLiteSocketServer | null = null\n private subprocessManager: SubprocessManager | null = null\n\n constructor(config: ServerConfig) {\n this.config = config\n }\n\n static parseConfig(): ServerConfig {\n const extensionsArg = args.values.extensions as string | undefined\n return {\n dbPath: args.values.db as string,\n port: parseInt(args.values.port as string, 10),\n host: args.values.host as string,\n path: args.values.path as string,\n debugLevel: parseInt(args.values.debug as string, 10) as DebugLevel,\n extensionNames: extensionsArg\n ? extensionsArg.split(',').map((e) => e.trim())\n : undefined,\n runCommand: args.values.run as string,\n includeDatabaseUrl: args.values['include-database-url'] as boolean,\n shutdownTimeout: parseInt(args.values['shutdown-timeout'] as string, 10),\n maxConnections: parseInt(args.values['max-connections'] as string, 10),\n }\n }\n\n private createDatabaseUrl(): string {\n const { host, port, path } = this.config\n\n if (path) {\n // Unix socket connection\n const socketDir = path.endsWith('/.s.PGSQL.5432')\n ? path.slice(0, -13)\n : path\n return `postgresql://postgres:postgres@/postgres?host=${encodeURIComponent(socketDir)}`\n } else {\n // TCP connection\n return `postgresql://postgres:postgres@${host}:${port}/postgres`\n }\n }\n\n private async importExtensions(): Promise<Extensions | undefined> {\n if (!this.config.extensionNames?.length) {\n return undefined\n }\n\n const extensions: Extensions = {}\n\n // Built-in extensions that are not in contrib\n const builtInExtensions = [\n 'vector',\n 'live',\n 'pg_hashids',\n 'pg_ivm',\n 'pg_uuidv7',\n 'pgtap',\n 'age',\n 'pg_textsearch',\n ]\n\n for (const name of this.config.extensionNames) {\n let ext: Extension | null = null\n\n try {\n // Check if this is a custom package path (contains ':')\n // Format: @org/package/path:exportedName or package/path:exportedName\n if (name.includes(':')) {\n const [packagePath, exportName] = name.split(':')\n if (!packagePath || !exportName) {\n throw new Error(\n `Invalid extension format '${name}'. Expected: package/path:exportedName`,\n )\n }\n const mod = await import(packagePath)\n ext = mod[exportName] as Extension\n if (ext) {\n extensions[exportName] = ext\n console.log(\n `Imported extension '${exportName}' from '${packagePath}'`,\n )\n }\n } else if (builtInExtensions.includes(name)) {\n // Built-in extension (e.g., @electric-sql/pglite/vector)\n const mod = await import(`@electric-sql/pglite/${name}`)\n ext = mod[name] as Extension\n if (ext) {\n extensions[name] = ext\n console.log(`Imported extension: ${name}`)\n }\n } else {\n // Try contrib first (e.g., @electric-sql/pglite/contrib/pgcrypto)\n try {\n const mod = await import(`@electric-sql/pglite/contrib/${name}`)\n ext = mod[name] as Extension\n } catch {\n // Fall back to external package (e.g., @electric-sql/pglite-<extension>)\n const mod = await import(`@electric-sql/pglite-${name}`)\n ext = mod[name] as Extension\n }\n if (ext) {\n extensions[name] = ext\n console.log(`Imported extension: ${name}`)\n }\n }\n } catch (error) {\n console.error(`Failed to import extension '${name}':`, error)\n throw new Error(`Failed to import extension '${name}'`)\n }\n }\n\n return Object.keys(extensions).length > 0 ? extensions : undefined\n }\n\n private async initializeDatabase(): Promise<void> {\n console.log(`Initializing PGLite with database: ${this.config.dbPath}`)\n console.log(`Debug level: ${this.config.debugLevel}`)\n\n const extensions = await this.importExtensions()\n\n this.db = new PGlite(this.config.dbPath, {\n debug: this.config.debugLevel,\n extensions,\n })\n await this.db.waitReady\n console.log('PGlite database initialized')\n }\n\n private setupServerEventHandlers(): void {\n if (!this.server || !this.subprocessManager) {\n throw new Error('Server or subprocess manager not initialized')\n }\n\n this.server.addEventListener('listening', (event) => {\n const detail = (\n event as CustomEvent<{ port: number; host: string } | { host: string }>\n ).detail\n console.log(`PGLiteSocketServer listening on ${JSON.stringify(detail)}`)\n\n // Run the command after server starts listening\n if (this.config.runCommand && this.subprocessManager) {\n const databaseUrl = this.createDatabaseUrl()\n this.subprocessManager.spawn(\n this.config.runCommand,\n databaseUrl,\n this.config.includeDatabaseUrl,\n )\n }\n })\n\n this.server.addEventListener('connection', (event) => {\n const { clientAddress, clientPort } = (\n event as CustomEvent<{ clientAddress: string; clientPort: number }>\n ).detail\n console.log(`Client connected from ${clientAddress}:${clientPort}`)\n })\n\n this.server.addEventListener('error', (event) => {\n const error = (event as CustomEvent<Error>).detail\n console.error('Socket server error:', error)\n })\n }\n\n private setupSignalHandlers(): void {\n process.on('SIGINT', () => this.shutdown())\n process.on('SIGTERM', () => this.shutdown())\n }\n\n async start(): Promise<void> {\n try {\n // Initialize database\n await this.initializeDatabase()\n\n if (!this.db) {\n throw new Error('Database initialization failed')\n }\n\n // Create and setup the socket server\n this.server = new PGLiteSocketServer({\n db: this.db,\n port: this.config.port,\n host: this.config.host,\n path: this.config.path,\n inspect: this.config.debugLevel > 0,\n maxConnections: this.config.maxConnections,\n })\n\n // Create subprocess manager\n this.subprocessManager = new SubprocessManager((exitCode) => {\n this.shutdown(exitCode)\n })\n\n // Setup event handlers\n this.setupServerEventHandlers()\n this.setupSignalHandlers()\n\n // Start the server\n await this.server.start()\n } catch (error) {\n console.error('Failed to start PGLiteSocketServer:', error)\n throw error\n }\n }\n\n async shutdown(exitCode: number = 0): Promise<void> {\n console.log('\\nShutting down PGLiteSocketServer...')\n\n // Terminate subprocess if running\n if (this.subprocessManager) {\n this.subprocessManager.terminate(this.config.shutdownTimeout)\n }\n\n // Stop server\n if (this.server) {\n await this.server.stop()\n }\n\n // Close database\n if (this.db) {\n await this.db.close()\n }\n\n console.log('Server stopped')\n process.exit(exitCode)\n }\n}\n\nclass SubprocessManager {\n private childProcess: ChildProcess | null = null\n private onExit: (code: number) => void\n\n constructor(onExit: (code: number) => void) {\n this.onExit = onExit\n }\n\n get process(): ChildProcess | null {\n return this.childProcess\n }\n\n spawn(\n command: string,\n databaseUrl: string,\n includeDatabaseUrl: boolean,\n ): void {\n console.log(`Running command: ${command}`)\n\n // Prepare environment variables\n const env = { ...process.env }\n if (includeDatabaseUrl) {\n env.DATABASE_URL = databaseUrl\n console.log(`Setting DATABASE_URL=${databaseUrl}`)\n }\n\n // Parse and spawn the command\n const commandParts = command.trim().split(/\\s+/)\n this.childProcess = spawn(commandParts[0], commandParts.slice(1), {\n env,\n stdio: 'inherit',\n })\n\n this.childProcess.on('error', (error) => {\n console.error('Error running command:', error)\n // If subprocess fails to start, shutdown the server\n console.log('Subprocess failed to start, shutting down...')\n this.onExit(1)\n })\n\n this.childProcess.on('close', (code) => {\n console.log(`Command exited with code ${code}`)\n this.childProcess = null\n\n // If child process exits with non-zero code, notify parent\n if (code !== null && code !== 0) {\n console.log(\n `Child process failed with exit code ${code}, shutting down...`,\n )\n this.onExit(code)\n }\n })\n }\n\n terminate(timeout: number): void {\n if (this.childProcess) {\n console.log('Terminating child process...')\n this.childProcess.kill('SIGTERM')\n\n // Give it a moment to exit gracefully, then force kill if needed\n setTimeout(() => {\n if (this.childProcess && !this.childProcess.killed) {\n console.log('Force killing child process...')\n this.childProcess.kill('SIGKILL')\n }\n }, timeout)\n }\n }\n}\n\n// Main execution\nasync function main() {\n // Show help and exit if requested\n if (args.values.help) {\n console.log(help)\n process.exit(0)\n }\n\n try {\n const config = PGLiteServerRunner.parseConfig()\n const serverRunner = new PGLiteServerRunner(config)\n await serverRunner.start()\n } catch (error) {\n console.error('Unhandled error:', error)\n process.exit(1)\n }\n}\n\n// Run the main function\nmain()\n","import type { PGlite } from '@electric-sql/pglite'\nimport { type Server, type Socket, createServer } from 'net'\n\n// Connection queue timeout in milliseconds\nexport const CONNECTION_QUEUE_TIMEOUT = 60000 // 60 seconds\n\n/**\n * Represents a queued query waiting for PGlite access\n */\ninterface QueuedQuery {\n handlerId: number\n message: Uint8Array\n resolve: (resultSize: number) => void\n reject: (error: Error) => void\n timestamp: number\n onData: (data: Uint8Array) => void\n}\n\n/**\n * Global query queue manager\n * Ensures only one query executes at a time in PGlite\n */\nclass QueryQueueManager {\n private queue: QueuedQuery[] = []\n private processing = false\n private db: PGlite\n private debug: boolean\n private lastHandlerId: null | number = null\n\n constructor(db: PGlite, debug = false) {\n this.db = db\n this.debug = debug\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[QueryQueueManager] ${message}`, ...args)\n }\n }\n\n async enqueue(\n handlerId: number,\n message: Uint8Array,\n onData: (data: Uint8Array) => void,\n ): Promise<number> {\n return new Promise((resolve, reject) => {\n const query: QueuedQuery = {\n handlerId,\n message,\n resolve,\n reject,\n timestamp: Date.now(),\n onData,\n }\n\n this.queue.push(query)\n this.log(\n `enqueued query from handler #${handlerId}, queue size: ${this.queue.length}`,\n )\n\n // Process queue if not already processing\n if (!this.processing) {\n this.processQueue()\n }\n })\n }\n\n private async processQueue(): Promise<void> {\n if (this.processing || this.queue.length === 0) {\n return\n }\n\n this.processing = true\n\n while (this.queue.length > 0) {\n let query\n\n if (this.db.isInTransaction() && this.lastHandlerId) {\n const i = this.queue.findIndex(\n (q) => q.handlerId === this.lastHandlerId,\n )\n if (i === -1) {\n // we didn't find any other query from the same client!\n this.log(\n `transaction started, but no query from the same handler id found in queue`,\n this.lastHandlerId,\n )\n query = null\n } else {\n query = this.queue.splice(i, 1)[0]\n }\n } else {\n query = this.queue.shift()\n }\n if (!query) break\n\n const waitTime = Date.now() - query.timestamp\n this.log(\n `processing query from handler #${query.handlerId} (waited ${waitTime}ms)`,\n )\n\n let result = 0\n try {\n // Execute the query with exclusive access to PGlite\n await this.db.runExclusive(async () => {\n return await this.db.execProtocolRawStream(query.message, {\n onRawData: (data) => {\n result += data.length\n query.onData(data)\n },\n })\n })\n } catch (error) {\n this.log(`query from handler #${query.handlerId} failed:`, error)\n query.reject(error as Error)\n return\n }\n\n this.log(\n `query from handler #${query.handlerId} completed, ${result} bytes`,\n )\n this.lastHandlerId = query.handlerId\n query.resolve(result)\n }\n\n this.processing = false\n this.log(`queue processing complete, queue length is`, this.queue.length)\n }\n\n getQueueLength(): number {\n return this.queue.length\n }\n\n clearQueueForHandler(handlerId: number): void {\n const before = this.queue.length\n this.queue = this.queue.filter((q) => {\n if (q.handlerId === handlerId) {\n q.reject(new Error('Handler disconnected'))\n return false\n }\n return true\n })\n const removed = before - this.queue.length\n if (removed > 0) {\n this.log(`cleared ${removed} queries for handler #${handlerId}`)\n }\n }\n\n async clearTransactionIfNeeded(handlerId: number): Promise<void> {\n if (this.db.isInTransaction() && this.lastHandlerId === handlerId) {\n await this.db.exec('ROLLBACK')\n this.lastHandlerId = null\n await this.processQueue()\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketHandler\n */\nexport interface PGLiteSocketHandlerOptions {\n /** The query queue manager */\n queryQueue: QueryQueueManager\n /** Whether to close the socket when detached (default: false) */\n closeOnDetach?: boolean\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n}\n\n/**\n * Handler for a single socket connection to PGlite\n * Each connection can remain open and send multiple queries\n */\nexport class PGLiteSocketHandler extends EventTarget {\n private queryQueue: QueryQueueManager\n private socket: Socket | null = null\n private active = false\n private closeOnDetach: boolean\n private inspect: boolean\n private debug: boolean\n private readonly id: number\n private messageBuffer: Buffer = Buffer.alloc(0)\n private idleTimer?: NodeJS.Timeout\n private idleTimeout: number\n private lastActivityTime: number = Date.now()\n\n // Static counter for generating unique handler IDs\n private static nextHandlerId = 1\n\n constructor(options: PGLiteSocketHandlerOptions) {\n super()\n this.queryQueue = options.queryQueue\n this.closeOnDetach = options.closeOnDetach ?? false\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.id = PGLiteSocketHandler.nextHandlerId++\n\n this.log('constructor: created new handler')\n }\n\n public get handlerId(): number {\n return this.id\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketHandler#${this.id}] ${message}`, ...args)\n }\n }\n\n public async attach(socket: Socket): Promise<PGLiteSocketHandler> {\n this.log(\n `attach: attaching socket from ${socket.remoteAddress}:${socket.remotePort}`,\n )\n\n if (this.socket) {\n throw new Error('Socket already attached')\n }\n\n this.socket = socket\n this.active = true\n this.lastActivityTime = Date.now()\n\n // Set up socket options\n socket.setNoDelay(true)\n\n // Set up idle timeout if configured\n if (this.idleTimeout > 0) {\n this.resetIdleTimer()\n }\n\n // Setup event handlers\n this.log(`attach: setting up socket event handlers`)\n\n socket.on('data', (data) => {\n this.lastActivityTime = Date.now()\n this.resetIdleTimer()\n\n setImmediate(async () => {\n try {\n await this.handleData(data)\n } catch (err) {\n this.log('socket on data error: ', err)\n this.handleError(err as Error)\n }\n })\n })\n\n socket.on('error', (err) => {\n setImmediate(() => this.handleError(err))\n })\n\n socket.on('close', () => {\n setImmediate(() => this.handleClose())\n })\n\n this.log(`attach: socket handler ready`)\n return this\n }\n\n private resetIdleTimer(): void {\n if (this.idleTimeout <= 0) return\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n }\n\n this.idleTimer = setTimeout(() => {\n const idleTime = Date.now() - this.lastActivityTime\n this.log(`idle timeout after ${idleTime}ms`)\n this.handleError(new Error('Idle timeout'))\n }, this.idleTimeout)\n }\n\n public async detach(close?: boolean): Promise<PGLiteSocketHandler> {\n this.log(`detach: detaching socket, close=${close ?? this.closeOnDetach}`)\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n this.idleTimer = undefined\n }\n\n // Clear any pending queries for this handler\n this.queryQueue.clearQueueForHandler(this.id)\n\n await this.queryQueue.clearTransactionIfNeeded(this.id)\n\n if (!this.socket) {\n this.log(`detach: no socket attached, nothing to do`)\n return this\n }\n\n // Remove all listeners\n this.socket.removeAllListeners('data')\n this.socket.removeAllListeners('error')\n this.socket.removeAllListeners('close')\n\n // Close the socket if requested\n if (close ?? this.closeOnDetach) {\n if (this.socket.writable) {\n this.log(`detach: closing socket`)\n try {\n this.socket.end()\n this.socket.destroy()\n } catch (err) {\n this.log(`detach: error closing socket:`, err)\n }\n }\n }\n\n this.socket = null\n this.active = false\n this.messageBuffer = Buffer.alloc(0)\n\n this.log(`detach: handler cleaned up`)\n return this\n }\n\n public get isAttached(): boolean {\n return this.socket !== null\n }\n\n private async handleData(data: Buffer): Promise<number> {\n if (!this.socket || !this.active) {\n this.log(`handleData: no active socket, ignoring data`)\n return 0\n }\n\n this.log(`handleData: received ${data.length} bytes`)\n\n // Append to buffer for message reassembly\n this.messageBuffer = Buffer.concat([this.messageBuffer, data])\n\n // Print the incoming data to the console\n this.inspectData('incoming', data)\n\n try {\n let totalProcessed = 0\n\n while (this.messageBuffer.length > 0) {\n // Determine message length\n let messageLength = 0\n let isComplete = false\n\n // Handle startup message (no type byte, just length)\n if (this.messageBuffer.length >= 4) {\n const firstInt = this.messageBuffer.readInt32BE(0)\n\n if (this.messageBuffer.length >= 8) {\n const secondInt = this.messageBuffer.readInt32BE(4)\n // PostgreSQL 3.0 protocol version\n if (secondInt === 196608 || secondInt === 0x00030000) {\n messageLength = firstInt\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n // Regular message (type byte + length)\n if (!isComplete && this.messageBuffer.length >= 5) {\n const msgLength = this.messageBuffer.readInt32BE(1)\n messageLength = 1 + msgLength\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n if (!isComplete || messageLength === 0) {\n this.log(\n `handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`,\n )\n break\n }\n\n // Extract and process complete message\n const message = this.messageBuffer.slice(0, messageLength)\n this.messageBuffer = this.messageBuffer.slice(messageLength)\n\n this.log(`handleData: processing message of ${message.length} bytes`)\n\n // Check if socket is still active before processing\n if (!this.active || !this.socket) {\n this.log(`handleData: socket no longer active, stopping processing`)\n break\n }\n\n let socketWriteError: any = undefined\n // Queue the query for execution\n // This allows multiple connections to queue queries simultaneously\n await this.queryQueue.enqueue(\n this.id,\n new Uint8Array(message),\n (data) => {\n this.log(`handleData: received ${data.length} bytes from PGlite`)\n\n // Print the outgoing data to the console\n this.inspectData('outgoing', data)\n\n // Send response if available\n if (\n data.length > 0 &&\n this.socket &&\n this.socket.writable &&\n this.active\n ) {\n // await new Promise<number>((resolve, reject) => {\n this.log(`handleData: writing response to socket`)\n if (this.socket?.writable) {\n this.socket.write(Buffer.from(data), (err?: any) => {\n if (err) {\n this.log(`handleData: error writing to socket:`, err)\n socketWriteError = err\n } else {\n this.log(`handleData: socket sent: ${data.length} bytes`)\n }\n })\n } else {\n this.log(`handleData: socket no longer writable`)\n }\n }\n totalProcessed += data.length\n },\n )\n if (socketWriteError) throw socketWriteError\n }\n\n // Emit data event with byte sizes\n this.dispatchEvent(\n new CustomEvent('data', {\n detail: { incoming: data.length, outgoing: totalProcessed },\n }),\n )\n\n return totalProcessed\n } catch (err) {\n this.log(`handleData: error processing data:`, err)\n throw err\n }\n }\n\n private handleError(err: Error): void {\n if (!this.active) {\n this.log(`handleError: handler not active, ignoring error`)\n return\n }\n\n // ECONNRESET is expected behavior when clients disconnect\n if (err.message?.includes('ECONNRESET')) {\n this.log(\n `handleError: client disconnected (ECONNRESET) - normal behavior`,\n )\n } else if (err.message?.includes('Idle timeout')) {\n this.log(`handleError: connection idle timeout`)\n } else {\n this.log(`handleError:`, err)\n }\n\n this.active = false\n\n // Emit error event\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n\n // Clean up\n this.detach(true)\n }\n\n private handleClose(): void {\n this.log(`handleClose: socket closed`)\n this.active = false\n this.dispatchEvent(new CustomEvent('close'))\n this.detach(false)\n }\n\n private inspectData(\n direction: 'incoming' | 'outgoing',\n data: Buffer | Uint8Array,\n ): void {\n if (!this.inspect) return\n console.log('-'.repeat(75))\n if (direction === 'incoming') {\n console.log('-> incoming', data.length, 'bytes')\n } else {\n console.log('<- outgoing', data.length, 'bytes')\n }\n\n for (let offset = 0; offset < data.length; offset += 16) {\n const chunkSize = Math.min(16, data.length - offset)\n\n let hexPart = ''\n for (let i = 0; i < 16; i++) {\n if (i < chunkSize) {\n const byte = data[offset + i]\n hexPart += byte.toString(16).padStart(2, '0') + ' '\n } else {\n hexPart += ' '\n }\n }\n\n let asciiPart = ''\n for (let i = 0; i < chunkSize; i++) {\n const byte = data[offset + i]\n asciiPart += byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : '.'\n }\n\n console.log(\n `${offset.toString(16).padStart(8, '0')} ${hexPart} ${asciiPart}`,\n )\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketServer\n */\nexport interface PGLiteSocketServerOptions {\n /** The PGlite database instance */\n db: PGlite\n /** The port to listen on (default: 5432) */\n port?: number\n /** The host to bind to (default: 127.0.0.1) */\n host?: string\n /** Unix socket path to bind to (default: undefined) */\n path?: string\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n /** Maximum concurrent connections (default: 100) */\n maxConnections?: number\n}\n\n/**\n * PGLite Socket Server with support for multiple concurrent connections\n * Connections remain open and queries are queued at the query level\n */\nexport class PGLiteSocketServer extends EventTarget {\n readonly db: PGlite\n private server: Server | null = null\n private port?: number\n private host?: string\n private path?: string\n private active = false\n private inspect: boolean\n private debug: boolean\n private idleTimeout: number\n private maxConnections: number\n private handlers: Set<PGLiteSocketHandler> = new Set()\n private queryQueue: QueryQueueManager\n\n constructor(options: PGLiteSocketServerOptions) {\n super()\n this.db = options.db\n if (options.path) {\n this.path = options.path\n } else {\n if (typeof options.port === 'number') {\n // Keep port undefined on port 0, will be set by the OS when we start the server.\n this.port = options.port ?? options.port\n } else {\n this.port = 5432\n }\n this.host = options.host || '127.0.0.1'\n }\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.maxConnections = options.maxConnections ?? 1\n\n // Create the shared query queue\n this.queryQueue = new QueryQueueManager(this.db, this.debug)\n\n this.log(`constructor: created server on ${this.getServerConn()}`)\n this.log(`constructor: max connections: ${this.maxConnections}`)\n if (this.idleTimeout > 0) {\n this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)\n }\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketServer] ${message}`, ...args)\n }\n }\n\n public async start(): Promise<void> {\n this.log(`start: starting server on ${this.getServerConn()}`)\n\n if (this.server) {\n throw new Error('Socket server already started')\n }\n\n // Ensure PGlite is ready before accepting connections\n await this.db.waitReady\n\n this.active = true\n this.server = createServer((socket) => {\n setImmediate(() => this.handleConnection(socket))\n })\n\n this.server.maxConnections = this.maxConnections\n\n return new Promise<void>((resolve, reject) => {\n if (!this.server) return reject(new Error('Server not initialized'))\n\n this.server.on('error', (err) => {\n this.log(`start: server error:`, err)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n if (!this.active) {\n reject(err)\n }\n })\n\n if (this.path) {\n this.server.listen(this.path, () => {\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { path: this.path },\n }),\n )\n resolve()\n })\n } else {\n const server = this.server\n server.listen(this.port, this.host, () => {\n const address = server.address()\n // We are not using pipes, so return type should be AddressInfo\n if (address === null || typeof address !== 'object') {\n throw Error('Expected address info')\n }\n // Assign the new port number\n this.port = address.port\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { port: this.port, host: this.host },\n }),\n )\n resolve()\n })\n }\n })\n }\n\n public getServerConn(): string {\n if (this.path) return this.path\n return `${this.host}:${this.port}`\n }\n\n public async stop(): Promise<void> {\n this.log(`stop: stopping server`)\n\n this.active = false\n\n // Detach all handlers\n this.log(`stop: detaching ${this.handlers.size} handlers`)\n for (const handler of this.handlers) {\n handler.detach(true)\n }\n this.handlers.clear()\n\n if (!this.server) {\n this.log(`stop: server not running, nothing to do`)\n return Promise.resolve()\n }\n\n return new Promise<void>((resolve) => {\n if (!this.server) return resolve()\n\n this.server.close(() => {\n this.log(`stop: server closed`)\n this.server = null\n this.dispatchEvent(new CustomEvent('close'))\n resolve()\n })\n })\n }\n\n private async handleConnection(socket: Socket): Promise<void> {\n const clientInfo = {\n clientAddress: socket.remoteAddress || 'unknown',\n clientPort: socket.remotePort || 0,\n }\n\n this.log(\n `handleConnection: new connection from ${clientInfo.clientAddress}:${clientInfo.clientPort}`,\n )\n this.log(\n `handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`,\n )\n\n if (!this.active) {\n this.log(`handleConnection: server not active, closing connection`)\n try {\n socket.end()\n } catch (err) {\n this.log(`handleConnection: error closing socket:`, err)\n }\n return\n }\n\n // Check connection limit\n if (this.handlers.size >= this.maxConnections) {\n this.log(`handleConnection: max connections reached, rejecting`)\n socket.write(Buffer.from('Too many connections\\n'))\n socket.end()\n return\n }\n\n // Create a new handler for this connection\n const handler = new PGLiteSocketHandler({\n queryQueue: this.queryQueue,\n closeOnDetach: true,\n inspect: this.inspect,\n debug: this.debug,\n idleTimeout: this.idleTimeout,\n })\n\n // Track this handler\n this.handlers.add(handler)\n\n // Handle errors\n handler.addEventListener('error', (event) => {\n const error = (event as CustomEvent<Error>).detail\n\n if (error?.message?.includes('ECONNRESET')) {\n this.log(\n `handler #${handler.handlerId}: client disconnected (ECONNRESET)`,\n )\n } else if (error?.message?.includes('Idle timeout')) {\n this.log(`handler #${handler.handlerId}: idle timeout`)\n } else {\n this.log(`handler #${handler.handlerId}: error:`, error)\n }\n })\n\n // Handle close event\n handler.addEventListener('close', () => {\n this.log(`handler #${handler.handlerId}: closed`)\n this.handlers.delete(handler)\n this.log(`handleConnection: active connections: ${this.handlers.size}`)\n })\n\n try {\n await handler.attach(socket)\n this.dispatchEvent(new CustomEvent('connection', { detail: clientInfo }))\n } catch (err) {\n this.log(`handleConnection: error attaching socket:`, err)\n this.handlers.delete(handler)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n try {\n socket.end()\n } catch (closeErr) {\n this.log(`handleConnection: error closing socket:`, closeErr)\n }\n }\n }\n\n public getStats() {\n return {\n activeConnections: this.handlers.size,\n queuedQueries: this.queryQueue.getQueueLength(),\n maxConnections: this.maxConnections,\n }\n }\n}\n"],"mappings":"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","names":["import_pglite","import_net","QueryQueueManager","db","debug","message","args","handlerId","onData","resolve","reject","query","i","q","waitTime","result","data","error","before","removed","_PGLiteSocketHandler","options","socket","err","idleTime","close","totalProcessed","messageLength","isComplete","firstInt","secondInt","socketWriteError","direction","offset","chunkSize","hexPart","byte","asciiPart","PGLiteSocketHandler","PGLiteSocketServer","server","address","handler","clientInfo","event","closeErr","import_node_util","import_node_child_process","args","help","PGLiteServerRunner","config","extensionsArg","host","port","path","socketDir","extensions","builtInExtensions","name","ext","packagePath","exportName","error","event","detail","databaseUrl","clientAddress","clientPort","PGLiteSocketServer","SubprocessManager","exitCode","onExit","command","includeDatabaseUrl","env","commandParts","code","timeout","main"]} |
| #!/usr/bin/env node | ||
| import{c as d}from"../chunk-JGAU6COP.js";import{PGlite as u}from"@electric-sql/pglite";import{parseArgs as h}from"node:util";import{spawn as p}from"node:child_process";var r=h({options:{db:{type:"string",short:"d",default:"memory://",help:"Database path (relative or absolute). Use memory:// for in-memory database."},port:{type:"string",short:"p",default:"5432",help:"Port to listen on"},host:{type:"string",short:"h",default:"127.0.0.1",help:"Host to bind to"},path:{type:"string",short:"u",default:void 0,help:"unix socket to bind to. Takes precedence over host:port"},debug:{type:"string",short:"v",default:"0",help:"Debug level (0-5)"},extensions:{type:"string",short:"e",default:void 0,help:"Comma-separated list of extensions to load (e.g., vector,pgcrypto)"},run:{type:"string",short:"r",default:void 0,help:"Command to run after server starts"},"include-database-url":{type:"boolean",default:!1,help:"Include DATABASE_URL in the environment of the subprocess"},"shutdown-timeout":{type:"string",default:"5000",help:"Timeout in milliseconds for graceful subprocess shutdown (default: 5000)"},"max-connections":{type:"string",short:"m",default:"1",help:"Maximum concurrent connections (default: 1)"},help:{type:"boolean",short:"?",default:!1,help:"Show help"}}}),g=`PGlite Socket Server | ||
| import{c as d}from"../chunk-NSUMFCRM.js";import{PGlite as u}from"@electric-sql/pglite";import{parseArgs as h}from"node:util";import{spawn as p}from"node:child_process";var r=h({options:{db:{type:"string",short:"d",default:"memory://",help:"Database path (relative or absolute). Use memory:// for in-memory database."},port:{type:"string",short:"p",default:"5432",help:"Port to listen on"},host:{type:"string",short:"h",default:"127.0.0.1",help:"Host to bind to"},path:{type:"string",short:"u",default:void 0,help:"unix socket to bind to. Takes precedence over host:port"},debug:{type:"string",short:"v",default:"0",help:"Debug level (0-5)"},extensions:{type:"string",short:"e",default:void 0,help:"Comma-separated list of extensions to load (e.g., vector,pgcrypto,postgis etc.)"},run:{type:"string",short:"r",default:void 0,help:"Command to run after server starts"},"include-database-url":{type:"boolean",default:!1,help:"Include DATABASE_URL in the environment of the subprocess"},"shutdown-timeout":{type:"string",default:"5000",help:"Timeout in milliseconds for graceful subprocess shutdown (default: 5000)"},"max-connections":{type:"string",short:"m",default:"1",help:"Maximum concurrent connections (default: 1)"},help:{type:"boolean",short:"?",default:!1,help:"Show help"}}}),g=`PGlite Socket Server | ||
| Usage: pglite-server [options] | ||
@@ -18,4 +18,4 @@ | ||
| -m, --max-connections=N Maximum concurrent connections (default is no concurrency: 1) | ||
| `,l=class{constructor(e){this.db=null;this.server=null;this.subprocessManager=null;this.config=e}static parseConfig(){let e=r.values.extensions;return{dbPath:r.values.db,port:parseInt(r.values.port,10),host:r.values.host,path:r.values.path,debugLevel:parseInt(r.values.debug,10),extensionNames:e?e.split(",").map(o=>o.trim()):void 0,runCommand:r.values.run,includeDatabaseUrl:r.values["include-database-url"],shutdownTimeout:parseInt(r.values["shutdown-timeout"],10),maxConnections:parseInt(r.values["max-connections"],10)}}createDatabaseUrl(){let{host:e,port:o,path:t}=this.config;if(t){let s=t.endsWith("/.s.PGSQL.5432")?t.slice(0,-13):t;return`postgresql://postgres:postgres@/postgres?host=${encodeURIComponent(s)}`}else return`postgresql://postgres:postgres@${e}:${o}/postgres`}async importExtensions(){if(!this.config.extensionNames?.length)return;let e={},o=["vector","live","pg_hashids","pg_ivm","pg_uuidv7","pgtap"];for(let t of this.config.extensionNames){let s=null;try{if(t.includes(":")){let[i,n]=t.split(":");if(!i||!n)throw new Error(`Invalid extension format '${t}'. Expected: package/path:exportedName`);s=(await import(i))[n],s&&(e[n]=s,console.log(`Imported extension '${n}' from '${i}'`))}else if(o.includes(t))s=(await import(`@electric-sql/pglite/${t}`))[t],s&&(e[t]=s,console.log(`Imported extension: ${t}`));else{try{s=(await import(`@electric-sql/pglite/contrib/${t}`))[t]}catch{s=(await import(`@electric-sql/pglite-${t}`))[t]}s&&(e[t]=s,console.log(`Imported extension: ${t}`))}}catch(i){throw console.error(`Failed to import extension '${t}':`,i),new Error(`Failed to import extension '${t}'`)}}return Object.keys(e).length>0?e:void 0}async initializeDatabase(){console.log(`Initializing PGLite with database: ${this.config.dbPath}`),console.log(`Debug level: ${this.config.debugLevel}`);let e=await this.importExtensions();this.db=new u(this.config.dbPath,{debug:this.config.debugLevel,extensions:e}),await this.db.waitReady,console.log("PGlite database initialized")}setupServerEventHandlers(){if(!this.server||!this.subprocessManager)throw new Error("Server or subprocess manager not initialized");this.server.addEventListener("listening",e=>{let o=e.detail;if(console.log(`PGLiteSocketServer listening on ${JSON.stringify(o)}`),this.config.runCommand&&this.subprocessManager){let t=this.createDatabaseUrl();this.subprocessManager.spawn(this.config.runCommand,t,this.config.includeDatabaseUrl)}}),this.server.addEventListener("connection",e=>{let{clientAddress:o,clientPort:t}=e.detail;console.log(`Client connected from ${o}:${t}`)}),this.server.addEventListener("error",e=>{let o=e.detail;console.error("Socket server error:",o)})}setupSignalHandlers(){process.on("SIGINT",()=>this.shutdown()),process.on("SIGTERM",()=>this.shutdown())}async start(){try{if(await this.initializeDatabase(),!this.db)throw new Error("Database initialization failed");this.server=new d({db:this.db,port:this.config.port,host:this.config.host,path:this.config.path,inspect:this.config.debugLevel>0,maxConnections:this.config.maxConnections}),this.subprocessManager=new c(e=>{this.shutdown(e)}),this.setupServerEventHandlers(),this.setupSignalHandlers(),await this.server.start()}catch(e){throw console.error("Failed to start PGLiteSocketServer:",e),e}}async shutdown(e=0){console.log(` | ||
| `,l=class{constructor(e){this.db=null;this.server=null;this.subprocessManager=null;this.config=e}static parseConfig(){let e=r.values.extensions;return{dbPath:r.values.db,port:parseInt(r.values.port,10),host:r.values.host,path:r.values.path,debugLevel:parseInt(r.values.debug,10),extensionNames:e?e.split(",").map(o=>o.trim()):void 0,runCommand:r.values.run,includeDatabaseUrl:r.values["include-database-url"],shutdownTimeout:parseInt(r.values["shutdown-timeout"],10),maxConnections:parseInt(r.values["max-connections"],10)}}createDatabaseUrl(){let{host:e,port:o,path:t}=this.config;if(t){let s=t.endsWith("/.s.PGSQL.5432")?t.slice(0,-13):t;return`postgresql://postgres:postgres@/postgres?host=${encodeURIComponent(s)}`}else return`postgresql://postgres:postgres@${e}:${o}/postgres`}async importExtensions(){if(!this.config.extensionNames?.length)return;let e={},o=["vector","live","pg_hashids","pg_ivm","pg_uuidv7","pgtap","age","pg_textsearch"];for(let t of this.config.extensionNames){let s=null;try{if(t.includes(":")){let[i,n]=t.split(":");if(!i||!n)throw new Error(`Invalid extension format '${t}'. Expected: package/path:exportedName`);s=(await import(i))[n],s&&(e[n]=s,console.log(`Imported extension '${n}' from '${i}'`))}else if(o.includes(t))s=(await import(`@electric-sql/pglite/${t}`))[t],s&&(e[t]=s,console.log(`Imported extension: ${t}`));else{try{s=(await import(`@electric-sql/pglite/contrib/${t}`))[t]}catch{s=(await import(`@electric-sql/pglite-${t}`))[t]}s&&(e[t]=s,console.log(`Imported extension: ${t}`))}}catch(i){throw console.error(`Failed to import extension '${t}':`,i),new Error(`Failed to import extension '${t}'`)}}return Object.keys(e).length>0?e:void 0}async initializeDatabase(){console.log(`Initializing PGLite with database: ${this.config.dbPath}`),console.log(`Debug level: ${this.config.debugLevel}`);let e=await this.importExtensions();this.db=new u(this.config.dbPath,{debug:this.config.debugLevel,extensions:e}),await this.db.waitReady,console.log("PGlite database initialized")}setupServerEventHandlers(){if(!this.server||!this.subprocessManager)throw new Error("Server or subprocess manager not initialized");this.server.addEventListener("listening",e=>{let o=e.detail;if(console.log(`PGLiteSocketServer listening on ${JSON.stringify(o)}`),this.config.runCommand&&this.subprocessManager){let t=this.createDatabaseUrl();this.subprocessManager.spawn(this.config.runCommand,t,this.config.includeDatabaseUrl)}}),this.server.addEventListener("connection",e=>{let{clientAddress:o,clientPort:t}=e.detail;console.log(`Client connected from ${o}:${t}`)}),this.server.addEventListener("error",e=>{let o=e.detail;console.error("Socket server error:",o)})}setupSignalHandlers(){process.on("SIGINT",()=>this.shutdown()),process.on("SIGTERM",()=>this.shutdown())}async start(){try{if(await this.initializeDatabase(),!this.db)throw new Error("Database initialization failed");this.server=new d({db:this.db,port:this.config.port,host:this.config.host,path:this.config.path,inspect:this.config.debugLevel>0,maxConnections:this.config.maxConnections}),this.subprocessManager=new c(e=>{this.shutdown(e)}),this.setupServerEventHandlers(),this.setupSignalHandlers(),await this.server.start()}catch(e){throw console.error("Failed to start PGLiteSocketServer:",e),e}}async shutdown(e=0){console.log(` | ||
| Shutting down PGLiteSocketServer...`),this.subprocessManager&&this.subprocessManager.terminate(this.config.shutdownTimeout),this.server&&await this.server.stop(),this.db&&await this.db.close(),console.log("Server stopped"),process.exit(e)}},c=class{constructor(e){this.childProcess=null;this.onExit=e}get process(){return this.childProcess}spawn(e,o,t){console.log(`Running command: ${e}`);let s={...process.env};t&&(s.DATABASE_URL=o,console.log(`Setting DATABASE_URL=${o}`));let i=e.trim().split(/\s+/);this.childProcess=p(i[0],i.slice(1),{env:s,stdio:"inherit"}),this.childProcess.on("error",n=>{console.error("Error running command:",n),console.log("Subprocess failed to start, shutting down..."),this.onExit(1)}),this.childProcess.on("close",n=>{console.log(`Command exited with code ${n}`),this.childProcess=null,n!==null&&n!==0&&(console.log(`Child process failed with exit code ${n}, shutting down...`),this.onExit(n))})}terminate(e){this.childProcess&&(console.log("Terminating child process..."),this.childProcess.kill("SIGTERM"),setTimeout(()=>{this.childProcess&&!this.childProcess.killed&&(console.log("Force killing child process..."),this.childProcess.kill("SIGKILL"))},e))}};async function m(){r.values.help&&(console.log(g),process.exit(0));try{let a=l.parseConfig();await new l(a).start()}catch(a){console.error("Unhandled error:",a),process.exit(1)}}m(); | ||
| //# sourceMappingURL=server.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../../src/scripts/server.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { PGlite, DebugLevel } from '@electric-sql/pglite'\nimport type { Extension, Extensions } from '@electric-sql/pglite'\nimport { PGLiteSocketServer } from '../index'\nimport { parseArgs } from 'node:util'\nimport { spawn, ChildProcess } from 'node:child_process'\n\n// Define command line argument options\nconst args = parseArgs({\n options: {\n db: {\n type: 'string',\n short: 'd',\n default: 'memory://',\n help: 'Database path (relative or absolute). Use memory:// for in-memory database.',\n },\n port: {\n type: 'string',\n short: 'p',\n default: '5432',\n help: 'Port to listen on',\n },\n host: {\n type: 'string',\n short: 'h',\n default: '127.0.0.1',\n help: 'Host to bind to',\n },\n path: {\n type: 'string',\n short: 'u',\n default: undefined,\n help: 'unix socket to bind to. Takes precedence over host:port',\n },\n debug: {\n type: 'string',\n short: 'v',\n default: '0',\n help: 'Debug level (0-5)',\n },\n extensions: {\n type: 'string',\n short: 'e',\n default: undefined,\n help: 'Comma-separated list of extensions to load (e.g., vector,pgcrypto)',\n },\n run: {\n type: 'string',\n short: 'r',\n default: undefined,\n help: 'Command to run after server starts',\n },\n 'include-database-url': {\n type: 'boolean',\n default: false,\n help: 'Include DATABASE_URL in the environment of the subprocess',\n },\n 'shutdown-timeout': {\n type: 'string',\n default: '5000',\n help: 'Timeout in milliseconds for graceful subprocess shutdown (default: 5000)',\n },\n 'max-connections': {\n type: 'string',\n short: 'm',\n default: '1',\n help: 'Maximum concurrent connections (default: 1)',\n },\n help: {\n type: 'boolean',\n short: '?',\n default: false,\n help: 'Show help',\n },\n },\n})\n\nconst help = `PGlite Socket Server\nUsage: pglite-server [options]\n\nOptions:\n -d, --db=PATH Database path (default: memory://)\n -p, --port=PORT Port to listen on (default: 5432)\n -h, --host=HOST Host to bind to (default: 127.0.0.1)\n -u, --path=UNIX Unix socket to bind to (default: undefined). Takes precedence over host:port\n -v, --debug=LEVEL Debug level 0-5 (default: 0)\n -e, --extensions=LIST Comma-separated list of extensions to load\n Formats: vector, pgcrypto (built-in/contrib)\n @org/package/path:exportedName (npm package)\n -r, --run=COMMAND Command to run after server starts\n --include-database-url Include DATABASE_URL in subprocess environment\n --shutdown-timeout=MS Timeout for graceful subprocess shutdown in ms (default: 5000)\n -m, --max-connections=N Maximum concurrent connections (default is no concurrency: 1)\n`\n\ninterface ServerConfig {\n dbPath: string\n port: number\n host: string\n path?: string\n debugLevel: DebugLevel\n extensionNames?: string[]\n runCommand?: string\n includeDatabaseUrl: boolean\n shutdownTimeout: number\n maxConnections: number\n}\n\nclass PGLiteServerRunner {\n private config: ServerConfig\n private db: PGlite | null = null\n private server: PGLiteSocketServer | null = null\n private subprocessManager: SubprocessManager | null = null\n\n constructor(config: ServerConfig) {\n this.config = config\n }\n\n static parseConfig(): ServerConfig {\n const extensionsArg = args.values.extensions as string | undefined\n return {\n dbPath: args.values.db as string,\n port: parseInt(args.values.port as string, 10),\n host: args.values.host as string,\n path: args.values.path as string,\n debugLevel: parseInt(args.values.debug as string, 10) as DebugLevel,\n extensionNames: extensionsArg\n ? extensionsArg.split(',').map((e) => e.trim())\n : undefined,\n runCommand: args.values.run as string,\n includeDatabaseUrl: args.values['include-database-url'] as boolean,\n shutdownTimeout: parseInt(args.values['shutdown-timeout'] as string, 10),\n maxConnections: parseInt(args.values['max-connections'] as string, 10),\n }\n }\n\n private createDatabaseUrl(): string {\n const { host, port, path } = this.config\n\n if (path) {\n // Unix socket connection\n const socketDir = path.endsWith('/.s.PGSQL.5432')\n ? path.slice(0, -13)\n : path\n return `postgresql://postgres:postgres@/postgres?host=${encodeURIComponent(socketDir)}`\n } else {\n // TCP connection\n return `postgresql://postgres:postgres@${host}:${port}/postgres`\n }\n }\n\n private async importExtensions(): Promise<Extensions | undefined> {\n if (!this.config.extensionNames?.length) {\n return undefined\n }\n\n const extensions: Extensions = {}\n\n // Built-in extensions that are not in contrib\n const builtInExtensions = [\n 'vector',\n 'live',\n 'pg_hashids',\n 'pg_ivm',\n 'pg_uuidv7',\n 'pgtap',\n ]\n\n for (const name of this.config.extensionNames) {\n let ext: Extension | null = null\n\n try {\n // Check if this is a custom package path (contains ':')\n // Format: @org/package/path:exportedName or package/path:exportedName\n if (name.includes(':')) {\n const [packagePath, exportName] = name.split(':')\n if (!packagePath || !exportName) {\n throw new Error(\n `Invalid extension format '${name}'. Expected: package/path:exportedName`,\n )\n }\n const mod = await import(packagePath)\n ext = mod[exportName] as Extension\n if (ext) {\n extensions[exportName] = ext\n console.log(\n `Imported extension '${exportName}' from '${packagePath}'`,\n )\n }\n } else if (builtInExtensions.includes(name)) {\n // Built-in extension (e.g., @electric-sql/pglite/vector)\n const mod = await import(`@electric-sql/pglite/${name}`)\n ext = mod[name] as Extension\n if (ext) {\n extensions[name] = ext\n console.log(`Imported extension: ${name}`)\n }\n } else {\n // Try contrib first (e.g., @electric-sql/pglite/contrib/pgcrypto)\n try {\n const mod = await import(`@electric-sql/pglite/contrib/${name}`)\n ext = mod[name] as Extension\n } catch {\n // Fall back to external package (e.g., @electric-sql/pglite-<extension>)\n const mod = await import(`@electric-sql/pglite-${name}`)\n ext = mod[name] as Extension\n }\n if (ext) {\n extensions[name] = ext\n console.log(`Imported extension: ${name}`)\n }\n }\n } catch (error) {\n console.error(`Failed to import extension '${name}':`, error)\n throw new Error(`Failed to import extension '${name}'`)\n }\n }\n\n return Object.keys(extensions).length > 0 ? extensions : undefined\n }\n\n private async initializeDatabase(): Promise<void> {\n console.log(`Initializing PGLite with database: ${this.config.dbPath}`)\n console.log(`Debug level: ${this.config.debugLevel}`)\n\n const extensions = await this.importExtensions()\n\n this.db = new PGlite(this.config.dbPath, {\n debug: this.config.debugLevel,\n extensions,\n })\n await this.db.waitReady\n console.log('PGlite database initialized')\n }\n\n private setupServerEventHandlers(): void {\n if (!this.server || !this.subprocessManager) {\n throw new Error('Server or subprocess manager not initialized')\n }\n\n this.server.addEventListener('listening', (event) => {\n const detail = (\n event as CustomEvent<{ port: number; host: string } | { host: string }>\n ).detail\n console.log(`PGLiteSocketServer listening on ${JSON.stringify(detail)}`)\n\n // Run the command after server starts listening\n if (this.config.runCommand && this.subprocessManager) {\n const databaseUrl = this.createDatabaseUrl()\n this.subprocessManager.spawn(\n this.config.runCommand,\n databaseUrl,\n this.config.includeDatabaseUrl,\n )\n }\n })\n\n this.server.addEventListener('connection', (event) => {\n const { clientAddress, clientPort } = (\n event as CustomEvent<{ clientAddress: string; clientPort: number }>\n ).detail\n console.log(`Client connected from ${clientAddress}:${clientPort}`)\n })\n\n this.server.addEventListener('error', (event) => {\n const error = (event as CustomEvent<Error>).detail\n console.error('Socket server error:', error)\n })\n }\n\n private setupSignalHandlers(): void {\n process.on('SIGINT', () => this.shutdown())\n process.on('SIGTERM', () => this.shutdown())\n }\n\n async start(): Promise<void> {\n try {\n // Initialize database\n await this.initializeDatabase()\n\n if (!this.db) {\n throw new Error('Database initialization failed')\n }\n\n // Create and setup the socket server\n this.server = new PGLiteSocketServer({\n db: this.db,\n port: this.config.port,\n host: this.config.host,\n path: this.config.path,\n inspect: this.config.debugLevel > 0,\n maxConnections: this.config.maxConnections,\n })\n\n // Create subprocess manager\n this.subprocessManager = new SubprocessManager((exitCode) => {\n this.shutdown(exitCode)\n })\n\n // Setup event handlers\n this.setupServerEventHandlers()\n this.setupSignalHandlers()\n\n // Start the server\n await this.server.start()\n } catch (error) {\n console.error('Failed to start PGLiteSocketServer:', error)\n throw error\n }\n }\n\n async shutdown(exitCode: number = 0): Promise<void> {\n console.log('\\nShutting down PGLiteSocketServer...')\n\n // Terminate subprocess if running\n if (this.subprocessManager) {\n this.subprocessManager.terminate(this.config.shutdownTimeout)\n }\n\n // Stop server\n if (this.server) {\n await this.server.stop()\n }\n\n // Close database\n if (this.db) {\n await this.db.close()\n }\n\n console.log('Server stopped')\n process.exit(exitCode)\n }\n}\n\nclass SubprocessManager {\n private childProcess: ChildProcess | null = null\n private onExit: (code: number) => void\n\n constructor(onExit: (code: number) => void) {\n this.onExit = onExit\n }\n\n get process(): ChildProcess | null {\n return this.childProcess\n }\n\n spawn(\n command: string,\n databaseUrl: string,\n includeDatabaseUrl: boolean,\n ): void {\n console.log(`Running command: ${command}`)\n\n // Prepare environment variables\n const env = { ...process.env }\n if (includeDatabaseUrl) {\n env.DATABASE_URL = databaseUrl\n console.log(`Setting DATABASE_URL=${databaseUrl}`)\n }\n\n // Parse and spawn the command\n const commandParts = command.trim().split(/\\s+/)\n this.childProcess = spawn(commandParts[0], commandParts.slice(1), {\n env,\n stdio: 'inherit',\n })\n\n this.childProcess.on('error', (error) => {\n console.error('Error running command:', error)\n // If subprocess fails to start, shutdown the server\n console.log('Subprocess failed to start, shutting down...')\n this.onExit(1)\n })\n\n this.childProcess.on('close', (code) => {\n console.log(`Command exited with code ${code}`)\n this.childProcess = null\n\n // If child process exits with non-zero code, notify parent\n if (code !== null && code !== 0) {\n console.log(\n `Child process failed with exit code ${code}, shutting down...`,\n )\n this.onExit(code)\n }\n })\n }\n\n terminate(timeout: number): void {\n if (this.childProcess) {\n console.log('Terminating child process...')\n this.childProcess.kill('SIGTERM')\n\n // Give it a moment to exit gracefully, then force kill if needed\n setTimeout(() => {\n if (this.childProcess && !this.childProcess.killed) {\n console.log('Force killing child process...')\n this.childProcess.kill('SIGKILL')\n }\n }, timeout)\n }\n }\n}\n\n// Main execution\nasync function main() {\n // Show help and exit if requested\n if (args.values.help) {\n console.log(help)\n process.exit(0)\n }\n\n try {\n const config = PGLiteServerRunner.parseConfig()\n const serverRunner = new PGLiteServerRunner(config)\n await serverRunner.start()\n } catch (error) {\n console.error('Unhandled error:', error)\n process.exit(1)\n }\n}\n\n// Run the main function\nmain()\n"],"mappings":"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","names":["PGlite","parseArgs","spawn","args","help","PGLiteServerRunner","config","extensionsArg","e","host","port","path","socketDir","extensions","builtInExtensions","name","ext","packagePath","exportName","error","PGlite","event","detail","databaseUrl","clientAddress","clientPort","PGLiteSocketServer","SubprocessManager","exitCode","onExit","command","includeDatabaseUrl","env","commandParts","code","timeout","main"]} | ||
| {"version":3,"sources":["../../src/scripts/server.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { PGlite, DebugLevel } from '@electric-sql/pglite'\nimport type { Extension, Extensions } from '@electric-sql/pglite'\nimport { PGLiteSocketServer } from '../index'\nimport { parseArgs } from 'node:util'\nimport { spawn, ChildProcess } from 'node:child_process'\n\n// Define command line argument options\nconst args = parseArgs({\n options: {\n db: {\n type: 'string',\n short: 'd',\n default: 'memory://',\n help: 'Database path (relative or absolute). Use memory:// for in-memory database.',\n },\n port: {\n type: 'string',\n short: 'p',\n default: '5432',\n help: 'Port to listen on',\n },\n host: {\n type: 'string',\n short: 'h',\n default: '127.0.0.1',\n help: 'Host to bind to',\n },\n path: {\n type: 'string',\n short: 'u',\n default: undefined,\n help: 'unix socket to bind to. Takes precedence over host:port',\n },\n debug: {\n type: 'string',\n short: 'v',\n default: '0',\n help: 'Debug level (0-5)',\n },\n extensions: {\n type: 'string',\n short: 'e',\n default: undefined,\n help: 'Comma-separated list of extensions to load (e.g., vector,pgcrypto,postgis etc.)',\n },\n run: {\n type: 'string',\n short: 'r',\n default: undefined,\n help: 'Command to run after server starts',\n },\n 'include-database-url': {\n type: 'boolean',\n default: false,\n help: 'Include DATABASE_URL in the environment of the subprocess',\n },\n 'shutdown-timeout': {\n type: 'string',\n default: '5000',\n help: 'Timeout in milliseconds for graceful subprocess shutdown (default: 5000)',\n },\n 'max-connections': {\n type: 'string',\n short: 'm',\n default: '1',\n help: 'Maximum concurrent connections (default: 1)',\n },\n help: {\n type: 'boolean',\n short: '?',\n default: false,\n help: 'Show help',\n },\n },\n})\n\nconst help = `PGlite Socket Server\nUsage: pglite-server [options]\n\nOptions:\n -d, --db=PATH Database path (default: memory://)\n -p, --port=PORT Port to listen on (default: 5432)\n -h, --host=HOST Host to bind to (default: 127.0.0.1)\n -u, --path=UNIX Unix socket to bind to (default: undefined). Takes precedence over host:port\n -v, --debug=LEVEL Debug level 0-5 (default: 0)\n -e, --extensions=LIST Comma-separated list of extensions to load\n Formats: vector, pgcrypto (built-in/contrib)\n @org/package/path:exportedName (npm package)\n -r, --run=COMMAND Command to run after server starts\n --include-database-url Include DATABASE_URL in subprocess environment\n --shutdown-timeout=MS Timeout for graceful subprocess shutdown in ms (default: 5000)\n -m, --max-connections=N Maximum concurrent connections (default is no concurrency: 1)\n`\n\ninterface ServerConfig {\n dbPath: string\n port: number\n host: string\n path?: string\n debugLevel: DebugLevel\n extensionNames?: string[]\n runCommand?: string\n includeDatabaseUrl: boolean\n shutdownTimeout: number\n maxConnections: number\n}\n\nclass PGLiteServerRunner {\n private config: ServerConfig\n private db: PGlite | null = null\n private server: PGLiteSocketServer | null = null\n private subprocessManager: SubprocessManager | null = null\n\n constructor(config: ServerConfig) {\n this.config = config\n }\n\n static parseConfig(): ServerConfig {\n const extensionsArg = args.values.extensions as string | undefined\n return {\n dbPath: args.values.db as string,\n port: parseInt(args.values.port as string, 10),\n host: args.values.host as string,\n path: args.values.path as string,\n debugLevel: parseInt(args.values.debug as string, 10) as DebugLevel,\n extensionNames: extensionsArg\n ? extensionsArg.split(',').map((e) => e.trim())\n : undefined,\n runCommand: args.values.run as string,\n includeDatabaseUrl: args.values['include-database-url'] as boolean,\n shutdownTimeout: parseInt(args.values['shutdown-timeout'] as string, 10),\n maxConnections: parseInt(args.values['max-connections'] as string, 10),\n }\n }\n\n private createDatabaseUrl(): string {\n const { host, port, path } = this.config\n\n if (path) {\n // Unix socket connection\n const socketDir = path.endsWith('/.s.PGSQL.5432')\n ? path.slice(0, -13)\n : path\n return `postgresql://postgres:postgres@/postgres?host=${encodeURIComponent(socketDir)}`\n } else {\n // TCP connection\n return `postgresql://postgres:postgres@${host}:${port}/postgres`\n }\n }\n\n private async importExtensions(): Promise<Extensions | undefined> {\n if (!this.config.extensionNames?.length) {\n return undefined\n }\n\n const extensions: Extensions = {}\n\n // Built-in extensions that are not in contrib\n const builtInExtensions = [\n 'vector',\n 'live',\n 'pg_hashids',\n 'pg_ivm',\n 'pg_uuidv7',\n 'pgtap',\n 'age',\n 'pg_textsearch',\n ]\n\n for (const name of this.config.extensionNames) {\n let ext: Extension | null = null\n\n try {\n // Check if this is a custom package path (contains ':')\n // Format: @org/package/path:exportedName or package/path:exportedName\n if (name.includes(':')) {\n const [packagePath, exportName] = name.split(':')\n if (!packagePath || !exportName) {\n throw new Error(\n `Invalid extension format '${name}'. Expected: package/path:exportedName`,\n )\n }\n const mod = await import(packagePath)\n ext = mod[exportName] as Extension\n if (ext) {\n extensions[exportName] = ext\n console.log(\n `Imported extension '${exportName}' from '${packagePath}'`,\n )\n }\n } else if (builtInExtensions.includes(name)) {\n // Built-in extension (e.g., @electric-sql/pglite/vector)\n const mod = await import(`@electric-sql/pglite/${name}`)\n ext = mod[name] as Extension\n if (ext) {\n extensions[name] = ext\n console.log(`Imported extension: ${name}`)\n }\n } else {\n // Try contrib first (e.g., @electric-sql/pglite/contrib/pgcrypto)\n try {\n const mod = await import(`@electric-sql/pglite/contrib/${name}`)\n ext = mod[name] as Extension\n } catch {\n // Fall back to external package (e.g., @electric-sql/pglite-<extension>)\n const mod = await import(`@electric-sql/pglite-${name}`)\n ext = mod[name] as Extension\n }\n if (ext) {\n extensions[name] = ext\n console.log(`Imported extension: ${name}`)\n }\n }\n } catch (error) {\n console.error(`Failed to import extension '${name}':`, error)\n throw new Error(`Failed to import extension '${name}'`)\n }\n }\n\n return Object.keys(extensions).length > 0 ? extensions : undefined\n }\n\n private async initializeDatabase(): Promise<void> {\n console.log(`Initializing PGLite with database: ${this.config.dbPath}`)\n console.log(`Debug level: ${this.config.debugLevel}`)\n\n const extensions = await this.importExtensions()\n\n this.db = new PGlite(this.config.dbPath, {\n debug: this.config.debugLevel,\n extensions,\n })\n await this.db.waitReady\n console.log('PGlite database initialized')\n }\n\n private setupServerEventHandlers(): void {\n if (!this.server || !this.subprocessManager) {\n throw new Error('Server or subprocess manager not initialized')\n }\n\n this.server.addEventListener('listening', (event) => {\n const detail = (\n event as CustomEvent<{ port: number; host: string } | { host: string }>\n ).detail\n console.log(`PGLiteSocketServer listening on ${JSON.stringify(detail)}`)\n\n // Run the command after server starts listening\n if (this.config.runCommand && this.subprocessManager) {\n const databaseUrl = this.createDatabaseUrl()\n this.subprocessManager.spawn(\n this.config.runCommand,\n databaseUrl,\n this.config.includeDatabaseUrl,\n )\n }\n })\n\n this.server.addEventListener('connection', (event) => {\n const { clientAddress, clientPort } = (\n event as CustomEvent<{ clientAddress: string; clientPort: number }>\n ).detail\n console.log(`Client connected from ${clientAddress}:${clientPort}`)\n })\n\n this.server.addEventListener('error', (event) => {\n const error = (event as CustomEvent<Error>).detail\n console.error('Socket server error:', error)\n })\n }\n\n private setupSignalHandlers(): void {\n process.on('SIGINT', () => this.shutdown())\n process.on('SIGTERM', () => this.shutdown())\n }\n\n async start(): Promise<void> {\n try {\n // Initialize database\n await this.initializeDatabase()\n\n if (!this.db) {\n throw new Error('Database initialization failed')\n }\n\n // Create and setup the socket server\n this.server = new PGLiteSocketServer({\n db: this.db,\n port: this.config.port,\n host: this.config.host,\n path: this.config.path,\n inspect: this.config.debugLevel > 0,\n maxConnections: this.config.maxConnections,\n })\n\n // Create subprocess manager\n this.subprocessManager = new SubprocessManager((exitCode) => {\n this.shutdown(exitCode)\n })\n\n // Setup event handlers\n this.setupServerEventHandlers()\n this.setupSignalHandlers()\n\n // Start the server\n await this.server.start()\n } catch (error) {\n console.error('Failed to start PGLiteSocketServer:', error)\n throw error\n }\n }\n\n async shutdown(exitCode: number = 0): Promise<void> {\n console.log('\\nShutting down PGLiteSocketServer...')\n\n // Terminate subprocess if running\n if (this.subprocessManager) {\n this.subprocessManager.terminate(this.config.shutdownTimeout)\n }\n\n // Stop server\n if (this.server) {\n await this.server.stop()\n }\n\n // Close database\n if (this.db) {\n await this.db.close()\n }\n\n console.log('Server stopped')\n process.exit(exitCode)\n }\n}\n\nclass SubprocessManager {\n private childProcess: ChildProcess | null = null\n private onExit: (code: number) => void\n\n constructor(onExit: (code: number) => void) {\n this.onExit = onExit\n }\n\n get process(): ChildProcess | null {\n return this.childProcess\n }\n\n spawn(\n command: string,\n databaseUrl: string,\n includeDatabaseUrl: boolean,\n ): void {\n console.log(`Running command: ${command}`)\n\n // Prepare environment variables\n const env = { ...process.env }\n if (includeDatabaseUrl) {\n env.DATABASE_URL = databaseUrl\n console.log(`Setting DATABASE_URL=${databaseUrl}`)\n }\n\n // Parse and spawn the command\n const commandParts = command.trim().split(/\\s+/)\n this.childProcess = spawn(commandParts[0], commandParts.slice(1), {\n env,\n stdio: 'inherit',\n })\n\n this.childProcess.on('error', (error) => {\n console.error('Error running command:', error)\n // If subprocess fails to start, shutdown the server\n console.log('Subprocess failed to start, shutting down...')\n this.onExit(1)\n })\n\n this.childProcess.on('close', (code) => {\n console.log(`Command exited with code ${code}`)\n this.childProcess = null\n\n // If child process exits with non-zero code, notify parent\n if (code !== null && code !== 0) {\n console.log(\n `Child process failed with exit code ${code}, shutting down...`,\n )\n this.onExit(code)\n }\n })\n }\n\n terminate(timeout: number): void {\n if (this.childProcess) {\n console.log('Terminating child process...')\n this.childProcess.kill('SIGTERM')\n\n // Give it a moment to exit gracefully, then force kill if needed\n setTimeout(() => {\n if (this.childProcess && !this.childProcess.killed) {\n console.log('Force killing child process...')\n this.childProcess.kill('SIGKILL')\n }\n }, timeout)\n }\n }\n}\n\n// Main execution\nasync function main() {\n // Show help and exit if requested\n if (args.values.help) {\n console.log(help)\n process.exit(0)\n }\n\n try {\n const config = PGLiteServerRunner.parseConfig()\n const serverRunner = new PGLiteServerRunner(config)\n await serverRunner.start()\n } catch (error) {\n console.error('Unhandled error:', error)\n process.exit(1)\n }\n}\n\n// Run the main function\nmain()\n"],"mappings":"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","names":["PGlite","parseArgs","spawn","args","help","PGLiteServerRunner","config","extensionsArg","e","host","port","path","socketDir","extensions","builtInExtensions","name","ext","packagePath","exportName","error","PGlite","event","detail","databaseUrl","clientAddress","clientPort","PGLiteSocketServer","SubprocessManager","exitCode","onExit","command","includeDatabaseUrl","env","commandParts","code","timeout","main"]} |
+5
-3
| { | ||
| "name": "@electric-sql/pglite-socket", | ||
| "version": "0.0.22", | ||
| "version": "0.1.0", | ||
| "description": "A socket implementation for PGlite enabling remote connections", | ||
@@ -53,6 +53,8 @@ "author": "Electric DB Limited", | ||
| "@electric-sql/pg-protocol": "0.0.4", | ||
| "@electric-sql/pglite": "0.3.16" | ||
| "@electric-sql/pglite": "0.4.0", | ||
| "@electric-sql/pglite-postgis": "0.0.1" | ||
| }, | ||
| "peerDependencies": { | ||
| "@electric-sql/pglite": "0.3.16" | ||
| "@electric-sql/pglite": "0.4.0", | ||
| "@electric-sql/pglite-postgis": "0.0.1" | ||
| }, | ||
@@ -59,0 +61,0 @@ "scripts": { |
+54
-45
@@ -13,5 +13,6 @@ import type { PGlite } from '@electric-sql/pglite' | ||
| message: Uint8Array | ||
| resolve: (result: Uint8Array) => void | ||
| resolve: (resultSize: number) => void | ||
| reject: (error: Error) => void | ||
| timestamp: number | ||
| onData: (data: Uint8Array) => void | ||
| } | ||
@@ -41,3 +42,7 @@ | ||
| async enqueue(handlerId: number, message: Uint8Array): Promise<Uint8Array> { | ||
| async enqueue( | ||
| handlerId: number, | ||
| message: Uint8Array, | ||
| onData: (data: Uint8Array) => void, | ||
| ): Promise<number> { | ||
| return new Promise((resolve, reject) => { | ||
@@ -50,2 +55,3 @@ const query: QueuedQuery = { | ||
| timestamp: Date.now(), | ||
| onData, | ||
| } | ||
@@ -99,17 +105,24 @@ | ||
| let result = 0 | ||
| try { | ||
| // Execute the query with exclusive access to PGlite | ||
| const result = await this.db.runExclusive(async () => { | ||
| return await this.db.execProtocolRaw(query.message) | ||
| await this.db.runExclusive(async () => { | ||
| return await this.db.execProtocolRawStream(query.message, { | ||
| onRawData: (data) => { | ||
| result += data.length | ||
| query.onData(data) | ||
| }, | ||
| }) | ||
| }) | ||
| this.log( | ||
| `query from handler #${query.handlerId} completed, ${result.length} bytes`, | ||
| ) | ||
| this.lastHandlerId = query.handlerId | ||
| query.resolve(result) | ||
| } catch (error) { | ||
| this.log(`query from handler #${query.handlerId} failed:`, error) | ||
| query.reject(error as Error) | ||
| return | ||
| } | ||
| this.log( | ||
| `query from handler #${query.handlerId} completed, ${result} bytes`, | ||
| ) | ||
| this.lastHandlerId = query.handlerId | ||
| query.resolve(result) | ||
| } | ||
@@ -381,44 +394,40 @@ | ||
| let socketWriteError: any = undefined | ||
| // Queue the query for execution | ||
| // This allows multiple connections to queue queries simultaneously | ||
| const result = await this.queryQueue.enqueue( | ||
| await this.queryQueue.enqueue( | ||
| this.id, | ||
| new Uint8Array(message), | ||
| ) | ||
| (data) => { | ||
| this.log(`handleData: received ${data.length} bytes from PGlite`) | ||
| this.log(`handleData: received ${result.length} bytes from PGlite`) | ||
| // Print the outgoing data to the console | ||
| this.inspectData('outgoing', data) | ||
| // Print the outgoing data to the console | ||
| this.inspectData('outgoing', result) | ||
| // Send response if available | ||
| if ( | ||
| result.length > 0 && | ||
| this.socket && | ||
| this.socket.writable && | ||
| this.active | ||
| ) { | ||
| await new Promise<number>((resolve, reject) => { | ||
| this.log(`handleData: writing response to socket`) | ||
| if (this.socket?.writable) { | ||
| this.socket.write(Buffer.from(result), (err?: any) => { | ||
| if (err) { | ||
| this.log(`handleData: error writing to socket:`, err) | ||
| reject(err) | ||
| } else { | ||
| this.log(`handleData: socket sent: ${result.length} bytes`) | ||
| resolve(result.length) | ||
| } | ||
| }) | ||
| } else { | ||
| this.log(`handleData: socket no longer writable`) | ||
| resolve(0) | ||
| // Send response if available | ||
| if ( | ||
| data.length > 0 && | ||
| this.socket && | ||
| this.socket.writable && | ||
| this.active | ||
| ) { | ||
| // await new Promise<number>((resolve, reject) => { | ||
| this.log(`handleData: writing response to socket`) | ||
| if (this.socket?.writable) { | ||
| this.socket.write(Buffer.from(data), (err?: any) => { | ||
| if (err) { | ||
| this.log(`handleData: error writing to socket:`, err) | ||
| socketWriteError = err | ||
| } else { | ||
| this.log(`handleData: socket sent: ${data.length} bytes`) | ||
| } | ||
| }) | ||
| } else { | ||
| this.log(`handleData: socket no longer writable`) | ||
| } | ||
| } | ||
| }).catch((writeErr) => { | ||
| this.log(`handleData: failed to write to socket:`, writeErr) | ||
| throw writeErr | ||
| }) | ||
| } | ||
| totalProcessed += result.length | ||
| totalProcessed += data.length | ||
| }, | ||
| ) | ||
| if (socketWriteError) throw socketWriteError | ||
| } | ||
@@ -425,0 +434,0 @@ |
@@ -46,3 +46,3 @@ #!/usr/bin/env node | ||
| default: undefined, | ||
| help: 'Comma-separated list of extensions to load (e.g., vector,pgcrypto)', | ||
| help: 'Comma-separated list of extensions to load (e.g., vector,pgcrypto,postgis etc.)', | ||
| }, | ||
@@ -169,2 +169,4 @@ run: { | ||
| 'pgtap', | ||
| 'age', | ||
| 'pg_textsearch', | ||
| ] | ||
@@ -171,0 +173,0 @@ |
| import{createServer as f}from"net";var y=6e4,d=class{constructor(s,e=!1){this.queue=[];this.processing=!1;this.lastHandlerId=null;this.db=s,this.debug=e}log(s,...e){this.debug&&console.log(`[QueryQueueManager] ${s}`,...e)}async enqueue(s,e){return new Promise((t,i)=>{let r={handlerId:s,message:e,resolve:t,reject:i,timestamp:Date.now()};this.queue.push(r),this.log(`enqueued query from handler #${s}, queue size: ${this.queue.length}`),this.processing||this.processQueue()})}async processQueue(){if(!(this.processing||this.queue.length===0)){for(this.processing=!0;this.queue.length>0;){let s;if(this.db.isInTransaction()&&this.lastHandlerId){let t=this.queue.findIndex(i=>i.handlerId===this.lastHandlerId);t===-1?(this.log("transaction started, but no query from the same handler id found in queue",this.lastHandlerId),s=null):s=this.queue.splice(t,1)[0]}else s=this.queue.shift();if(!s)break;let e=Date.now()-s.timestamp;this.log(`processing query from handler #${s.handlerId} (waited ${e}ms)`);try{let t=await this.db.runExclusive(async()=>await this.db.execProtocolRaw(s.message));this.log(`query from handler #${s.handlerId} completed, ${t.length} bytes`),this.lastHandlerId=s.handlerId,s.resolve(t)}catch(t){this.log(`query from handler #${s.handlerId} failed:`,t),s.reject(t)}}this.processing=!1,this.log("queue processing complete, queue length is",this.queue.length)}}getQueueLength(){return this.queue.length}clearQueueForHandler(s){let e=this.queue.length;this.queue=this.queue.filter(i=>i.handlerId===s?(i.reject(new Error("Handler disconnected")),!1):!0);let t=e-this.queue.length;t>0&&this.log(`cleared ${t} queries for handler #${s}`)}async clearTransactionIfNeeded(s){this.db.isInTransaction()&&this.lastHandlerId===s&&(await this.db.exec("ROLLBACK"),this.lastHandlerId=null,await this.processQueue())}},l=class l extends EventTarget{constructor(e){super();this.socket=null;this.active=!1;this.messageBuffer=Buffer.alloc(0);this.lastActivityTime=Date.now();this.queryQueue=e.queryQueue,this.closeOnDetach=e.closeOnDetach??!1,this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.id=l.nextHandlerId++,this.log("constructor: created new handler")}get handlerId(){return this.id}log(e,...t){this.debug&&console.log(`[PGLiteSocketHandler#${this.id}] ${e}`,...t)}async attach(e){if(this.log(`attach: attaching socket from ${e.remoteAddress}:${e.remotePort}`),this.socket)throw new Error("Socket already attached");return this.socket=e,this.active=!0,this.lastActivityTime=Date.now(),e.setNoDelay(!0),this.idleTimeout>0&&this.resetIdleTimer(),this.log("attach: setting up socket event handlers"),e.on("data",t=>{this.lastActivityTime=Date.now(),this.resetIdleTimer(),setImmediate(async()=>{try{await this.handleData(t)}catch(i){this.log("socket on data error: ",i),this.handleError(i)}})}),e.on("error",t=>{setImmediate(()=>this.handleError(t))}),e.on("close",()=>{setImmediate(()=>this.handleClose())}),this.log("attach: socket handler ready"),this}resetIdleTimer(){this.idleTimeout<=0||(this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>{let e=Date.now()-this.lastActivityTime;this.log(`idle timeout after ${e}ms`),this.handleError(new Error("Idle timeout"))},this.idleTimeout))}async detach(e){if(this.log(`detach: detaching socket, close=${e??this.closeOnDetach}`),this.idleTimer&&(clearTimeout(this.idleTimer),this.idleTimer=void 0),this.queryQueue.clearQueueForHandler(this.id),await this.queryQueue.clearTransactionIfNeeded(this.id),!this.socket)return this.log("detach: no socket attached, nothing to do"),this;if(this.socket.removeAllListeners("data"),this.socket.removeAllListeners("error"),this.socket.removeAllListeners("close"),(e??this.closeOnDetach)&&this.socket.writable){this.log("detach: closing socket");try{this.socket.end(),this.socket.destroy()}catch(t){this.log("detach: error closing socket:",t)}}return this.socket=null,this.active=!1,this.messageBuffer=Buffer.alloc(0),this.log("detach: handler cleaned up"),this}get isAttached(){return this.socket!==null}async handleData(e){if(!this.socket||!this.active)return this.log("handleData: no active socket, ignoring data"),0;this.log(`handleData: received ${e.length} bytes`),this.messageBuffer=Buffer.concat([this.messageBuffer,e]),this.inspectData("incoming",e);try{let t=0;for(;this.messageBuffer.length>0;){let i=0,r=!1;if(this.messageBuffer.length>=4){let n=this.messageBuffer.readInt32BE(0);if(this.messageBuffer.length>=8){let a=this.messageBuffer.readInt32BE(4);(a===196608||a===196608)&&(i=n,r=this.messageBuffer.length>=i)}!r&&this.messageBuffer.length>=5&&(i=1+this.messageBuffer.readInt32BE(1),r=this.messageBuffer.length>=i)}if(!r||i===0){this.log(`handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`);break}let o=this.messageBuffer.slice(0,i);if(this.messageBuffer=this.messageBuffer.slice(i),this.log(`handleData: processing message of ${o.length} bytes`),!this.active||!this.socket){this.log("handleData: socket no longer active, stopping processing");break}let h=await this.queryQueue.enqueue(this.id,new Uint8Array(o));this.log(`handleData: received ${h.length} bytes from PGlite`),this.inspectData("outgoing",h),h.length>0&&this.socket&&this.socket.writable&&this.active&&await new Promise((n,a)=>{this.log("handleData: writing response to socket"),this.socket?.writable?this.socket.write(Buffer.from(h),c=>{c?(this.log("handleData: error writing to socket:",c),a(c)):(this.log(`handleData: socket sent: ${h.length} bytes`),n(h.length))}):(this.log("handleData: socket no longer writable"),n(0))}).catch(n=>{throw this.log("handleData: failed to write to socket:",n),n}),t+=h.length}return this.dispatchEvent(new CustomEvent("data",{detail:{incoming:e.length,outgoing:t}})),t}catch(t){throw this.log("handleData: error processing data:",t),t}}handleError(e){if(!this.active){this.log("handleError: handler not active, ignoring error");return}e.message?.includes("ECONNRESET")?this.log("handleError: client disconnected (ECONNRESET) - normal behavior"):e.message?.includes("Idle timeout")?this.log("handleError: connection idle timeout"):this.log("handleError:",e),this.active=!1,this.dispatchEvent(new CustomEvent("error",{detail:e})),this.detach(!0)}handleClose(){this.log("handleClose: socket closed"),this.active=!1,this.dispatchEvent(new CustomEvent("close")),this.detach(!1)}inspectData(e,t){if(this.inspect){console.log("-".repeat(75)),console.log(e==="incoming"?"-> incoming":"<- outgoing",t.length,"bytes");for(let i=0;i<t.length;i+=16){let r=Math.min(16,t.length-i),o="";for(let n=0;n<16;n++)if(n<r){let a=t[i+n];o+=a.toString(16).padStart(2,"0")+" "}else o+=" ";let h="";for(let n=0;n<r;n++){let a=t[i+n];h+=a>=32&&a<=126?String.fromCharCode(a):"."}console.log(`${i.toString(16).padStart(8,"0")} ${o} ${h}`)}}}};l.nextHandlerId=1;var u=l,g=class extends EventTarget{constructor(e){super();this.server=null;this.active=!1;this.handlers=new Set;this.db=e.db,e.path?this.path=e.path:(typeof e.port=="number"?this.port=e.port??e.port:this.port=5432,this.host=e.host||"127.0.0.1"),this.inspect=e.inspect??!1,this.debug=e.debug??!1,this.idleTimeout=e.idleTimeout??0,this.maxConnections=e.maxConnections??1,this.queryQueue=new d(this.db,this.debug),this.log(`constructor: created server on ${this.getServerConn()}`),this.log(`constructor: max connections: ${this.maxConnections}`),this.idleTimeout>0&&this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)}log(e,...t){this.debug&&console.log(`[PGLiteSocketServer] ${e}`,...t)}async start(){if(this.log(`start: starting server on ${this.getServerConn()}`),this.server)throw new Error("Socket server already started");return await this.db.waitReady,this.active=!0,this.server=f(e=>{setImmediate(()=>this.handleConnection(e))}),this.server.maxConnections=this.maxConnections,new Promise((e,t)=>{if(!this.server)return t(new Error("Server not initialized"));if(this.server.on("error",i=>{this.log("start: server error:",i),this.dispatchEvent(new CustomEvent("error",{detail:i})),this.active||t(i)}),this.path)this.server.listen(this.path,()=>{this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{path:this.path}})),e()});else{let i=this.server;i.listen(this.port,this.host,()=>{let r=i.address();if(r===null||typeof r!="object")throw Error("Expected address info");this.port=r.port,this.log(`start: server listening on ${this.getServerConn()}`),this.dispatchEvent(new CustomEvent("listening",{detail:{port:this.port,host:this.host}})),e()})}})}getServerConn(){return this.path?this.path:`${this.host}:${this.port}`}async stop(){this.log("stop: stopping server"),this.active=!1,this.log(`stop: detaching ${this.handlers.size} handlers`);for(let e of this.handlers)e.detach(!0);return this.handlers.clear(),this.server?new Promise(e=>{if(!this.server)return e();this.server.close(()=>{this.log("stop: server closed"),this.server=null,this.dispatchEvent(new CustomEvent("close")),e()})}):(this.log("stop: server not running, nothing to do"),Promise.resolve())}async handleConnection(e){let t={clientAddress:e.remoteAddress||"unknown",clientPort:e.remotePort||0};if(this.log(`handleConnection: new connection from ${t.clientAddress}:${t.clientPort}`),this.log(`handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`),!this.active){this.log("handleConnection: server not active, closing connection");try{e.end()}catch(r){this.log("handleConnection: error closing socket:",r)}return}if(this.handlers.size>=this.maxConnections){this.log("handleConnection: max connections reached, rejecting"),e.write(Buffer.from(`Too many connections | ||
| `)),e.end();return}let i=new u({queryQueue:this.queryQueue,closeOnDetach:!0,inspect:this.inspect,debug:this.debug,idleTimeout:this.idleTimeout});this.handlers.add(i),i.addEventListener("error",r=>{let o=r.detail;o?.message?.includes("ECONNRESET")?this.log(`handler #${i.handlerId}: client disconnected (ECONNRESET)`):o?.message?.includes("Idle timeout")?this.log(`handler #${i.handlerId}: idle timeout`):this.log(`handler #${i.handlerId}: error:`,o)}),i.addEventListener("close",()=>{this.log(`handler #${i.handlerId}: closed`),this.handlers.delete(i),this.log(`handleConnection: active connections: ${this.handlers.size}`)});try{await i.attach(e),this.dispatchEvent(new CustomEvent("connection",{detail:t}))}catch(r){this.log("handleConnection: error attaching socket:",r),this.handlers.delete(i),this.dispatchEvent(new CustomEvent("error",{detail:r}));try{e.end()}catch(o){this.log("handleConnection: error closing socket:",o)}}}getStats(){return{activeConnections:this.handlers.size,queuedQueries:this.queryQueue.getQueueLength(),maxConnections:this.maxConnections}}};export{y as a,u as b,g as c}; | ||
| //# sourceMappingURL=chunk-JGAU6COP.js.map |
| {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { PGlite } from '@electric-sql/pglite'\nimport { type Server, type Socket, createServer } from 'net'\n\n// Connection queue timeout in milliseconds\nexport const CONNECTION_QUEUE_TIMEOUT = 60000 // 60 seconds\n\n/**\n * Represents a queued query waiting for PGlite access\n */\ninterface QueuedQuery {\n handlerId: number\n message: Uint8Array\n resolve: (result: Uint8Array) => void\n reject: (error: Error) => void\n timestamp: number\n}\n\n/**\n * Global query queue manager\n * Ensures only one query executes at a time in PGlite\n */\nclass QueryQueueManager {\n private queue: QueuedQuery[] = []\n private processing = false\n private db: PGlite\n private debug: boolean\n private lastHandlerId: null | number = null\n\n constructor(db: PGlite, debug = false) {\n this.db = db\n this.debug = debug\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[QueryQueueManager] ${message}`, ...args)\n }\n }\n\n async enqueue(handlerId: number, message: Uint8Array): Promise<Uint8Array> {\n return new Promise((resolve, reject) => {\n const query: QueuedQuery = {\n handlerId,\n message,\n resolve,\n reject,\n timestamp: Date.now(),\n }\n\n this.queue.push(query)\n this.log(\n `enqueued query from handler #${handlerId}, queue size: ${this.queue.length}`,\n )\n\n // Process queue if not already processing\n if (!this.processing) {\n this.processQueue()\n }\n })\n }\n\n private async processQueue(): Promise<void> {\n if (this.processing || this.queue.length === 0) {\n return\n }\n\n this.processing = true\n\n while (this.queue.length > 0) {\n let query\n\n if (this.db.isInTransaction() && this.lastHandlerId) {\n const i = this.queue.findIndex(\n (q) => q.handlerId === this.lastHandlerId,\n )\n if (i === -1) {\n // we didn't find any other query from the same client!\n this.log(\n `transaction started, but no query from the same handler id found in queue`,\n this.lastHandlerId,\n )\n query = null\n } else {\n query = this.queue.splice(i, 1)[0]\n }\n } else {\n query = this.queue.shift()\n }\n if (!query) break\n\n const waitTime = Date.now() - query.timestamp\n this.log(\n `processing query from handler #${query.handlerId} (waited ${waitTime}ms)`,\n )\n\n try {\n // Execute the query with exclusive access to PGlite\n const result = await this.db.runExclusive(async () => {\n return await this.db.execProtocolRaw(query.message)\n })\n\n this.log(\n `query from handler #${query.handlerId} completed, ${result.length} bytes`,\n )\n this.lastHandlerId = query.handlerId\n query.resolve(result)\n } catch (error) {\n this.log(`query from handler #${query.handlerId} failed:`, error)\n query.reject(error as Error)\n }\n }\n\n this.processing = false\n this.log(`queue processing complete, queue length is`, this.queue.length)\n }\n\n getQueueLength(): number {\n return this.queue.length\n }\n\n clearQueueForHandler(handlerId: number): void {\n const before = this.queue.length\n this.queue = this.queue.filter((q) => {\n if (q.handlerId === handlerId) {\n q.reject(new Error('Handler disconnected'))\n return false\n }\n return true\n })\n const removed = before - this.queue.length\n if (removed > 0) {\n this.log(`cleared ${removed} queries for handler #${handlerId}`)\n }\n }\n\n async clearTransactionIfNeeded(handlerId: number): Promise<void> {\n if (this.db.isInTransaction() && this.lastHandlerId === handlerId) {\n await this.db.exec('ROLLBACK')\n this.lastHandlerId = null\n await this.processQueue()\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketHandler\n */\nexport interface PGLiteSocketHandlerOptions {\n /** The query queue manager */\n queryQueue: QueryQueueManager\n /** Whether to close the socket when detached (default: false) */\n closeOnDetach?: boolean\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n}\n\n/**\n * Handler for a single socket connection to PGlite\n * Each connection can remain open and send multiple queries\n */\nexport class PGLiteSocketHandler extends EventTarget {\n private queryQueue: QueryQueueManager\n private socket: Socket | null = null\n private active = false\n private closeOnDetach: boolean\n private inspect: boolean\n private debug: boolean\n private readonly id: number\n private messageBuffer: Buffer = Buffer.alloc(0)\n private idleTimer?: NodeJS.Timeout\n private idleTimeout: number\n private lastActivityTime: number = Date.now()\n\n // Static counter for generating unique handler IDs\n private static nextHandlerId = 1\n\n constructor(options: PGLiteSocketHandlerOptions) {\n super()\n this.queryQueue = options.queryQueue\n this.closeOnDetach = options.closeOnDetach ?? false\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.id = PGLiteSocketHandler.nextHandlerId++\n\n this.log('constructor: created new handler')\n }\n\n public get handlerId(): number {\n return this.id\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketHandler#${this.id}] ${message}`, ...args)\n }\n }\n\n public async attach(socket: Socket): Promise<PGLiteSocketHandler> {\n this.log(\n `attach: attaching socket from ${socket.remoteAddress}:${socket.remotePort}`,\n )\n\n if (this.socket) {\n throw new Error('Socket already attached')\n }\n\n this.socket = socket\n this.active = true\n this.lastActivityTime = Date.now()\n\n // Set up socket options\n socket.setNoDelay(true)\n\n // Set up idle timeout if configured\n if (this.idleTimeout > 0) {\n this.resetIdleTimer()\n }\n\n // Setup event handlers\n this.log(`attach: setting up socket event handlers`)\n\n socket.on('data', (data) => {\n this.lastActivityTime = Date.now()\n this.resetIdleTimer()\n\n setImmediate(async () => {\n try {\n await this.handleData(data)\n } catch (err) {\n this.log('socket on data error: ', err)\n this.handleError(err as Error)\n }\n })\n })\n\n socket.on('error', (err) => {\n setImmediate(() => this.handleError(err))\n })\n\n socket.on('close', () => {\n setImmediate(() => this.handleClose())\n })\n\n this.log(`attach: socket handler ready`)\n return this\n }\n\n private resetIdleTimer(): void {\n if (this.idleTimeout <= 0) return\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n }\n\n this.idleTimer = setTimeout(() => {\n const idleTime = Date.now() - this.lastActivityTime\n this.log(`idle timeout after ${idleTime}ms`)\n this.handleError(new Error('Idle timeout'))\n }, this.idleTimeout)\n }\n\n public async detach(close?: boolean): Promise<PGLiteSocketHandler> {\n this.log(`detach: detaching socket, close=${close ?? this.closeOnDetach}`)\n\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n this.idleTimer = undefined\n }\n\n // Clear any pending queries for this handler\n this.queryQueue.clearQueueForHandler(this.id)\n\n await this.queryQueue.clearTransactionIfNeeded(this.id)\n\n if (!this.socket) {\n this.log(`detach: no socket attached, nothing to do`)\n return this\n }\n\n // Remove all listeners\n this.socket.removeAllListeners('data')\n this.socket.removeAllListeners('error')\n this.socket.removeAllListeners('close')\n\n // Close the socket if requested\n if (close ?? this.closeOnDetach) {\n if (this.socket.writable) {\n this.log(`detach: closing socket`)\n try {\n this.socket.end()\n this.socket.destroy()\n } catch (err) {\n this.log(`detach: error closing socket:`, err)\n }\n }\n }\n\n this.socket = null\n this.active = false\n this.messageBuffer = Buffer.alloc(0)\n\n this.log(`detach: handler cleaned up`)\n return this\n }\n\n public get isAttached(): boolean {\n return this.socket !== null\n }\n\n private async handleData(data: Buffer): Promise<number> {\n if (!this.socket || !this.active) {\n this.log(`handleData: no active socket, ignoring data`)\n return 0\n }\n\n this.log(`handleData: received ${data.length} bytes`)\n\n // Append to buffer for message reassembly\n this.messageBuffer = Buffer.concat([this.messageBuffer, data])\n\n // Print the incoming data to the console\n this.inspectData('incoming', data)\n\n try {\n let totalProcessed = 0\n\n while (this.messageBuffer.length > 0) {\n // Determine message length\n let messageLength = 0\n let isComplete = false\n\n // Handle startup message (no type byte, just length)\n if (this.messageBuffer.length >= 4) {\n const firstInt = this.messageBuffer.readInt32BE(0)\n\n if (this.messageBuffer.length >= 8) {\n const secondInt = this.messageBuffer.readInt32BE(4)\n // PostgreSQL 3.0 protocol version\n if (secondInt === 196608 || secondInt === 0x00030000) {\n messageLength = firstInt\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n // Regular message (type byte + length)\n if (!isComplete && this.messageBuffer.length >= 5) {\n const msgLength = this.messageBuffer.readInt32BE(1)\n messageLength = 1 + msgLength\n isComplete = this.messageBuffer.length >= messageLength\n }\n }\n\n if (!isComplete || messageLength === 0) {\n this.log(\n `handleData: incomplete message, buffering ${this.messageBuffer.length} bytes`,\n )\n break\n }\n\n // Extract and process complete message\n const message = this.messageBuffer.slice(0, messageLength)\n this.messageBuffer = this.messageBuffer.slice(messageLength)\n\n this.log(`handleData: processing message of ${message.length} bytes`)\n\n // Check if socket is still active before processing\n if (!this.active || !this.socket) {\n this.log(`handleData: socket no longer active, stopping processing`)\n break\n }\n\n // Queue the query for execution\n // This allows multiple connections to queue queries simultaneously\n const result = await this.queryQueue.enqueue(\n this.id,\n new Uint8Array(message),\n )\n\n this.log(`handleData: received ${result.length} bytes from PGlite`)\n\n // Print the outgoing data to the console\n this.inspectData('outgoing', result)\n\n // Send response if available\n if (\n result.length > 0 &&\n this.socket &&\n this.socket.writable &&\n this.active\n ) {\n await new Promise<number>((resolve, reject) => {\n this.log(`handleData: writing response to socket`)\n if (this.socket?.writable) {\n this.socket.write(Buffer.from(result), (err?: any) => {\n if (err) {\n this.log(`handleData: error writing to socket:`, err)\n reject(err)\n } else {\n this.log(`handleData: socket sent: ${result.length} bytes`)\n resolve(result.length)\n }\n })\n } else {\n this.log(`handleData: socket no longer writable`)\n resolve(0)\n }\n }).catch((writeErr) => {\n this.log(`handleData: failed to write to socket:`, writeErr)\n throw writeErr\n })\n }\n\n totalProcessed += result.length\n }\n\n // Emit data event with byte sizes\n this.dispatchEvent(\n new CustomEvent('data', {\n detail: { incoming: data.length, outgoing: totalProcessed },\n }),\n )\n\n return totalProcessed\n } catch (err) {\n this.log(`handleData: error processing data:`, err)\n throw err\n }\n }\n\n private handleError(err: Error): void {\n if (!this.active) {\n this.log(`handleError: handler not active, ignoring error`)\n return\n }\n\n // ECONNRESET is expected behavior when clients disconnect\n if (err.message?.includes('ECONNRESET')) {\n this.log(\n `handleError: client disconnected (ECONNRESET) - normal behavior`,\n )\n } else if (err.message?.includes('Idle timeout')) {\n this.log(`handleError: connection idle timeout`)\n } else {\n this.log(`handleError:`, err)\n }\n\n this.active = false\n\n // Emit error event\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n\n // Clean up\n this.detach(true)\n }\n\n private handleClose(): void {\n this.log(`handleClose: socket closed`)\n this.active = false\n this.dispatchEvent(new CustomEvent('close'))\n this.detach(false)\n }\n\n private inspectData(\n direction: 'incoming' | 'outgoing',\n data: Buffer | Uint8Array,\n ): void {\n if (!this.inspect) return\n console.log('-'.repeat(75))\n if (direction === 'incoming') {\n console.log('-> incoming', data.length, 'bytes')\n } else {\n console.log('<- outgoing', data.length, 'bytes')\n }\n\n for (let offset = 0; offset < data.length; offset += 16) {\n const chunkSize = Math.min(16, data.length - offset)\n\n let hexPart = ''\n for (let i = 0; i < 16; i++) {\n if (i < chunkSize) {\n const byte = data[offset + i]\n hexPart += byte.toString(16).padStart(2, '0') + ' '\n } else {\n hexPart += ' '\n }\n }\n\n let asciiPart = ''\n for (let i = 0; i < chunkSize; i++) {\n const byte = data[offset + i]\n asciiPart += byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : '.'\n }\n\n console.log(\n `${offset.toString(16).padStart(8, '0')} ${hexPart} ${asciiPart}`,\n )\n }\n }\n}\n\n/**\n * Options for creating a PGLiteSocketServer\n */\nexport interface PGLiteSocketServerOptions {\n /** The PGlite database instance */\n db: PGlite\n /** The port to listen on (default: 5432) */\n port?: number\n /** The host to bind to (default: 127.0.0.1) */\n host?: string\n /** Unix socket path to bind to (default: undefined) */\n path?: string\n /** Print the incoming and outgoing data to the console in hex and ascii */\n inspect?: boolean\n /** Enable debug logging of method calls */\n debug?: boolean\n /** Idle timeout in ms (0 to disable, default: 0) */\n idleTimeout?: number\n /** Maximum concurrent connections (default: 100) */\n maxConnections?: number\n}\n\n/**\n * PGLite Socket Server with support for multiple concurrent connections\n * Connections remain open and queries are queued at the query level\n */\nexport class PGLiteSocketServer extends EventTarget {\n readonly db: PGlite\n private server: Server | null = null\n private port?: number\n private host?: string\n private path?: string\n private active = false\n private inspect: boolean\n private debug: boolean\n private idleTimeout: number\n private maxConnections: number\n private handlers: Set<PGLiteSocketHandler> = new Set()\n private queryQueue: QueryQueueManager\n\n constructor(options: PGLiteSocketServerOptions) {\n super()\n this.db = options.db\n if (options.path) {\n this.path = options.path\n } else {\n if (typeof options.port === 'number') {\n // Keep port undefined on port 0, will be set by the OS when we start the server.\n this.port = options.port ?? options.port\n } else {\n this.port = 5432\n }\n this.host = options.host || '127.0.0.1'\n }\n this.inspect = options.inspect ?? false\n this.debug = options.debug ?? false\n this.idleTimeout = options.idleTimeout ?? 0\n this.maxConnections = options.maxConnections ?? 1\n\n // Create the shared query queue\n this.queryQueue = new QueryQueueManager(this.db, this.debug)\n\n this.log(`constructor: created server on ${this.getServerConn()}`)\n this.log(`constructor: max connections: ${this.maxConnections}`)\n if (this.idleTimeout > 0) {\n this.log(`constructor: idle timeout: ${this.idleTimeout}ms`)\n }\n }\n\n private log(message: string, ...args: any[]): void {\n if (this.debug) {\n console.log(`[PGLiteSocketServer] ${message}`, ...args)\n }\n }\n\n public async start(): Promise<void> {\n this.log(`start: starting server on ${this.getServerConn()}`)\n\n if (this.server) {\n throw new Error('Socket server already started')\n }\n\n // Ensure PGlite is ready before accepting connections\n await this.db.waitReady\n\n this.active = true\n this.server = createServer((socket) => {\n setImmediate(() => this.handleConnection(socket))\n })\n\n this.server.maxConnections = this.maxConnections\n\n return new Promise<void>((resolve, reject) => {\n if (!this.server) return reject(new Error('Server not initialized'))\n\n this.server.on('error', (err) => {\n this.log(`start: server error:`, err)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n if (!this.active) {\n reject(err)\n }\n })\n\n if (this.path) {\n this.server.listen(this.path, () => {\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { path: this.path },\n }),\n )\n resolve()\n })\n } else {\n const server = this.server\n server.listen(this.port, this.host, () => {\n const address = server.address()\n // We are not using pipes, so return type should be AddressInfo\n if (address === null || typeof address !== 'object') {\n throw Error('Expected address info')\n }\n // Assign the new port number\n this.port = address.port\n this.log(`start: server listening on ${this.getServerConn()}`)\n this.dispatchEvent(\n new CustomEvent('listening', {\n detail: { port: this.port, host: this.host },\n }),\n )\n resolve()\n })\n }\n })\n }\n\n public getServerConn(): string {\n if (this.path) return this.path\n return `${this.host}:${this.port}`\n }\n\n public async stop(): Promise<void> {\n this.log(`stop: stopping server`)\n\n this.active = false\n\n // Detach all handlers\n this.log(`stop: detaching ${this.handlers.size} handlers`)\n for (const handler of this.handlers) {\n handler.detach(true)\n }\n this.handlers.clear()\n\n if (!this.server) {\n this.log(`stop: server not running, nothing to do`)\n return Promise.resolve()\n }\n\n return new Promise<void>((resolve) => {\n if (!this.server) return resolve()\n\n this.server.close(() => {\n this.log(`stop: server closed`)\n this.server = null\n this.dispatchEvent(new CustomEvent('close'))\n resolve()\n })\n })\n }\n\n private async handleConnection(socket: Socket): Promise<void> {\n const clientInfo = {\n clientAddress: socket.remoteAddress || 'unknown',\n clientPort: socket.remotePort || 0,\n }\n\n this.log(\n `handleConnection: new connection from ${clientInfo.clientAddress}:${clientInfo.clientPort}`,\n )\n this.log(\n `handleConnection: active connections: ${this.handlers.size}, queued queries: ${this.queryQueue.getQueueLength()}`,\n )\n\n if (!this.active) {\n this.log(`handleConnection: server not active, closing connection`)\n try {\n socket.end()\n } catch (err) {\n this.log(`handleConnection: error closing socket:`, err)\n }\n return\n }\n\n // Check connection limit\n if (this.handlers.size >= this.maxConnections) {\n this.log(`handleConnection: max connections reached, rejecting`)\n socket.write(Buffer.from('Too many connections\\n'))\n socket.end()\n return\n }\n\n // Create a new handler for this connection\n const handler = new PGLiteSocketHandler({\n queryQueue: this.queryQueue,\n closeOnDetach: true,\n inspect: this.inspect,\n debug: this.debug,\n idleTimeout: this.idleTimeout,\n })\n\n // Track this handler\n this.handlers.add(handler)\n\n // Handle errors\n handler.addEventListener('error', (event) => {\n const error = (event as CustomEvent<Error>).detail\n\n if (error?.message?.includes('ECONNRESET')) {\n this.log(\n `handler #${handler.handlerId}: client disconnected (ECONNRESET)`,\n )\n } else if (error?.message?.includes('Idle timeout')) {\n this.log(`handler #${handler.handlerId}: idle timeout`)\n } else {\n this.log(`handler #${handler.handlerId}: error:`, error)\n }\n })\n\n // Handle close event\n handler.addEventListener('close', () => {\n this.log(`handler #${handler.handlerId}: closed`)\n this.handlers.delete(handler)\n this.log(`handleConnection: active connections: ${this.handlers.size}`)\n })\n\n try {\n await handler.attach(socket)\n this.dispatchEvent(new CustomEvent('connection', { detail: clientInfo }))\n } catch (err) {\n this.log(`handleConnection: error attaching socket:`, err)\n this.handlers.delete(handler)\n this.dispatchEvent(new CustomEvent('error', { detail: err }))\n try {\n socket.end()\n } catch (closeErr) {\n this.log(`handleConnection: error closing socket:`, closeErr)\n }\n }\n }\n\n public getStats() {\n return {\n activeConnections: this.handlers.size,\n queuedQueries: this.queryQueue.getQueueLength(),\n maxConnections: this.maxConnections,\n }\n }\n}\n"],"mappings":"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","names":["createServer","CONNECTION_QUEUE_TIMEOUT","QueryQueueManager","db","debug","message","args","handlerId","resolve","reject","query","i","q","waitTime","result","error","before","removed","_PGLiteSocketHandler","options","socket","data","err","idleTime","close","totalProcessed","messageLength","isComplete","firstInt","secondInt","writeErr","direction","offset","chunkSize","hexPart","byte","asciiPart","PGLiteSocketHandler","PGLiteSocketServer","server","address","handler","clientInfo","event","closeErr"]} |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
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 8 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
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
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
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 8 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
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
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
325136
0.43%3508
0.37%2
100%10
11.11%