prisma-pglite-bridge 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +90 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -5
- package/dist/index.d.mts +12 -5
- package/dist/index.mjs +90 -75
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -15
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["diagnostics_channel","Duplex","#ownsPglite","PGlite","quoteIdent","#pglite","#hasSnapshot","#snapshotSchemaExists","#getTables","#withReplicationRoleReplica","#pool","#stats","#snapshot","#leakToken","#ownsPglite","PGlite","PrismaPg","#assertPoolIdle","#closing","#options","#server","#sessionLock","#sockets","#ownsPglite","PGlite","net","#onConnection","#connectionString","nodePath","#initDuplex"],"sources":["../src/telemetry/diagnostics.ts","../src/utils/time.ts","../src/utils/wait-pglite-ready.ts","../src/duplex/constants.ts","../src/duplex/row-description.ts","../src/duplex/backend-framer.ts","../src/duplex/frontend-buffer.ts","../src/duplex/index.ts","../src/utils/resolve-sync-to-fs.ts","../src/utils/session-lock.ts","../src/pool/fast-array-parsers.ts","../src/pool/pg-bridge-client.ts","../src/pool/index.ts","../src/telemetry/bridge-stats.ts","../src/pglite-bridge/snapshot-manager.ts","../src/pglite-bridge/index.ts","../src/pglite-server/index.ts","../src/schema/index.ts","../src/schema/migrations.ts"],"sourcesContent":["/**\n * Public `node:diagnostics_channel` surface.\n *\n * The bridge publishes per-query and per-lock-wait events to named\n * channels. External consumers (OpenTelemetry, APM tools, custom\n * loggers) can subscribe without touching the library API. Built-in\n * bridge stats are updated directly from the bridge and are\n * independent of these public channels.\n *\n * Publication is gated by `channel.hasSubscribers`, so the hot path\n * pays no cost when nobody is listening. Subscribing opts the consumer\n * in to the timing/publication overhead.\n *\n * Filter on `bridgeId` to distinguish events from different bridges\n * in the same process — read `bridge.bridgeId` (on `PGliteBridge`) or\n * `pool.bridgeId` (on `PgBridgePool`).\n */\nimport diagnostics_channel from 'node:diagnostics_channel';\n\n/**\n * `node:diagnostics_channel` name for per-query events. Subscribers receive\n * a {@link QueryEvent} each time the bridge completes a query.\n */\nexport const QUERY_CHANNEL = 'prisma-pglite-bridge:query';\n\n/**\n * `node:diagnostics_channel` name for per-acquisition session-lock wait\n * events. Subscribers receive a {@link LockWaitEvent} after each\n * acquisition completes.\n */\nexport const LOCK_WAIT_CHANNEL = 'prisma-pglite-bridge:lock-wait';\n\n/** Payload published to {@link QUERY_CHANNEL}. */\nexport interface QueryEvent {\n /** Bridge identity tag — filter on this to isolate one bridge's events. */\n bridgeId: symbol;\n /** Wall-clock duration of the query in milliseconds. */\n durationMs: number;\n /** `false` when the query rejected (protocol or SQL error). */\n succeeded: boolean;\n}\n\n/** Payload published to {@link LOCK_WAIT_CHANNEL}. */\nexport interface LockWaitEvent {\n /** Bridge identity tag — filter on this to isolate one bridge's events. */\n bridgeId: symbol;\n /** Time spent waiting to acquire the session lock, in milliseconds. */\n durationMs: number;\n}\n\nexport const queryChannel: diagnostics_channel.Channel = diagnostics_channel.channel(QUERY_CHANNEL);\nexport const lockWaitChannel: diagnostics_channel.Channel =\n diagnostics_channel.channel(LOCK_WAIT_CHANNEL);\n","/** Convert a nanosecond `bigint` (as returned by `process.hrtime.bigint()`) to milliseconds. */\nexport const nsToMs = (ns: bigint): number => Number(ns) / 1_000_000;\n","import type { PGlite, PGliteInterface } from '@electric-sql/pglite';\n\nconst withTimeout = async <T>(promise: Promise<T>, ms: number): Promise<T> => {\n let timer: ReturnType<typeof setTimeout>;\n\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`Operation timed out after ${ms}ms`));\n }, ms);\n });\n\n return Promise.race([promise, timeout]).finally(() => {\n clearTimeout(timer);\n });\n};\n\nexport const waitPGliteReady = async (\n pglite: PGlite | PGliteInterface,\n ms: number = Number.POSITIVE_INFINITY,\n): Promise<void> => {\n if (pglite.ready) return;\n if (pglite.closed) throw new Error('PGlite instance closed');\n\n try {\n await (Number.isFinite(ms) ? withTimeout(pglite.waitReady, ms) : pglite.waitReady);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n throw new Error(`PGlite instance not ready: ${msg}`);\n }\n};\n","// Frontend message types\nconst PARSE = 0x50; // P\nconst BIND = 0x42; // B\nconst DESCRIBE = 0x44; // D\nconst EXECUTE = 0x45; // E\nconst CLOSE = 0x43; // C\nconst FLUSH = 0x48; // H\nexport const SYNC = 0x53; // S (frontend)\nexport const TERMINATE = 0x58; // X\n\n// Backend message types\nexport const READY_FOR_QUERY = 0x5a; // Z — 6 bytes: Z + length(5) + status\nexport const ERROR_RESPONSE = 0x45; // E — signals in-band SQL error (not a JS throw)\nexport const ROW_DESCRIPTION = 0x54; // T — column metadata; rewritten to widen oid 18\n\n// ReadyForQuery transaction status bytes (RFC: pg wire protocol §53.7)\nexport const RFQ_STATUS_IDLE = 0x49; // 'I' — no transaction\nexport const RFQ_STATUS_IN_TRANSACTION = 0x54; // 'T' — in transaction block\nexport const RFQ_STATUS_FAILED = 0x45; // 'E' — failed transaction block\n\n// pg_catalog system columns (pg_constraint.contype, pg_type.typtype, pg_class.relkind,\n// etc.) report type oid 18 (\"char\", 1-byte). @prisma/adapter-pg's fieldToColumnType\n// has no case for oid 18 and throws UnsupportedNativeDataType. The bytes on the wire\n// for these single-ASCII-char values decode identically as text, so we widen oid 18\n// to oid 25 (text) in RowDescription frames before pg.Client sees them — but only\n// when the field originates from a pg_catalog relation (tableOID below\n// FirstNormalObjectId). User-defined \"char\" columns are left untouched so the\n// upstream type-mapping gap surfaces instead of being silently papered over with a\n// possibly-lossy text decode. Remove this rewrite once @prisma/adapter-pg gains a\n// fieldToColumnType case for oid 18.\nexport const PG_TYPE_OID_CHAR = 18;\nexport const PG_TYPE_OID_TEXT = 25;\n// Mirrors PostgreSQL's FirstNormalObjectId (src/include/access/transam.h): every\n// system catalog relation has an OID below this; user-created objects start at it.\nexport const PG_FIRST_USER_OID = 16384;\n\n// Extended Query Protocol message types — must be batched until Sync\nexport const EQP_MESSAGES: ReadonlySet<number> = new Set([\n PARSE,\n BIND,\n DESCRIBE,\n EXECUTE,\n CLOSE,\n FLUSH,\n]);\n\n/**\n * Upper bound on a single backend message length declared in its 4-byte\n * header. PostgreSQL's own wire protocol maxes out around 1 GiB per\n * message; anything larger indicates a corrupted or hostile stream and\n * must not be allocated against.\n */\nexport const MAX_BACKEND_MESSAGE_LENGTH = 1_073_741_824;\n","import { PG_FIRST_USER_OID, PG_TYPE_OID_CHAR, PG_TYPE_OID_TEXT } from './constants.ts';\n\nconst readUInt32BE = (buf: Uint8Array, offset: number): number => {\n /* c8 ignore start — callers guard fixed-width reads */\n const b1 = buf[offset] ?? 0;\n const b2 = buf[offset + 1] ?? 0;\n const b3 = buf[offset + 2] ?? 0;\n const b4 = buf[offset + 3] ?? 0;\n /* c8 ignore stop */\n return ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) >>> 0;\n};\n\nconst readUInt16BE = (buf: Uint8Array, offset: number): number => {\n /* c8 ignore start — callers guard fixed-width reads */\n const b1 = buf[offset] ?? 0;\n const b2 = buf[offset + 1] ?? 0;\n /* c8 ignore stop */\n return (b1 << 8) | b2;\n};\n\nexport const rowDescriptionNeedsRewrite = (\n buf: Uint8Array,\n start: number = 0,\n end: number = buf.length,\n): boolean => {\n if (end - start < 7) return false;\n\n const fieldCount = readUInt16BE(buf, start + 5);\n let p = start + 7;\n for (let i = 0; i < fieldCount; i++) {\n while (p < end && buf[p] !== 0) p++;\n p++; // NUL terminator\n /* c8 ignore next — defense-in-depth: framer caller passes a complete frame */\n if (p + 18 > end) return false;\n const tableOID = readUInt32BE(buf, p);\n p += 4 + 2; // tableOID, columnAttr\n const oid = readUInt32BE(buf, p);\n if (oid === PG_TYPE_OID_CHAR && tableOID !== 0 && tableOID < PG_FIRST_USER_OID) {\n return true;\n }\n p += 4 + 2 + 4 + 2; // dataTypeOID, dataTypeSize, typeModifier, formatCode\n }\n\n return false;\n};\n\n/**\n * Widens any field whose dataTypeOID is 18 (\"char\") to oid 25 (text) — but\n * only when the field originates from a pg_catalog relation (tableOID is\n * non-zero and below FirstNormalObjectId). Single-ASCII-byte payloads from\n * pg_catalog views (pg_constraint.contype, pg_type.typtype, etc.) decode\n * identically as text, so the row data needs no transformation. Fields with\n * tableOID === 0 (computed expressions) and user-table fields are left\n * untouched, since arbitrary bytes in user \"char\" data can't safely be\n * relabelled as text. Frame length is unchanged because all rewrites are\n * fixed-size in place.\n */\nexport const rewriteRowDescriptionInPlace = (buf: Buffer): void => {\n if (!rowDescriptionNeedsRewrite(buf)) return;\n\n const fieldCount = buf.readInt16BE(5);\n let p = 7;\n for (let i = 0; i < fieldCount; i++) {\n while (p < buf.length && buf[p] !== 0) p++;\n p++; // NUL terminator\n // Per-field fixed suffix is 18 bytes: tableOID(4) + columnAttr(2) +\n // dataTypeOID(4) + dataTypeSize(2) + typeModifier(4) + formatCode(2).\n // Bail if the frame is truncated rather than throwing RangeError mid-loop.\n /* c8 ignore next — defense-in-depth: framer caller passes a complete frame */\n if (p + 18 > buf.length) return;\n const tableOID = buf.readUInt32BE(p);\n p += 4 + 2; // tableOID, columnAttr\n const oid = buf.readUInt32BE(p);\n if (oid === PG_TYPE_OID_CHAR && tableOID !== 0 && tableOID < PG_FIRST_USER_OID) {\n buf.writeUInt32BE(PG_TYPE_OID_TEXT, p);\n buf.writeInt16BE(-1, p + 4);\n }\n p += 4 + 2 + 4 + 2; // dataTypeOID, dataTypeSize, typeModifier, formatCode\n }\n};\n","import {\n ERROR_RESPONSE,\n MAX_BACKEND_MESSAGE_LENGTH,\n READY_FOR_QUERY,\n ROW_DESCRIPTION,\n} from './constants.ts';\nimport { rewriteRowDescriptionInPlace, rowDescriptionNeedsRewrite } from './row-description.ts';\n\ntype BackendMessageFramerOptions = {\n suppressIntermediateReadyForQuery?: boolean;\n onChunk: (chunk: Uint8Array) => void;\n onErrorResponse?: () => void;\n onReadyForQuery?: (status: number) => void;\n};\n\n/**\n * Streams backend protocol messages without materializing whole responses.\n *\n * Non-RFQ payload bytes are forwarded as they arrive. ReadyForQuery frames are\n * tracked only once complete; when suppression is enabled, only the final RFQ\n * is emitted.\n *\n * @internal — exported for testing only\n */\nexport class BackendMessageFramer {\n private readonly suppressIntermediateReadyForQuery: boolean;\n private readonly onChunk: (chunk: Uint8Array) => void;\n private readonly onErrorResponse?: () => void;\n private readonly onReadyForQuery?: (status: number) => void;\n private readonly headerScratch = new Uint8Array(4);\n private readonly heldRfq = new Uint8Array(6);\n private messageType?: number;\n private headerBytesRead = 0;\n private payloadBytesRemaining = 0;\n private rfqBytesRead = 0;\n /** Slow-path RowDescription accumulator. Set once the T-frame header is decoded\n * and a multi-chunk payload starts arriving; cleared after rewrite + emit. */\n private rowDescBuffer?: Buffer;\n private rowDescOffset = 0;\n\n constructor(options: BackendMessageFramerOptions) {\n this.suppressIntermediateReadyForQuery = options.suppressIntermediateReadyForQuery ?? false;\n this.onChunk = options.onChunk;\n this.onErrorResponse = options.onErrorResponse;\n this.onReadyForQuery = options.onReadyForQuery;\n }\n\n write(chunk: Uint8Array): void {\n if (chunk.length === 0) return;\n\n let offset = 0;\n let passthroughStart = -1;\n const flushPassthrough = (end: number): void => {\n if (passthroughStart >= 0 && end > passthroughStart) {\n this.emitChunkSlice(chunk, passthroughStart, end);\n passthroughStart = -1;\n }\n };\n while (offset < chunk.length) {\n if (this.messageType === undefined) {\n // Fast path: if type + 4-byte header + full payload are all in this\n // chunk, emit the whole message as one slice. Avoids the per-message\n // prefix allocation + two downstream pushes that the byte-state-machine\n // path below performs. Falls through to the slow path when the message\n // spans chunks.\n const available = chunk.length - offset;\n if (available >= 5) {\n /* c8 ignore start — bounds guaranteed by `available >= 5` */\n const msgType = chunk[offset] ?? 0;\n const b1 = chunk[offset + 1] ?? 0;\n const b2 = chunk[offset + 2] ?? 0;\n const b3 = chunk[offset + 3] ?? 0;\n const b4 = chunk[offset + 4] ?? 0;\n /* c8 ignore stop */\n const messageLength = ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) >>> 0;\n if (messageLength < 4) {\n throw new Error(`Malformed backend message length: ${messageLength}`);\n }\n if (messageLength > MAX_BACKEND_MESSAGE_LENGTH) {\n throw new Error(\n `Backend message length ${messageLength} exceeds sanity cap ${MAX_BACKEND_MESSAGE_LENGTH}`,\n );\n }\n const totalLen = 1 + messageLength;\n if (available >= totalLen) {\n if (msgType === ERROR_RESPONSE) {\n this.onErrorResponse?.();\n }\n if (msgType === READY_FOR_QUERY && messageLength === 5) {\n flushPassthrough(offset);\n if (this.suppressIntermediateReadyForQuery && this.rfqBytesRead === 6) {\n this.dropHeldReadyForQuery();\n }\n /* c8 ignore next — messageLength === 5 for RFQ; payload is 1 byte */\n const status = chunk[offset + 5] ?? 0;\n this.heldRfq[0] = msgType;\n this.heldRfq[1] = b1;\n this.heldRfq[2] = b2;\n this.heldRfq[3] = b3;\n this.heldRfq[4] = b4;\n this.heldRfq[5] = status;\n this.rfqBytesRead = 6;\n this.onReadyForQuery?.(status);\n if (!this.suppressIntermediateReadyForQuery) {\n this.emitReadyForQuery();\n this.rfqBytesRead = 0;\n }\n } else if (msgType === ROW_DESCRIPTION) {\n if (rowDescriptionNeedsRewrite(chunk, offset, offset + totalLen)) {\n flushPassthrough(offset);\n if (this.suppressIntermediateReadyForQuery && this.rfqBytesRead === 6) {\n this.dropHeldReadyForQuery();\n }\n this.emitRewrittenRowDescription(\n Buffer.from(chunk.subarray(offset, offset + totalLen)),\n );\n } else {\n if (this.suppressIntermediateReadyForQuery && this.rfqBytesRead === 6) {\n this.dropHeldReadyForQuery();\n }\n if (passthroughStart < 0) {\n passthroughStart = offset;\n }\n }\n } else {\n if (this.suppressIntermediateReadyForQuery && this.rfqBytesRead === 6) {\n this.dropHeldReadyForQuery();\n }\n if (passthroughStart < 0) {\n passthroughStart = offset;\n }\n }\n offset += totalLen;\n continue;\n }\n }\n\n flushPassthrough(offset);\n if (this.suppressIntermediateReadyForQuery && this.rfqBytesRead === 6) {\n this.dropHeldReadyForQuery();\n }\n /* c8 ignore next — offset < chunk.length guaranteed by outer while */\n this.messageType = chunk[offset] ?? 0;\n this.headerBytesRead = 0;\n this.payloadBytesRemaining = 0;\n this.rfqBytesRead = this.messageType === READY_FOR_QUERY ? 1 : 0;\n if (this.rfqBytesRead === 1) {\n this.heldRfq[0] = this.messageType;\n }\n offset++;\n continue;\n }\n\n if (this.headerBytesRead < 4) {\n const bytesToCopy = Math.min(4 - this.headerBytesRead, chunk.length - offset);\n const headerChunk = chunk.subarray(offset, offset + bytesToCopy);\n this.headerScratch.set(headerChunk, this.headerBytesRead);\n if (this.messageType === READY_FOR_QUERY) {\n this.heldRfq.set(headerChunk, this.rfqBytesRead);\n this.rfqBytesRead += bytesToCopy;\n }\n this.headerBytesRead += bytesToCopy;\n offset += bytesToCopy;\n if (this.headerBytesRead < 4) continue;\n\n /* c8 ignore start — header bytes all populated before read */\n const b1 = this.headerScratch[0] ?? 0;\n const b2 = this.headerScratch[1] ?? 0;\n const b3 = this.headerScratch[2] ?? 0;\n const b4 = this.headerScratch[3] ?? 0;\n /* c8 ignore stop */\n const messageLength = ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) >>> 0;\n if (messageLength < 4) {\n throw new Error(`Malformed backend message length: ${messageLength}`);\n }\n if (messageLength > MAX_BACKEND_MESSAGE_LENGTH) {\n throw new Error(\n `Backend message length ${messageLength} exceeds sanity cap ${MAX_BACKEND_MESSAGE_LENGTH}`,\n );\n }\n\n this.payloadBytesRemaining = messageLength - 4;\n\n if (this.messageType === ERROR_RESPONSE) {\n this.onErrorResponse?.();\n }\n\n if (this.isReadyForQueryFrame()) {\n continue;\n }\n\n this.dropHeldReadyForQuery();\n if (this.messageType === ROW_DESCRIPTION) {\n // Valid RowDescription always carries at least a 2-byte fieldCount,\n // so payloadBytesRemaining is > 0 here — no zero-payload finalize needed.\n this.rowDescBuffer = Buffer.alloc(5 + this.payloadBytesRemaining);\n this.rowDescBuffer[0] = ROW_DESCRIPTION;\n this.rowDescBuffer.set(this.headerScratch, 1);\n this.rowDescOffset = 5;\n continue;\n }\n this.emitPrefix();\n if (this.payloadBytesRemaining === 0) {\n this.finishMessage();\n }\n continue;\n }\n\n if (this.isReadyForQueryFrame()) {\n const bytesToCopy = Math.min(this.payloadBytesRemaining, chunk.length - offset);\n const payloadChunk = chunk.subarray(offset, offset + bytesToCopy);\n this.heldRfq.set(payloadChunk, this.rfqBytesRead);\n this.rfqBytesRead += bytesToCopy;\n this.payloadBytesRemaining -= bytesToCopy;\n offset += bytesToCopy;\n /* c8 ignore next 3 — bytesToCopy ≥ 1 consumes the 1-byte RFQ payload */\n if (this.payloadBytesRemaining === 0) {\n this.finishReadyForQuery();\n }\n continue;\n }\n\n const bytesToEmit = Math.min(this.payloadBytesRemaining, chunk.length - offset);\n /* c8 ignore next — bytesToEmit always ≥ 1 when reached */\n if (bytesToEmit > 0) {\n if (this.rowDescBuffer !== undefined) {\n this.rowDescBuffer.set(chunk.subarray(offset, offset + bytesToEmit), this.rowDescOffset);\n this.rowDescOffset += bytesToEmit;\n } else {\n this.emitChunkSlice(chunk, offset, offset + bytesToEmit);\n }\n this.payloadBytesRemaining -= bytesToEmit;\n offset += bytesToEmit;\n }\n if (this.payloadBytesRemaining === 0) {\n if (this.rowDescBuffer !== undefined) {\n const buf = this.rowDescBuffer;\n this.rowDescBuffer = undefined;\n this.emitRewrittenRowDescription(buf);\n }\n this.finishMessage();\n }\n }\n\n flushPassthrough(offset);\n }\n\n flush(options?: { dropHeldReadyForQuery?: boolean }): void {\n if (options?.dropHeldReadyForQuery === true) {\n this.dropHeldReadyForQuery();\n } else if (this.suppressIntermediateReadyForQuery && this.rfqBytesRead === 6) {\n this.emitReadyForQuery();\n this.rfqBytesRead = 0;\n }\n }\n\n reset(): void {\n this.messageType = undefined;\n this.headerBytesRead = 0;\n this.payloadBytesRemaining = 0;\n this.rfqBytesRead = 0;\n this.rowDescBuffer = undefined;\n this.rowDescOffset = 0;\n }\n\n private isReadyForQueryFrame(): boolean {\n return this.messageType === READY_FOR_QUERY && this.payloadBytesRemaining === 1;\n }\n\n private finishReadyForQuery(): void {\n const status = this.heldRfq[5];\n /* c8 ignore next — heldRfq[5] always populated before finishReadyForQuery */\n if (status !== undefined) {\n this.onReadyForQuery?.(status);\n }\n\n if (!this.suppressIntermediateReadyForQuery) {\n this.emitReadyForQuery();\n }\n\n this.finishMessage();\n }\n\n private emitReadyForQuery(): void {\n this.onChunk(this.heldRfq.slice(0, 6));\n }\n\n private dropHeldReadyForQuery(): void {\n this.rfqBytesRead = 0;\n }\n\n private emitPrefix(): void {\n const prefix = new Uint8Array(5);\n /* c8 ignore next — messageType always set when emitPrefix is called */\n prefix[0] = this.messageType ?? 0;\n prefix.set(this.headerScratch, 1);\n this.onChunk(prefix);\n }\n\n private emitRewrittenRowDescription(buf: Buffer): void {\n rewriteRowDescriptionInPlace(buf);\n this.onChunk(buf);\n }\n\n private emitChunkSlice(chunk: Uint8Array, start: number, end: number): void {\n const length = end - start;\n /* c8 ignore next — callers pass end > start */\n if (length <= 0) return;\n\n // PGlite already hands us standalone Uint8Array chunks copied out of the\n // WASM heap, so when this chunk owns its full backing store we can hand pg\n // zero-copy Buffer views for arbitrary subranges. We still copy when the\n // chunk is a view into a larger backing buffer (to avoid pinning unrelated\n // trailing bytes) or when the backing store is shared (to prevent the WASM\n // runtime from mutating bytes pg is still consuming).\n if (\n chunk.byteOffset === 0 &&\n chunk.byteLength === chunk.buffer.byteLength &&\n !(chunk.buffer instanceof SharedArrayBuffer)\n ) {\n this.onChunk(Buffer.from(chunk.buffer, start, length));\n return;\n }\n\n const exact = Buffer.from(chunk.subarray(start, end));\n this.onChunk(exact);\n }\n\n private finishMessage(): void {\n this.messageType = undefined;\n this.headerBytesRead = 0;\n this.payloadBytesRemaining = 0;\n if (!this.suppressIntermediateReadyForQuery) {\n this.rfqBytesRead = 0;\n }\n }\n}\n","/**\n * Frontend chunk queue that frames messages without repeatedly compacting\n * the full buffered input.\n *\n * @internal — exported for testing only\n */\nexport class FrontendMessageBuffer {\n private chunks: Uint8Array[] = [];\n private headIndex = 0;\n private headOffset = 0;\n private totalLength = 0;\n\n get length(): number {\n return this.totalLength;\n }\n\n push(chunk: Uint8Array): void {\n if (chunk.length === 0) return;\n this.chunks.push(chunk);\n this.totalLength += chunk.length;\n }\n\n clear(): void {\n this.chunks = [];\n this.headIndex = 0;\n this.headOffset = 0;\n this.totalLength = 0;\n }\n\n readInt32BE(offset: number): number | undefined {\n if (offset < 0 || offset + 4 > this.totalLength) return undefined;\n\n const head = this.chunks[this.headIndex];\n /* c8 ignore next — head defined when totalLength > 0 */\n if (head !== undefined) {\n const start = this.headOffset + offset;\n if (start + 4 <= head.length) {\n /* c8 ignore start — bounds guaranteed by `start + 4 <= head.length` */\n const b1 = head[start] ?? 0;\n const b2 = head[start + 1] ?? 0;\n const b3 = head[start + 2] ?? 0;\n const b4 = head[start + 3] ?? 0;\n /* c8 ignore stop */\n return ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) >>> 0;\n }\n }\n\n let remaining = this.headOffset + offset;\n const bytes = new Uint8Array(4);\n let writeOffset = 0;\n\n for (let i = this.headIndex; i < this.chunks.length && writeOffset < 4; i++) {\n const chunk = this.chunks[i];\n /* c8 ignore next — chunks between headIndex and end are always populated */\n if (chunk === undefined) return undefined;\n if (remaining >= chunk.length) {\n remaining -= chunk.length;\n continue;\n }\n\n const bytesToCopy = Math.min(4 - writeOffset, chunk.length - remaining);\n bytes.set(chunk.subarray(remaining, remaining + bytesToCopy), writeOffset);\n writeOffset += bytesToCopy;\n remaining = 0;\n }\n\n /* c8 ignore start — bytes is a fixed 4-byte Uint8Array */\n const b1 = bytes[0] ?? 0;\n const b2 = bytes[1] ?? 0;\n const b3 = bytes[2] ?? 0;\n const b4 = bytes[3] ?? 0;\n /* c8 ignore stop */\n return ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) >>> 0;\n }\n\n consume(length: number): Uint8Array {\n if (length < 0 || length > this.totalLength) {\n throw new Error(`Cannot consume ${length} bytes from ${this.totalLength}-byte buffer`);\n }\n if (length === 0) return new Uint8Array(0);\n\n const head = this.chunks[this.headIndex];\n /* c8 ignore next — head defined when totalLength > 0 */\n if (head !== undefined) {\n const headRemaining = head.length - this.headOffset;\n if (headRemaining >= length) {\n const slice = head.subarray(this.headOffset, this.headOffset + length);\n this.headOffset += length;\n this.totalLength -= length;\n if (this.headOffset === head.length) {\n this.headIndex++;\n this.headOffset = 0;\n this.compactChunks();\n }\n return slice;\n }\n }\n\n const result = new Uint8Array(length);\n let writeOffset = 0;\n let remaining = length;\n\n while (remaining > 0) {\n const chunk = this.chunks[this.headIndex];\n /* c8 ignore next 3 — guarded by line-116 length check */\n if (chunk === undefined) {\n throw new Error('FrontendMessageBuffer underflow');\n }\n const available = chunk.length - this.headOffset;\n const bytesToCopy = Math.min(remaining, available);\n result.set(chunk.subarray(this.headOffset, this.headOffset + bytesToCopy), writeOffset);\n writeOffset += bytesToCopy;\n remaining -= bytesToCopy;\n this.headOffset += bytesToCopy;\n this.totalLength -= bytesToCopy;\n if (this.headOffset === chunk.length) {\n this.headIndex++;\n this.headOffset = 0;\n this.compactChunks();\n }\n }\n\n return result;\n }\n\n private compactChunks(): void {\n if (this.headIndex === this.chunks.length) {\n this.chunks = [];\n this.headIndex = 0;\n return;\n }\n\n if (this.headIndex >= 32 && this.headIndex * 2 >= this.chunks.length) {\n this.chunks = this.chunks.slice(this.headIndex);\n this.headIndex = 0;\n }\n }\n}\n","/**\n * PGlite duplex stream.\n *\n * A Duplex stream that replaces the TCP socket in pg.Client, routing\n * wire protocol messages directly to an in-process PGlite instance.\n *\n * pg.Client writes wire protocol bytes → duplex frames messages →\n * PGlite processes via execProtocolRawStream → duplex pushes responses back.\n *\n * Extended Query Protocol pipelines (Parse→Bind→Describe→Execute→Sync) are\n * concatenated into a single buffer and sent as one atomic execProtocolRawStream\n * call within one runExclusive. This prevents portal interleaving between\n * concurrent streams AND reduces async overhead (1 WASM call instead of 5).\n *\n * The response from a batched pipeline contains spurious ReadyForQuery messages\n * after each sub-message (PGlite's single-user mode). These are stripped,\n * keeping only the final ReadyForQuery after Sync.\n */\nimport { Duplex } from 'node:stream';\n\nimport type { PGlite, PGliteInterface } from '@electric-sql/pglite';\n\nimport type { TelemetrySink } from '../telemetry/bridge-stats.ts';\nimport { lockWaitChannel, queryChannel } from '../telemetry/diagnostics.ts';\nimport type { SessionLock } from '../utils/session-lock.ts';\nimport { nsToMs } from '../utils/time.ts';\nimport { waitPGliteReady } from '../utils/wait-pglite-ready.ts';\n\nimport { BackendMessageFramer } from './backend-framer.ts';\nimport {\n EQP_MESSAGES,\n RFQ_STATUS_FAILED,\n RFQ_STATUS_IDLE,\n RFQ_STATUS_IN_TRANSACTION,\n SYNC,\n TERMINATE,\n} from './constants.ts';\nimport { FrontendMessageBuffer } from './frontend-buffer.ts';\n\n// PGlite 0.4.5's execProtocolRawSync short-circuits X/Terminate before\n// entering the Postgres backend loop, while execProtocolStream still clears\n// its parsed-message array. Re-verify this behavior on PGlite upgrades.\nconst TERMINATE_MESSAGE = new Uint8Array([TERMINATE, 0x00, 0x00, 0x00, 0x04]);\n// Keep cleanup infrequent on small queries, but bounded for large read bursts;\n// these thresholds came from the adapter comparison memory profile.\nconst PROTOCOL_CLEANUP_RAW_BYTES = 8 * 1024 * 1024;\nconst PROTOCOL_CLEANUP_CALLS = 32;\n\ntype PGliteProtocolCleanupCapable = PGliteInterface & {\n execProtocolStream?: PGlite['execProtocolStream'];\n};\n\nexport interface PGliteDuplexOptions {\n /**\n * Shared lock that serialises access to the PGlite runtime across\n * multiple duplex streams. Omit for a standalone duplex.\n */\n sessionLock?: SessionLock;\n /**\n * Identity tag published with diagnostics-channel events. Omit to\n * disable channel publication for this duplex.\n */\n bridgeId?: symbol;\n /**\n * Internal sink used by `PGliteBridge` for built-in stats. Not a public\n * extension point — subscribe via `node:diagnostics_channel` instead.\n */\n telemetry?: TelemetrySink;\n /**\n * Maximum milliseconds to wait for the PGlite instance to become ready\n * before each bridge operation. Defaults to no timeout (waits indefinitely).\n */\n timeout?: number;\n /**\n * Whether each wire-protocol call should force a filesystem sync before\n * returning. Disable only when higher throughput / lower RSS is worth\n * weaker durability. Default `true`.\n */\n syncToFs?: boolean;\n}\n\n/**\n * Duplex stream that bridges `pg.Client` to an in-process PGlite instance.\n *\n * **Most users should reach for {@link PGliteBridge} instead** — it\n * bundles `PGliteDuplex` with a pool, telemetry, snapshot/reset\n * lifecycle, and the Prisma adapter. Use `PGliteDuplex` directly only\n * when wiring a custom `pg.Client` setup that's outside the\n * `PgBridgePool` model.\n *\n * Replaces the TCP socket in `pg.Client` via the `stream` option. Speaks\n * PostgreSQL wire protocol directly to PGlite — no TCP, no serialization\n * overhead beyond what the wire protocol requires. When using multiple\n * duplexes against one PGlite, pass a shared {@link SessionLock} so\n * cross-duplex transactions don't interleave.\n *\n * ```typescript\n * const client = new pg.Client({\n * stream: () => new PGliteDuplex(pglite),\n * });\n * ```\n */\nexport class PGliteDuplex extends Duplex {\n private readonly pglite: PGliteProtocolCleanupCapable;\n private readonly sessionLock?: SessionLock;\n private readonly bridgeId?: symbol;\n private readonly telemetry?: TelemetrySink;\n private readonly syncToFs: boolean;\n private readonly timeout?: number;\n private readonly duplexId: symbol;\n /** Incoming bytes framed directly from a queued chunk buffer */\n private readonly input = new FrontendMessageBuffer();\n private phase: 'pre_startup' | 'ready' = 'pre_startup';\n private draining = false;\n private tornDown = false;\n /** Callbacks waiting for drain to process their data */\n private drainQueue: Array<(error?: Error | null) => void> = [];\n /** Buffered EQP messages awaiting Sync */\n private pipeline: Uint8Array[] = [];\n /** Last RFQ status byte observed in a backend response — independent of\n * SessionLock so rollback works for max=1 pools (no lock) and survives\n * the in-flight BEGIN race (lock owner is set only on RFQ arrival). */\n private lastSeenRfqStatus?: number;\n /** In-flight `op()` from the runExclusive callback, if any. Set only once\n * the callback has actually entered `op()` — a queued callback that fires\n * after `tornDown` returns immediately and is never registered here, so\n * teardown does not block on its slot. Rollback awaits this to catch the\n * RFQ from a BEGIN that started before destroy. */\n private currentPGliteCall?: Promise<unknown>;\n /** Memoized rollback so concurrent teardown paths (e.g., `_final` then\n * `_destroy`) don't issue duplicate `ROLLBACK` statements. */\n private rollbackPromise?: Promise<void>;\n private pendingProtocolCleanupBytes = 0;\n private pendingProtocolCleanupCalls = 0;\n private protocolCleanupUnsupported = false;\n /** Resolves once the stream has fully torn down (post-`_final` rollback,\n * post-`_destroy`). Single-shot, mirroring the `'close'` event. */\n readonly onClose: Promise<void>;\n\n /**\n * @param pglite PGlite instance to bridge to. The caller owns its lifecycle.\n * @param options See {@link PGliteDuplexOptions}.\n */\n constructor(pglite: PGlite | PGliteInterface, options: PGliteDuplexOptions = {}) {\n super();\n this.pglite = pglite;\n this.sessionLock = options.sessionLock;\n this.bridgeId = options.bridgeId;\n this.telemetry = options.telemetry;\n this.timeout = options.timeout;\n this.syncToFs = options.syncToFs ?? true;\n\n this.duplexId = Symbol('duplex');\n this.onClose = new Promise<void>((resolve) => this.once('close', () => resolve()));\n }\n\n // ── Socket compatibility (called by pg's Connection) ──\n\n connect(): this {\n setImmediate(() => this.emit('connect'));\n return this;\n }\n\n setKeepAlive(): this {\n return this;\n }\n\n setNoDelay(): this {\n return this;\n }\n\n setTimeout(): this {\n return this;\n }\n\n ref(): this {\n return this;\n }\n\n unref(): this {\n return this;\n }\n\n // ── Duplex implementation ──\n\n override _read(): void {\n // Data is pushed proactively when PGlite responses arrive\n }\n\n override _write(\n chunk: Buffer,\n _encoding: BufferEncoding,\n callback: (error?: Error | null) => void,\n ): void {\n this.input.push(chunk);\n this.enqueue(callback);\n }\n\n /** Handles corked batches — pg.Client corks during prepared queries (P+B+D+E+S) */\n override _writev(\n chunks: Array<{ chunk: Buffer; encoding: BufferEncoding }>,\n callback: (error?: Error | null) => void,\n ): void {\n for (const { chunk } of chunks) {\n this.input.push(chunk);\n }\n this.enqueue(callback);\n }\n\n override _final(callback: (error?: Error | null) => void): void {\n // Forced disconnects (raw socket close, no Terminate) reach us here. Roll\n // back any open transaction before releasing the session lock — otherwise\n // the next bridge can run queries while PGlite is still in 'T' state and\n // observe rows from the dead transaction.\n this.rollbackIfInTransaction()\n /* v8 ignore start */\n .catch(() => {})\n /* v8 ignore stop */\n .then(() => {\n this.sessionLock?.release(this.duplexId);\n this.push(null);\n callback();\n });\n }\n\n override _destroy(error: Error | null, callback: (error?: Error | null) => void): void {\n this.tornDown = true;\n this.pipeline.length = 0;\n this.input.clear();\n\n // Fail queued write callbacks promptly — rollback below may await an\n // in-flight pglite call, and pg.Client must not see those writes resolve\n // successfully when the bridge is being destroyed.\n const destroyError = error ?? new Error('Bridge destroyed');\n const callbacks = this.drainQueue;\n this.drainQueue = [];\n for (const cb of callbacks) {\n cb(destroyError);\n }\n\n // Roll back BEFORE cancelling the session lock — cancel() unblocks waiters,\n // so any rollback after that would race with the next bridge's queries.\n this.rollbackIfInTransaction()\n /* v8 ignore start */\n .catch(() => {})\n /* v8 ignore stop */\n .then(() => {\n this.sessionLock?.cancel(this.duplexId, destroyError);\n callback(error);\n });\n }\n\n // ── Drain loop ──\n\n /**\n * Enqueue a write callback and start draining if not already running.\n * The callback is NOT called until drain has processed the data.\n */\n private enqueue(callback: (error?: Error | null) => void): void {\n this.drainQueue.push(callback);\n if (!this.draining) {\n // Errors are propagated through drainQueue callbacks, not through this promise\n this.drain().catch(/* c8 ignore next */ () => {});\n }\n }\n\n /**\n * Process all pending data, looping until no new data arrives.\n * Fires all queued callbacks on completion or error.\n */\n private async drain(): Promise<void> {\n /* c8 ignore next — enqueue only starts drain when !draining */\n if (this.draining) return;\n this.draining = true;\n\n let error: Error | null = null;\n\n try {\n // Loop until no more pending data to process\n while (this.input.length > 0) {\n /* c8 ignore start — race-only: destroy after a drain iteration resolves */\n if (this.tornDown) break;\n const beforeLength = this.input.length;\n\n if (this.phase === 'pre_startup') {\n await this.processPreStartup();\n }\n if (this.phase === 'ready') {\n await this.processMessages();\n }\n /* c8 ignore stop */\n\n // If processMessages couldn't consume anything (incomplete message),\n // stop looping — more data will arrive via _write\n /* c8 ignore next — loop-continue unreachable: no new input arrives mid-drain */\n if (this.input.length === 0 || this.input.length === beforeLength) break;\n }\n } catch (err) {\n error = err instanceof Error ? err : new Error(String(err));\n // Release session lock on error — prevents permanent deadlock if\n // PGlite crashes mid-transaction (other bridges would wait forever)\n this.sessionLock?.release(this.duplexId);\n } finally {\n this.draining = false;\n\n // Fire all waiting callbacks\n const callbacks = this.drainQueue;\n this.drainQueue = [];\n for (const cb of callbacks) {\n cb(error);\n }\n }\n }\n\n // ── Message framing ──\n\n /**\n * Frames and processes the startup message.\n *\n * Format: [4 bytes: total length] [4 bytes: protocol version] [key\\0value\\0 pairs]\n * No type byte — length includes itself.\n */\n private async processPreStartup(): Promise<void> {\n if (this.input.length < 4) return;\n const len = this.input.readInt32BE(0);\n /* c8 ignore next — len === undefined unreachable once length ≥ 4 */\n if (len === undefined || this.input.length < len) return;\n\n const message = this.input.consume(len);\n\n const session = this.acquireSession();\n if (session) await session;\n await this.runUnderRunExclusive(async () => {\n await this.streamProtocol(message, { detectErrors: false, suppressIntermediateRfq: false });\n });\n\n this.phase = 'ready';\n }\n\n /**\n * Wrap a `pglite.runExclusive` call so the in-flight operation is tracked\n * via `currentPGliteCall`. Skip the work entirely if teardown happened\n * before our turn — otherwise a queued BEGIN would still run against PGlite\n * after `_destroy` has already returned, leaking 'T' state into the next\n * bridge's session.\n *\n * `currentPGliteCall` is set only once `op()` actually starts. A callback\n * that fires after `tornDown` returns immediately and was never observed by\n * rollback — there is no RFQ state to wait for, so blocking teardown on the\n * queued no-op would needlessly stall stream close (and any SessionLock\n * waiters behind it).\n */\n private async runUnderRunExclusive(op: () => Promise<void>): Promise<void> {\n await waitPGliteReady(this.pglite, this.timeout);\n\n await this.pglite.runExclusive(async () => {\n if (this.tornDown) return;\n const opPromise = op();\n this.currentPGliteCall = opPromise;\n try {\n await opPromise;\n } finally {\n this.currentPGliteCall = undefined;\n }\n });\n }\n\n /**\n * Frames and processes regular wire protocol messages.\n *\n * Extended Query Protocol messages (Parse, Bind, Describe, Execute, Close,\n * Flush) are buffered in `this.pipeline`. When Sync arrives, the entire\n * pipeline is concatenated and sent to PGlite as one atomic\n * execProtocolRawStream call within one runExclusive.\n *\n * SimpleQuery messages are sent directly (they're self-contained).\n */\n private async processMessages(): Promise<void> {\n while (this.input.length >= 5) {\n const msgLen = this.input.readInt32BE(1);\n /* c8 ignore next — input.length ≥ 5 guarantees readable int32 */\n if (msgLen === undefined) break;\n const len = 1 + msgLen;\n if (len < 5 || this.input.length < len) break;\n\n const message = this.input.consume(len);\n /* c8 ignore next — consume(len ≥ 5) returns non-empty */\n const msgType = message[0] ?? 0;\n\n if (msgType === TERMINATE) {\n await this.rollbackIfInTransaction();\n this.sessionLock?.release(this.duplexId);\n this.push(null);\n return;\n }\n\n if (EQP_MESSAGES.has(msgType)) {\n this.pipeline.push(message);\n continue;\n }\n\n if (msgType === SYNC) {\n this.pipeline.push(message);\n await this.flushPipeline();\n continue;\n }\n\n // SimpleQuery or other standalone message\n await this.runWithTiming((detectErrors) =>\n this.streamProtocol(message, { detectErrors, suppressIntermediateRfq: false }),\n );\n }\n }\n\n /**\n * Sends the accumulated EQP pipeline as one atomic operation.\n *\n * All buffered messages are concatenated into a single buffer and sent\n * as one execProtocolRawStream call. This is both correct (prevents\n * portal interleaving) and fast (1 WASM call + 1 async boundary instead\n * of 5). A streaming framer suppresses intermediate ReadyForQuery\n * messages while forwarding the rest of the response without\n * materializing it.\n */\n private async flushPipeline(): Promise<void> {\n const messages = this.pipeline;\n this.pipeline = [];\n let batch: Uint8Array;\n /* c8 ignore next 3 — flushPipeline only runs after Sync is appended */\n if (messages.length === 1) {\n batch = messages[0] ?? new Uint8Array(0);\n } else {\n batch = this.tryContiguousPipelineBatch(messages) ?? this.concatPipeline(messages);\n }\n await this.runWithTiming((detectErrors) =>\n this.streamProtocol(batch, { detectErrors, suppressIntermediateRfq: true }),\n );\n }\n\n private tryContiguousPipelineBatch(messages: Uint8Array[]): Uint8Array | undefined {\n const first = messages[0];\n /* c8 ignore next — caller only passes non-empty pipelines */\n if (first === undefined) return undefined;\n\n const buffer = first.buffer;\n const start = first.byteOffset;\n let end = start + first.byteLength;\n for (let i = 1; i < messages.length; i++) {\n const part = messages[i];\n /* c8 ignore next — pipeline array has no holes */\n if (part === undefined) return undefined;\n if (part.buffer !== buffer || part.byteOffset !== end) {\n return undefined;\n }\n end += part.byteLength;\n }\n\n return new Uint8Array(buffer, start, end - start);\n }\n\n private concatPipeline(messages: Uint8Array[]): Uint8Array {\n const total = messages.reduce((sum, p) => sum + p.length, 0);\n const batch = new Uint8Array(total);\n let offset = 0;\n for (const part of messages) {\n batch.set(part, offset);\n offset += part.length;\n }\n return batch;\n }\n\n // ── PGlite execution ──\n\n /**\n * Acquires the session, runs the op under `pglite.runExclusive`, and\n * updates internal stats and/or publishes diagnostics events when enabled.\n * When neither internal telemetry nor diagnostics subscribers need timing,\n * skips timing entirely.\n *\n * `op` returns `false` when an `ErrorResponse` was seen without throwing\n * (protocol-level failure). Combined with the catch branch, both failure\n * modes flip `succeeded` so both `BridgeStats` and `QUERY_CHANNEL`\n * payloads stay accurate. `detectErrors` is therefore tied to whether\n * either of those consumers is active, not to timing in general.\n */\n private async runWithTiming(op: (detectErrors: boolean) => Promise<boolean>): Promise<void> {\n const wantTelemetry = this.telemetry !== undefined;\n const publishQuery = this.bridgeId !== undefined && queryChannel.hasSubscribers;\n const publishLockWait = this.bridgeId !== undefined && lockWaitChannel.hasSubscribers;\n const wantTiming = wantTelemetry || publishQuery || publishLockWait;\n const detectErrors = wantTelemetry || publishQuery;\n\n if (!wantTiming) {\n const session = this.acquireSession();\n if (session) await session;\n await this.runUnderRunExclusive(async () => {\n await op(false);\n });\n return;\n }\n\n const lockStart = process.hrtime.bigint();\n const session = this.acquireSession();\n if (session) await session;\n const queryStart = process.hrtime.bigint();\n const lockWaitMs = nsToMs(queryStart - lockStart);\n if (wantTelemetry) {\n this.telemetry?.recordLockWait(lockWaitMs);\n }\n if (publishLockWait) {\n lockWaitChannel.publish({\n bridgeId: this.bridgeId,\n durationMs: lockWaitMs,\n });\n }\n\n let succeeded = true;\n try {\n await this.runUnderRunExclusive(async () => {\n succeeded = await op(detectErrors);\n });\n } catch (err) {\n succeeded = false;\n throw err;\n } finally {\n const queryMs = nsToMs(process.hrtime.bigint() - queryStart);\n if (wantTelemetry) {\n this.telemetry?.recordQuery(queryMs, succeeded);\n }\n if (publishQuery) {\n queryChannel.publish({\n bridgeId: this.bridgeId,\n durationMs: queryMs,\n succeeded,\n });\n }\n }\n }\n\n /**\n * Sends a message (or pipelined batch) to PGlite and pushes the raw protocol\n * response to the stream.\n *\n * For pipelined Extended Query batches, pass `suppressIntermediateRfq`\n * so only the final ReadyForQuery reaches the client.\n *\n * Must be called inside runExclusive.\n */\n private async streamProtocol(\n message: Uint8Array,\n options: { detectErrors: boolean; suppressIntermediateRfq: boolean },\n ): Promise<boolean> {\n const { detectErrors, suppressIntermediateRfq } = options;\n let errSeen = false;\n const framer = new BackendMessageFramer({\n suppressIntermediateReadyForQuery: suppressIntermediateRfq,\n onChunk: (chunk) => {\n /* c8 ignore next — race-only: tornDown becomes true mid-stream */\n if (!this.tornDown && chunk.length > 0) {\n this.push(chunk);\n }\n },\n onErrorResponse: () => {\n if (detectErrors) errSeen = true;\n },\n onReadyForQuery: (status) => {\n this.lastSeenRfqStatus = status;\n if (this.sessionLock) {\n this.sessionLock.updateStatus(this.duplexId, status);\n }\n },\n });\n\n // Re-check readiness inside the runExclusive mutex: the caller could\n // have closed pglite between `runUnderRunExclusive`'s pre-check and\n // our turn to run. Without this, `execProtocolRawStream` below would\n // throw a less informative error from inside the WASM runtime.\n await waitPGliteReady(this.pglite, this.timeout);\n\n // PGlite's raw streaming API still parses backend messages internally, but\n // does not clear that parsed-message array after returning. Keep the fast\n // streaming path, then clear PGlite's internal parsed-message state with an\n // ignored Terminate frame. `throwOnError: false` keeps the cleanup\n // best-effort if PGlite changes how that frame is handled.\n let rawBytes = 0;\n let streamFailed = false;\n try {\n await this.pglite.execProtocolRawStream(message, {\n syncToFs: this.syncToFs,\n onRawData: (chunk: Uint8Array) => {\n rawBytes += chunk.byteLength;\n framer.write(chunk);\n },\n });\n } catch (err) {\n streamFailed = true;\n throw err;\n } finally {\n this.pendingProtocolCleanupBytes += rawBytes;\n this.pendingProtocolCleanupCalls++;\n if (\n !this.protocolCleanupUnsupported &&\n (streamFailed ||\n this.pendingProtocolCleanupBytes >= PROTOCOL_CLEANUP_RAW_BYTES ||\n this.pendingProtocolCleanupCalls >= PROTOCOL_CLEANUP_CALLS)\n ) {\n await this.clearPGliteProtocolMessages();\n this.pendingProtocolCleanupBytes = 0;\n this.pendingProtocolCleanupCalls = 0;\n }\n }\n\n framer.flush({ dropHeldReadyForQuery: this.tornDown });\n return !errSeen;\n }\n\n private async clearPGliteProtocolMessages(): Promise<void> {\n if (this.protocolCleanupUnsupported) return;\n\n const { execProtocolStream } = this.pglite;\n if (typeof execProtocolStream !== 'function') {\n this.protocolCleanupUnsupported = true;\n return;\n }\n\n try {\n await execProtocolStream.call(this.pglite, TERMINATE_MESSAGE, {\n syncToFs: false,\n throwOnError: false,\n });\n } catch {\n // Best-effort cleanup for PGlite internals; preserve the original query\n // outcome if the no-op cleanup is unavailable or rejected. Disable\n // future attempts so a PGlite API change costs only one failed cleanup.\n this.protocolCleanupUnsupported = true;\n }\n }\n\n // ── Session lock & rollback ──\n\n private acquireSession(): Promise<void> | undefined {\n return this.sessionLock?.acquire(this.duplexId);\n }\n\n /**\n * Issue `ROLLBACK` against PGlite when the duplex's last observed\n * ReadyForQuery status indicates an open transaction (`T` or `E`). Safe\n * to call when no transaction is active — resolves with no effect.\n *\n * Transaction detection lives on the duplex (not on `SessionLock`) so it\n * works for both locked pools (max>1, TCP server) and standalone duplexes\n * (max=1 pool, no lock). When a `SessionLock` is present, ownership is an\n * additional safety check — a duplex whose lock was released via an error\n * path must not roll back another bridge's transaction.\n *\n * Used by `_final`, `_destroy`, and the Terminate handler. PGlite is\n * single-session, so leftover `T` state would otherwise leak into the\n * next connection.\n */\n async rollbackIfInTransaction(): Promise<void> {\n // Memoize so concurrent teardown paths (e.g., `_final` racing `_destroy`)\n // share one ROLLBACK instead of issuing duplicates.\n if (this.rollbackPromise !== undefined) return this.rollbackPromise;\n this.rollbackPromise = this.runRollback();\n return this.rollbackPromise;\n }\n\n private async runRollback(): Promise<void> {\n // Wait for any in-flight pglite call so its RFQ has been processed —\n // otherwise destroying mid-BEGIN would skip cleanup because the status\n // hasn't transitioned to 'T' yet.\n // The in-flight pglite call is awaited only to sequence its RFQ; the\n // result and any error are intentionally swallowed. Under current\n // orchestration runRollback always observes currentPGliteCall as falsy,\n // but the guard remains for the rare mid-call destroy path.\n /* v8 ignore start */\n if (this.currentPGliteCall) {\n await this.currentPGliteCall.catch(() => undefined);\n }\n /* v8 ignore stop */\n\n const status = this.lastSeenRfqStatus;\n if (status !== RFQ_STATUS_IN_TRANSACTION && status !== RFQ_STATUS_FAILED) return;\n\n // Defensive: rollback only runs while this duplex still owns the session\n // lock (or has none). The non-owner short-circuit cannot be reached under\n // current orchestration but stays as a guardrail.\n /* v8 ignore next */\n if (this.sessionLock !== undefined && !this.sessionLock.isOwner(this.duplexId)) return;\n\n try {\n // pglite.query acquires runExclusive internally — do not wrap it.\n await this.pglite.query('ROLLBACK');\n this.lastSeenRfqStatus = RFQ_STATUS_IDLE;\n } catch {\n // Best-effort cleanup. PGlite may reject ROLLBACK (e.g., already\n // auto-rolled back during shutdown) — nothing recoverable here.\n }\n }\n}\n","import type { PGlite, PGliteInterface } from '@electric-sql/pglite';\n\nexport type SyncToFsMode = 'auto' | boolean;\n\nexport const resolveSyncToFs = (pglite: PGlite | PGliteInterface, mode?: SyncToFsMode): boolean => {\n if (typeof mode === 'boolean') return mode;\n return !!pglite.dataDir && !pglite.dataDir.startsWith('memory://');\n};\n","/**\n * Session-level lock for PGlite's single-session model.\n *\n * PGlite runs PostgreSQL in single-user mode — one session shared by all\n * bridges. runExclusive serializes individual operations, but transactions\n * span multiple operations. Without session-level locking, Bridge A's BEGIN\n * and Bridge B's query interleave, corrupting transaction boundaries.\n *\n * The session lock tracks which bridge owns the session. When PGlite enters\n * transaction state (ReadyForQuery status 'T' or 'E'), the owning bridge\n * gets exclusive access until the transaction completes (status returns to 'I').\n *\n * Non-transactional operations from any bridge are allowed when no transaction\n * is active — they serialize naturally through runExclusive.\n */\n\n// ReadyForQuery status bytes\nconst STATUS_IDLE = 0x49; // 'I' — no transaction\nconst STATUS_IN_TRANSACTION = 0x54; // 'T' — in transaction block\nconst STATUS_FAILED = 0x45; // 'E' — failed transaction block\n\n/**\n * Coordinates PGlite access across concurrent pool connections.\n *\n * @remarks\n * PGlite runs PostgreSQL in single-user mode — one session shared by all\n * bridges. The session lock tracks which bridge owns the session during\n * transactions, preventing interleaving. Used internally by {@link PGliteDuplex}\n * and created automatically by {@link PgBridgePool}. Only instantiate directly\n * if building a custom pool setup.\n */\nexport class SessionLock {\n private owner?: symbol;\n private waitQueue: Array<{ id: symbol; resolve: () => void; reject: (error: Error) => void }> =\n [];\n\n /**\n * Acquire access to PGlite. Resolves immediately if no transaction is\n * active or if this bridge owns the current transaction. Queues otherwise.\n */\n async acquire(id: symbol): Promise<void> {\n // Free slot or re-entrant — pass through\n if (this.owner === undefined || this.owner === id) return;\n\n // Another bridge owns the session — wait\n return new Promise<void>((resolve, reject) => {\n this.waitQueue.push({\n id,\n resolve,\n reject,\n });\n });\n }\n\n /** Returns `true` if `id` currently holds the session (e.g., is mid-transaction). */\n isOwner(id: symbol): boolean {\n return this.owner === id;\n }\n\n /**\n * Update session state based on the ReadyForQuery status byte.\n * Call after every PGlite response that contains RFQ.\n *\n * @returns `true` if ownership transitioned on this call (acquired or\n * released). `false` for no-op updates (e.g., re-entrant status within\n * the same transaction, or IDLE from a non-owning bridge).\n */\n updateStatus(id: symbol, status: number): boolean {\n if (status === STATUS_IN_TRANSACTION || status === STATUS_FAILED) {\n if (this.owner === id) return false;\n this.owner = id;\n return true;\n }\n\n // Transaction complete — release ownership\n if (status === STATUS_IDLE && this.owner === id) {\n this.owner = undefined;\n this.drainWaitQueue();\n return true;\n }\n\n return false;\n }\n\n /**\n * Release ownership (e.g., when a bridge is destroyed mid-transaction).\n *\n * @returns `true` if this bridge held ownership and released it. `false`\n * if another bridge (or no one) owned the session.\n */\n release(id: symbol): boolean {\n if (this.owner === id) {\n this.owner = undefined;\n this.drainWaitQueue();\n return true;\n }\n\n return false;\n }\n\n /**\n * Cancel this bridge's pending or active claim on the session.\n *\n * Used when a bridge is torn down while blocked in `acquire()` so it cannot\n * later be granted ownership after destruction.\n */\n cancel(id: symbol, error: Error = new Error('Session lock acquire cancelled')): boolean {\n let cancelled = false;\n\n if (this.owner === id) {\n this.owner = undefined;\n this.drainWaitQueue();\n cancelled = true;\n }\n\n const remaining: typeof this.waitQueue = [];\n for (const waiter of this.waitQueue) {\n if (waiter.id === id) {\n waiter.reject(error);\n cancelled = true;\n } else {\n remaining.push(waiter);\n }\n }\n this.waitQueue = remaining;\n\n return cancelled;\n }\n\n /**\n * Grant ownership to the next waiter, if any.\n *\n * @returns `true` if a waiter was unblocked; `false` if the queue was empty.\n */\n private drainWaitQueue(): boolean {\n // Release one waiter at a time and grant ownership before resolving.\n // The waiter's operation will call updateStatus when it completes —\n // if IDLE, ownership is cleared and the next waiter is released.\n // This prevents interleaving where multiple waiters race past acquire\n // and one starts a transaction while others proceed unserialized.\n const next = this.waitQueue.shift();\n if (!next) return false;\n\n this.owner = next.id;\n next.resolve();\n return true;\n }\n}\n","/**\n * Wraps a pg-style `{ getTypeParser }` object so array text-format reads\n * use `postgres-array@3` instead of pg-types' transitive `postgres-array@2`.\n *\n * `@prisma/adapter-pg` already uses postgres-array@3 for its own normalized\n * arrays (NUMERIC[], JSON[], TIMESTAMP[], etc.) but falls back to pg-types'\n * slow parser for:\n * - BYTEA[] (captured at adapter-pg module load from pg-types)\n * - everything else not in its customParsers map: BOOL[], INT2/4/8[],\n * FLOAT4/8[], OID[], TEXT/VARCHAR/CHAR/UUID[], CIDR/INET/MACADDR[],\n * INTERVAL[], TIMETZ[], NUMRANGE[], POINT[]\n *\n * The pg-types path costs O(n) JS object allocations per character of the\n * array literal — turning a 19MB BYTEA[] into 19M+ allocations. v3 uses\n * `indexOf` + `slice`, dropping the same workload to milliseconds.\n *\n * We don't override the *element* parser — we ask the wrapped object for\n * whatever parser it would have used. So adapter-pg's customParsers for\n * scalars (e.g. its `convertBytes` for OID 17) is preserved.\n */\nimport { parse as parseArrayV3 } from 'postgres-array';\n\nexport const isObject = (val: unknown): val is Record<string, unknown> => {\n return typeof val === 'object' && val !== null && !Array.isArray(val);\n};\n\ntype Parser = (raw: string) => unknown;\ntype GetTypeParser = (oid: number, format?: string) => Parser;\n\nexport interface TypesLike {\n getTypeParser: GetTypeParser;\n}\nexport const isTypesLike = (value: unknown): value is TypesLike => {\n return isObject(value) && 'getTypeParser' in value && typeof value.getTypeParser === 'function';\n};\n\n// Array OIDs whose elements pg-types parses as raw strings (no transform).\n// We can hand back a cached parser without consulting `originalGetTypeParser`.\nconst ARRAY_OID_TEXT_LIKE: ReadonlySet<number> = new Set([\n 1002, // _char\n 1009, // _text\n 1014, // _bpchar\n 1015, // _varchar\n 1040, // _macaddr\n 1041, // _inet\n 1270, // _timetz\n 2951, // _uuid\n 651, // _cidr\n 3907, // _numrange\n]);\n\n// Array OID → element scalar OID for OIDs that need a real element parser.\nconst ARRAY_OID_WITH_ELEMENT: ReadonlyMap<number, number> = new Map([\n [1000, 16], // _bool\n [1001, 17], // _bytea\n [1005, 21], // _int2\n [1007, 23], // _int4\n [1016, 20], // _int8\n [1017, 600], // _point\n [1021, 700], // _float4\n [1022, 701], // _float8\n [1028, 26], // _oid\n [1187, 1186], // _interval\n]);\n\nconst FAST_TEXT_ARRAY_PARSER: Parser = (raw) => parseArrayV3(raw);\n\nexport const wrapTypesWithFastArrayParsers = <T extends TypesLike>(types: T): T => {\n const original = types.getTypeParser;\n const wrapped: GetTypeParser = (oid, format = 'text') => {\n if (format === 'text') {\n if (ARRAY_OID_TEXT_LIKE.has(oid)) return FAST_TEXT_ARRAY_PARSER;\n const elementOid = ARRAY_OID_WITH_ELEMENT.get(oid);\n if (elementOid !== undefined) {\n const elementParser = original(elementOid, 'text');\n return (raw) => parseArrayV3(raw, elementParser);\n }\n }\n return original(oid, format);\n };\n return { ...types, getTypeParser: wrapped };\n};\n","import type { PGlite, PGliteInterface } from '@electric-sql/pglite';\nimport pg from 'pg';\nimport { PGliteDuplex } from '../duplex';\nimport type { TelemetrySink } from '../telemetry/bridge-stats.ts';\nimport type { SessionLock } from '../utils/session-lock.ts';\nimport { isObject, isTypesLike, wrapTypesWithFastArrayParsers } from './fast-array-parsers.ts';\n\nexport interface PgBridgeClientOptions {\n pglite: PGlite | PGliteInterface;\n bridgeId: symbol;\n sessionLock?: SessionLock;\n telemetry?: TelemetrySink;\n syncToFs: boolean;\n timeout?: number;\n}\n\ntype PgBridgeClientConfig = pg.ClientConfig & {\n [PgBridgeClient.OptionsKey]: PgBridgeClientOptions;\n};\n\nexport class PgBridgeClient extends pg.Client {\n private querySubmissionChain?: Promise<void>;\n\n static readonly OptionsKey: unique symbol = Symbol('PgBridgeClientOptions');\n\n constructor(config?: PgBridgeClientConfig) {\n const resolved = config ?? ({} as PgBridgeClientConfig);\n const { [PgBridgeClient.OptionsKey]: bridge, ...clientConfig } = resolved;\n if (!bridge) {\n throw new Error('PgBridgeClient requires bridge options');\n }\n\n super({\n ...clientConfig,\n user: 'postgres',\n database: 'postgres',\n stream: () => new PGliteDuplex(bridge.pglite, bridge),\n });\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: satisfy pg.Client.query's overload union\n override query(...args: unknown[]): any {\n const first = args[0];\n const submit = () => {\n // biome-ignore lint/suspicious/noExplicitAny: pg.Client.query has 7 overloads\n return (super.query as any).apply(this, args) as Promise<unknown>;\n };\n\n // Preserve pg's synchronous TypeError for null/undefined query.\n if (first === null || first === undefined) return submit();\n\n // Submittable: terminal signaling isn't uniform across the pg contract.\n // Let pg's internal queue handle it unserialized. adapter-pg never uses\n // this form; users mixing Submittable + Promise forms on one client may\n // still trip the pg queue deprecation.\n if (typeof (first as { submit?: unknown }).submit === 'function') {\n return submit();\n }\n\n const cbIndex = args.findIndex((arg) => typeof arg === 'function');\n if (cbIndex !== -1) {\n const origCb = args[cbIndex] as (err: unknown, res: unknown) => void;\n const promiseArgs = args.slice();\n promiseArgs.splice(cbIndex, 1);\n\n try {\n this.query(...promiseArgs).then(\n (res: unknown) => origCb(null, res),\n (err: unknown) => origCb(err, undefined),\n );\n } catch (err) {\n origCb(err, undefined);\n }\n return undefined;\n }\n\n if (isObject(first) && isTypesLike(first.types)) {\n args[0] = {\n ...first,\n types: wrapTypesWithFastArrayParsers(first.types),\n };\n }\n\n const prior = this.querySubmissionChain;\n let p: Promise<unknown>;\n if (prior === undefined) {\n try {\n p = submit();\n } catch (err) {\n return Promise.reject(err);\n }\n } else {\n p = prior.then(submit);\n }\n let done: Promise<void>;\n const clearChain = () => {\n if (this.querySubmissionChain === done) {\n this.querySubmissionChain = undefined;\n }\n };\n done = p.then(clearChain, clearChain);\n this.querySubmissionChain = done;\n return p;\n }\n}\n","/**\n * `PgBridgePool` — a `pg.Pool` subclass backed by a PGlite instance.\n *\n * Each pool connection gets its own PGliteDuplex stream, all sharing the\n * same PGlite WASM instance. Pools with multiple connections also share a\n * SessionLock. The session lock ensures transaction isolation: when one\n * bridge starts a transaction (BEGIN), it gets exclusive PGlite access until\n * COMMIT/ROLLBACK. Non-transactional operations from any bridge serialize\n * through PGlite's runExclusive mutex.\n */\nimport { PGlite, type PGliteInterface } from '@electric-sql/pglite';\nimport pg from 'pg';\nimport type { TelemetrySink } from '../telemetry/bridge-stats.ts';\nimport { resolveSyncToFs, type SyncToFsMode } from '../utils/resolve-sync-to-fs.ts';\nimport { SessionLock } from '../utils/session-lock.ts';\nimport { PgBridgeClient, type PgBridgeClientOptions } from './pg-bridge-client.ts';\n\nexport interface PgBridgePoolOptions\n extends Omit<PgBridgeClientOptions, 'pglite' | 'bridgeId' | 'syncToFs' | 'telemetry'> {\n /**\n * PGlite instance to back the pool. When omitted the pool creates its own\n * in-memory `PGlite` and owns its lifecycle — `end()` shuts it down. When\n * provided the caller owns the lifecycle — `end()` leaves it open.\n */\n pglite?: PGlite | PGliteInterface;\n\n /**\n * Identity tag published with every diagnostics-channel event. Subscribers\n * filter on this to distinguish events from different bridges in the\n * same process. A fresh `Symbol('bridge')` is generated if omitted; the\n * same `symbol` reference is reused on every event from this pool.\n * Hold a reference if you need to filter from outside.\n */\n bridgeId?: symbol;\n\n /**\n * Maximum pool connections (default: 1). Compatibility knob, not a\n * throughput knob.\n *\n * PGlite's WASM runtime executes queries serially behind a single mutex.\n * Raising `max` above 1 therefore does not add parallelism — queries still\n * run one at a time — and each extra connection costs a full `PGliteDuplex`\n * (its framers and scratch buffers) plus shared session-lock coordination\n * in memory. Leave this at `1` unless your code specifically needs to check\n * out multiple `pg` clients or you are deliberately exercising wait-queue\n * behaviour in a test.\n */\n max?: number;\n\n /**\n * Filesystem sync policy for bridge-driven wire-protocol calls.\n *\n * - `'auto'` (default): disable per-query sync for clearly in-memory PGlite\n * instances, keep it enabled otherwise.\n * - `true`: always sync before the bridge returns a query result.\n * - `false`: never sync on bridge protocol calls; fastest, but weaker durability.\n *\n * `auto` uses `pglite.dataDir` as a heuristic. If you provide a custom\n * persistent `fs` without a meaningful `dataDir`, pass `true` explicitly.\n */\n syncToFs?: SyncToFsMode;\n\n /**\n * Maximum milliseconds to wait for the PGlite instance to become ready\n * before each bridge operation. Defaults to no timeout (waits indefinitely).\n */\n timeout?: number;\n}\n\n/**\n * A pg.Pool where every connection is an in-process PGlite bridge.\n *\n * **Ownership:** when no `pglite` is supplied the pool creates its own\n * in-memory `PGlite` and owns it — `end()` shuts it down. When you supply\n * a `pglite`, the pool treats it as caller-owned and `end()` leaves it open.\n *\n * Most users should prefer {@link PGliteBridge}, which wraps this class and\n * also handles schema application and reset/snapshot lifecycle.\n *\n * ```typescript\n * import { PgBridgePool } from 'prisma-pglite-bridge';\n * import { PrismaPg } from '@prisma/adapter-pg';\n * import { PrismaClient } from '@prisma/client';\n *\n * // Pool creates and owns its own in-memory PGlite:\n * const pool = new PgBridgePool();\n * const adapter = new PrismaPg(pool);\n * const prisma = new PrismaClient({ adapter });\n * await prisma.$disconnect();\n * await pool.end(); // closes pool + pglite (pool owns it)\n *\n * // Caller-supplied PGlite — caller owns the lifecycle:\n * import { PGlite } from '@electric-sql/pglite';\n * const pglite = new PGlite();\n * const pool = new PgBridgePool({ pglite });\n * await pool.end(); // closes pool only; pglite stays open\n * await pglite.close(); // caller is responsible\n * ```\n *\n * @see {@link PGliteBridge} for the higher-level API with schema management\n * and reset/snapshot lifecycle.\n */\nexport class PgBridgePool extends pg.Pool {\n /**\n * Identity tag published on every diagnostics-channel event from this\n * pool. Stable for the lifetime of the pool — the same `symbol`\n * reference appears on every event.\n */\n readonly bridgeId: symbol;\n\n /**\n * The PGlite instance this pool wraps. Created internally when no `pglite`\n * option was supplied; otherwise the caller-supplied instance.\n */\n readonly pglite: PGlite | PGliteInterface;\n\n readonly #ownsPglite: boolean;\n\n constructor({\n bridgeId = Symbol('bridge'),\n max = 1,\n pglite,\n telemetry,\n timeout,\n syncToFs,\n }: PgBridgePoolOptions & { telemetry?: TelemetrySink } = {}) {\n const resolvedPglite = pglite ?? new PGlite();\n\n // Load-bearing: pg.Pool forwards this config object verbatim to\n // `new Client(config)`, including the symbol-keyed property below.\n // PgBridgeClient reads its bridge options from the same symbol.\n const poolConfig = {\n Client: PgBridgeClient,\n max,\n [PgBridgeClient.OptionsKey]: {\n pglite: resolvedPglite,\n sessionLock: max > 1 ? new SessionLock() : undefined,\n bridgeId,\n telemetry,\n syncToFs: resolveSyncToFs(resolvedPglite, syncToFs),\n timeout,\n },\n };\n\n super(poolConfig);\n\n this.bridgeId = bridgeId;\n this.pglite = resolvedPglite;\n this.#ownsPglite = !pglite;\n }\n\n /**\n * Drain the pool and close all connections. When the pool created its own\n * PGlite (no `pglite` option at construction), also closes that instance.\n * When the caller supplied a `pglite`, it is left open.\n */\n override end(): Promise<void>;\n override end(callback: () => void): void;\n override end(callback?: () => void): Promise<void> | void {\n const cleanup = async (): Promise<void> => {\n if (this.#ownsPglite && !this.pglite.closed) {\n await this.pglite.close();\n }\n };\n if (callback) {\n super.end(() => {\n cleanup().then(callback, callback);\n });\n return;\n }\n return super.end().then(cleanup);\n }\n}\n","/**\n * Private per-bridge stats state used to power `stats()`.\n *\n * Instantiated at level `'basic'` or `'full'` (level `'off'` means no stats\n * object exists).\n * Query-level timing is recorded directly by the bridge; one-shot lifecycle\n * signals (`incrementResetDb`, `freeze`) remain direct method calls invoked\n * by the bridge itself.\n *\n * Percentiles use nearest-rank (no interpolation) over a sliding window of\n * the most recent {@link QUERY_DURATION_WINDOW_SIZE} queries. Lifetime\n * counters (`queryCount`, `totalQueryMs`, `avgQueryMs`) are not windowed.\n * `durationMs` is frozen at the instant `close()` was invoked, via the\n * `closeEntryHrtime` the bridge records as its very first action and\n * passes to {@link freeze}.\n */\nimport type { PGlite } from '@electric-sql/pglite';\nimport { nsToMs } from '../utils/time.ts';\n\ntype DbSizeQueryable = Pick<PGlite, 'query'>;\n\n/**\n * Stats collection level.\n *\n * - `'off'` — `stats()` returns `undefined`. Zero hot-path overhead.\n * - `'basic'` — timing (`durationMs`), query percentiles, counters, and\n * `dbSizeBytes`.\n * - `'full'` — `'basic'` plus `processRssPeakBytes` and session-lock\n * waits.\n */\nexport type StatsLevel = 'off' | 'basic' | 'full';\n\n/** Internal bridge-facing telemetry contract. */\nexport interface TelemetrySink {\n recordQuery(durationMs: number, succeeded: boolean): void;\n recordLockWait(durationMs: number): void;\n}\n\n/**\n * Maximum number of recent query durations retained for percentile\n * computation. Beyond this window, `recentP50QueryMs`, `recentP95QueryMs`,\n * and `recentMaxQueryMs` reflect only the most recent N queries — lifetime\n * counters (`queryCount`, `totalQueryMs`, `avgQueryMs`) remain complete.\n */\nexport const QUERY_DURATION_WINDOW_SIZE = 10_000;\n\nconst QUERY_DURATION_TRIM_THRESHOLD = QUERY_DURATION_WINDOW_SIZE * 2;\n\ninterface StatsBase {\n durationMs: number;\n /** Lifetime count of recorded queries. Not windowed. */\n queryCount: number;\n failedQueryCount: number;\n /** Lifetime sum of query durations. Not windowed. */\n totalQueryMs: number;\n /** Lifetime mean query duration. Not windowed. */\n avgQueryMs: number;\n /**\n * Nearest-rank 50th percentile over the most recent\n * {@link QUERY_DURATION_WINDOW_SIZE} queries. Compare to `avgQueryMs`\n * with care: the two fields describe different populations on long-lived\n * bridges.\n */\n recentP50QueryMs: number;\n /** Nearest-rank 95th percentile over the recent-query window. */\n recentP95QueryMs: number;\n /** Maximum query duration within the recent-query window. */\n recentMaxQueryMs: number;\n resetDbCalls: number;\n /** Undefined only when the `pg_database_size` query rejected. */\n dbSizeBytes?: number;\n}\n\nexport interface StatsBasic extends StatsBase {\n statsLevel: 'basic';\n}\n\nexport interface StatsFull extends StatsBase {\n statsLevel: 'full';\n /**\n * Process-wide RSS high-water mark since process start, read from\n * `process.resourceUsage().maxRSS` (kernel-tracked, lossless). Reflects\n * the entire Node process — parallel test runners, other bridges, and\n * prior work in the same process all contribute. Use only as an\n * ordering signal, not an absolute measurement.\n *\n * `undefined` on runtimes that don't expose `process.resourceUsage`\n * (e.g. Bun, Deno, edge workers) — matches the field-level-undefined\n * contract of every other `Stats` member.\n */\n processRssPeakBytes: number | undefined;\n totalSessionLockWaitMs: number;\n sessionLockAcquisitionCount: number;\n avgSessionLockWaitMs: number;\n maxSessionLockWaitMs: number;\n}\n\n/**\n * Snapshot returned by {@link PGliteBridge.stats}. Discriminated by\n * `statsLevel` — narrow before reading `'full'`-only fields.\n *\n * @example\n * ```typescript\n * const stats = await bridge.stats();\n * if (stats?.statsLevel === 'full') {\n * console.log(stats.processRssPeakBytes); // typed as number | undefined\n * }\n * ```\n */\nexport type Stats = StatsBasic | StatsFull;\n\nconst DB_SIZE_QUERY_TIMEOUT_MS = 5_000;\n\n/**\n * `process.resourceUsage().maxRSS` returns kilobytes on every platform\n * Node supports — we convert to bytes so the public `processRssPeakBytes`\n * field matches its name. Returns `undefined` on runtimes that don't\n * expose `resourceUsage` (Bun, Deno, edge workers).\n */\nconst readProcessRssPeakBytes = (): number | undefined => {\n try {\n return process.resourceUsage().maxRSS * 1024;\n } catch {\n return undefined;\n }\n};\n\nconst percentile = (sorted: readonly number[], p: number): number => {\n const n = sorted.length;\n if (n === 0) return 0;\n const index = Math.min(n - 1, Math.max(0, Math.ceil((p / 100) * n) - 1));\n /* c8 ignore next */\n return sorted[index] ?? 0;\n};\n\nexport class BridgeStats implements TelemetrySink {\n private readonly level: 'basic' | 'full';\n private readonly createdAtHrtime: bigint;\n\n private queryDurations: number[] = [];\n private totalQueryMs = 0;\n private queryCount = 0;\n private failedQueryCount = 0;\n private resetDbCalls = 0;\n\n private totalSessionLockWaitMs = 0;\n private maxSessionLockWaitMs = 0;\n private sessionLockAcquisitionCount = 0;\n\n private frozen = false;\n private cachedDurationMs?: number;\n private cachedDbSizeBytes?: number;\n private dbSizeFrozen = false;\n\n constructor(level: 'basic' | 'full') {\n this.level = level;\n this.createdAtHrtime = process.hrtime.bigint();\n }\n\n recordQuery(durationMs: number, succeeded: boolean): void {\n if (this.frozen) return;\n this.queryCount += 1;\n this.totalQueryMs += durationMs;\n this.queryDurations.push(durationMs);\n if (this.queryDurations.length > QUERY_DURATION_TRIM_THRESHOLD) {\n this.queryDurations = this.queryDurations.slice(-QUERY_DURATION_WINDOW_SIZE);\n }\n if (!succeeded) this.failedQueryCount += 1;\n }\n\n recordLockWait(durationMs: number): void {\n if (this.frozen) return;\n if (this.level !== 'full') return;\n this.totalSessionLockWaitMs += durationMs;\n this.sessionLockAcquisitionCount += 1;\n if (durationMs > this.maxSessionLockWaitMs) this.maxSessionLockWaitMs = durationMs;\n }\n\n incrementResetDb(): void {\n if (this.frozen) return;\n this.resetDbCalls += 1;\n }\n\n async snapshot(pglite: DbSizeQueryable): Promise<Stats> {\n const durationMs =\n this.cachedDurationMs ?? nsToMs(process.hrtime.bigint() - this.createdAtHrtime);\n const dbSizeBytes = this.dbSizeFrozen ? this.cachedDbSizeBytes : await this.queryDbSize(pglite);\n\n const sorted = [...this.queryDurations].sort((a, b) => a - b);\n const avgQueryMs = this.queryCount === 0 ? 0 : this.totalQueryMs / this.queryCount;\n\n const base: StatsBase = {\n durationMs,\n queryCount: this.queryCount,\n failedQueryCount: this.failedQueryCount,\n totalQueryMs: this.totalQueryMs,\n avgQueryMs,\n recentP50QueryMs: percentile(sorted, 50),\n recentP95QueryMs: percentile(sorted, 95),\n recentMaxQueryMs: percentile(sorted, 100),\n resetDbCalls: this.resetDbCalls,\n dbSizeBytes,\n };\n\n if (this.level === 'basic') {\n return { ...base, statsLevel: 'basic' };\n }\n\n const count = this.sessionLockAcquisitionCount;\n return {\n ...base,\n statsLevel: 'full',\n processRssPeakBytes: readProcessRssPeakBytes(),\n totalSessionLockWaitMs: this.totalSessionLockWaitMs,\n sessionLockAcquisitionCount: count,\n avgSessionLockWaitMs: count === 0 ? 0 : this.totalSessionLockWaitMs / count,\n maxSessionLockWaitMs: this.maxSessionLockWaitMs,\n };\n }\n\n async freeze(pglite: DbSizeQueryable, closeEntryHrtime: bigint): Promise<void> {\n if (this.frozen) return;\n this.frozen = true;\n\n this.cachedDurationMs = nsToMs(closeEntryHrtime - this.createdAtHrtime);\n try {\n this.cachedDbSizeBytes = await this.queryDbSize(pglite);\n } finally {\n this.dbSizeFrozen = true;\n }\n }\n\n private async queryDbSize(pglite: DbSizeQueryable): Promise<number | undefined> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error('pg_database_size query timed out')),\n DB_SIZE_QUERY_TIMEOUT_MS,\n );\n timer.unref?.();\n });\n const { rows } = await Promise.race([\n pglite.query<{ size: string | null }>(\n 'SELECT pg_database_size(current_database())::text AS size',\n ),\n timeout,\n ]);\n const size = rows[0]?.size;\n if (size == null) return undefined;\n const n = Number(size);\n return Number.isFinite(n) ? n : undefined;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n","import type { PGlite, PGliteInterface } from '@electric-sql/pglite';\n\nconst SNAPSHOT_SCHEMA = '_pglite_snapshot';\n\nconst USER_TABLES_WHERE = `schemaname NOT IN ('pg_catalog', 'information_schema')\n AND schemaname != '${SNAPSHOT_SCHEMA}'\n AND tablename NOT LIKE '_prisma%'`;\n\nconst escapeLiteral = (s: string): string => `'${s.replace(/'/g, \"''\")}'`;\n\n/** JS equivalent of PostgreSQL's `quote_ident()`; matches its escaping rules. */\nconst quoteIdent = (identifier: string): string => `\"${identifier.replace(/\"/g, '\"\"')}\"`;\n\nconst SNAPSHOT_SCHEMA_IDENT = quoteIdent(SNAPSHOT_SCHEMA);\nconst SNAPSHOT_SCHEMA_LITERAL = escapeLiteral(SNAPSHOT_SCHEMA);\n\n/**\n * Snapshot helpers backing `PGliteBridge`'s `snapshotDb` / `resetDb` /\n * `resetSnapshot` functions. Stores a copy of user tables and sequence\n * values in a dedicated `_pglite_snapshot` schema so tests can reset to a\n * known seed state without re-running migrations.\n *\n * @internal\n */\nexport class SnapshotManager {\n readonly #pglite: PGlite | PGliteInterface;\n #hasSnapshot = false;\n\n constructor(pglite: PGlite | PGliteInterface) {\n this.#pglite = pglite;\n }\n\n /**\n * Capture the current state of all user tables plus sequence values into\n * the `_pglite_snapshot` schema. Replaces any previous snapshot.\n */\n async snapshotDb(): Promise<void> {\n const pglite = this.#pglite;\n await pglite.exec(`DROP SCHEMA IF EXISTS ${SNAPSHOT_SCHEMA_IDENT} CASCADE`);\n\n try {\n await pglite.exec('BEGIN');\n await pglite.exec(`CREATE SCHEMA ${SNAPSHOT_SCHEMA_IDENT}`);\n\n const { rows: tables } = await pglite.query<{\n schemaname: string;\n tablename: string;\n qualified: string;\n }>(\n `SELECT schemaname, tablename,\n quote_ident(schemaname) || '.' || quote_ident(tablename) AS qualified\n FROM pg_tables\n WHERE ${USER_TABLES_WHERE}`,\n );\n\n await pglite.exec(\n `CREATE TABLE ${SNAPSHOT_SCHEMA_IDENT}.__tables (snap_name text, source_schema text, source_table text)`,\n );\n\n for (const [i, { schemaname, tablename, qualified }] of tables.entries()) {\n const snapName = `_snap_${i}`;\n await pglite.exec(\n `CREATE TABLE ${SNAPSHOT_SCHEMA_IDENT}.${quoteIdent(snapName)} AS SELECT * FROM ${qualified}`,\n );\n await pglite.exec(\n `INSERT INTO ${SNAPSHOT_SCHEMA_IDENT}.__tables VALUES (${escapeLiteral(snapName)}, ${escapeLiteral(schemaname)}, ${escapeLiteral(tablename)})`,\n );\n }\n\n const { rows: seqs } = await pglite.query<{ name: string; value: string }>(\n `SELECT quote_literal(quote_ident(schemaname) || '.' || quote_ident(sequencename)) AS name, last_value::text AS value\n FROM pg_sequences\n WHERE schemaname NOT IN ('pg_catalog', 'information_schema')\n AND schemaname != '${SNAPSHOT_SCHEMA}'\n AND last_value IS NOT NULL`,\n );\n\n await pglite.exec(\n `CREATE TABLE ${SNAPSHOT_SCHEMA_IDENT}.__sequences (name text, value bigint)`,\n );\n for (const { name, value } of seqs) {\n await pglite.exec(\n `INSERT INTO ${SNAPSHOT_SCHEMA_IDENT}.__sequences VALUES (${name}, ${value})`,\n );\n }\n\n await pglite.exec('COMMIT');\n } catch (err) {\n await pglite.exec('ROLLBACK');\n await pglite.exec(`DROP SCHEMA IF EXISTS ${SNAPSHOT_SCHEMA_IDENT} CASCADE`);\n throw err;\n }\n\n this.#hasSnapshot = true;\n }\n\n /** Drop the saved snapshot, reverting `resetDb` to plain truncation. */\n async resetSnapshot(): Promise<void> {\n this.#hasSnapshot = false;\n await this.#pglite.exec(`DROP SCHEMA IF EXISTS ${SNAPSHOT_SCHEMA_IDENT} CASCADE`);\n }\n\n /**\n * Truncate all user tables. If a snapshot exists, restore its contents and\n * sequence values afterwards; otherwise just truncate and `DISCARD ALL`.\n */\n async resetDb(): Promise<void> {\n const pglite = this.#pglite;\n if (this.#hasSnapshot) await this.#snapshotSchemaExists();\n\n const tables = await this.#getTables();\n\n if (tables) {\n await this.#withReplicationRoleReplica(async () => {\n await pglite.exec(`TRUNCATE TABLE ${tables} RESTART IDENTITY CASCADE`);\n\n if (!this.#hasSnapshot) return;\n\n const { rows: snapshotTables } = await pglite.query<{\n snap_name_ident: string;\n qualified: string;\n }>(\n `SELECT quote_ident(snap_name) AS snap_name_ident,\n quote_ident(source_schema) || '.' || quote_ident(source_table) AS qualified\n FROM ${SNAPSHOT_SCHEMA_IDENT}.__tables`,\n );\n\n for (const { snap_name_ident, qualified } of snapshotTables) {\n await pglite.exec(\n `INSERT INTO ${qualified} SELECT * FROM ${SNAPSHOT_SCHEMA_IDENT}.${snap_name_ident}`,\n );\n }\n\n const { rows: seqs } = await pglite.query<{ name: string; value: string }>(\n `SELECT quote_literal(name) AS name, value::text AS value FROM ${SNAPSHOT_SCHEMA_IDENT}.__sequences`,\n );\n\n for (const { name, value } of seqs) {\n await pglite.exec(`SELECT setval(${name}, ${value})`);\n }\n });\n }\n\n await pglite.exec('DISCARD ALL');\n }\n\n async #getTables(): Promise<string> {\n const { rows } = await this.#pglite.query<{ qualified: string }>(\n `SELECT quote_ident(schemaname) || '.' || quote_ident(tablename) AS qualified\n FROM pg_tables\n WHERE ${USER_TABLES_WHERE}`,\n );\n return rows.length > 0 ? rows.map((row) => row.qualified).join(', ') : '';\n }\n\n /**\n * Self-heal: someone (e.g. `resetSchema`) may drop `_pglite_snapshot`\n * out from under us. Returns whether the schema actually exists right now,\n * and clears `#hasSnapshot` if it doesn't.\n */\n async #snapshotSchemaExists(): Promise<boolean> {\n const { rows } = await this.#pglite.query<{ exists: boolean }>(\n `SELECT to_regnamespace(${SNAPSHOT_SCHEMA_LITERAL}) IS NOT NULL AS exists`,\n );\n const exists = rows[0]?.exists;\n if (!exists) this.#hasSnapshot = false;\n return !!exists;\n }\n\n async #withReplicationRoleReplica(fn: () => Promise<void>): Promise<void> {\n try {\n await this.#pglite.exec('SET session_replication_role = replica');\n await fn();\n } finally {\n await this.#pglite.exec('SET session_replication_role = DEFAULT');\n }\n }\n}\n","/**\n * `PGliteBridge` bundles a Prisma driver adapter, the underlying PGlite\n * instance, and lifecycle helpers. No TCP, no Docker, no worker threads —\n * everything runs in the same process. Suitable for testing, development,\n * seeding, and scripts.\n *\n * **Ownership:** When no `pglite` option is supplied the bridge creates its\n * own in-memory `PGlite` instance and owns it — `close()` shuts down the\n * pool _and_ the PGlite instance. When you supply a `pglite` the bridge\n * treats it as caller-owned and `close()` leaves it open.\n *\n * Schema application is a separate concern: call {@link pushMigrations}\n * (raw SQL / migrations directory) or {@link pushSchema} (WASM-engine diff)\n * before issuing Prisma traffic. When reopening a persistent `dataDir`, the\n * PGlite instance is assumed to already hold the schema.\n *\n * ```typescript\n * import { PGliteBridge, pushMigrations } from 'prisma-pglite-bridge';\n * import { PrismaClient } from '@prisma/client';\n *\n * // Bridge creates and owns its own in-memory PGlite:\n * const bridge = new PGliteBridge();\n * await pushMigrations(bridge.pglite, { migrationsPath: './prisma/migrations' });\n * const prisma = new PrismaClient({ adapter: bridge.adapter });\n * beforeEach(() => bridge.resetDb());\n * afterAll(async () => {\n * await prisma.$disconnect();\n * await bridge.close(); // closes pool + pglite (bridge owns it)\n * });\n *\n * // Caller-supplied PGlite — caller owns the lifecycle:\n * import { PGlite } from '@electric-sql/pglite';\n * const pglite = new PGlite();\n * const bridge = new PGliteBridge({ pglite });\n * afterAll(async () => {\n * await bridge.close(); // closes pool only; pglite stays open\n * await pglite.close(); // caller is responsible\n * });\n * ```\n *\n * Public methods are arrow-function class fields so destructuring stays safe:\n * `const { resetDb } = bridge; await resetDb();` works as expected.\n */\nimport { PGlite, type PGliteInterface } from '@electric-sql/pglite';\nimport { PrismaPg } from '@prisma/adapter-pg';\n\nimport { PgBridgePool } from '../pool';\nimport { BridgeStats, type Stats, type StatsLevel } from '../telemetry/bridge-stats.ts';\nimport type { SyncToFsMode } from '../utils/resolve-sync-to-fs.ts';\nimport { SnapshotManager } from './snapshot-manager.ts';\n\n/** @internal Exported for testing. */\nexport const emitBridgeLeakWarning = (): void => {\n process.emitWarning(\n 'PGliteBridge was garbage-collected before close() was called. ' +\n 'Call bridge.close() to release the pool and finalize stats().',\n { type: 'PGliteBridgeLeakWarning' },\n );\n};\n\nconst leakRegistry = new FinalizationRegistry<void>(emitBridgeLeakWarning);\n\nexport interface PGliteBridgeOptions {\n /**\n * Identity tag published with every diagnostics-channel event. Subscribers\n * filter on this to distinguish events from different bridges in the\n * same process. A fresh `Symbol('bridge')` is generated if omitted.\n */\n bridgeId?: symbol;\n\n /**\n * PGlite instance to bridge to. When omitted the bridge creates its own\n * in-memory `PGlite` and owns its lifecycle — `close()` shuts it down.\n * When provided the caller owns the lifecycle — `close()` leaves it open.\n */\n pglite?: PGlite | PGliteInterface;\n\n /**\n * Maximum pool connections (default: 1). Compatibility knob, not a\n * throughput knob.\n *\n * PGlite serialises queries inside its WASM runtime. Extra pool connections\n * do not add parallelism; they only add bridge/client memory and\n * session-lock coordination. Leave this at `1` unless the code under test\n * specifically needs multiple checked-out `pg` clients.\n */\n max?: number;\n\n /**\n * Collect bridge/query telemetry. Default `'off'` (zero overhead).\n *\n * - `'basic'` — timing (`durationMs`, query percentiles) and counters\n * (`queryCount`, `failedQueryCount`, `resetDbCalls`), plus\n * `dbSizeBytes`.\n * - `'full'` — everything in `'basic'`, plus `processRssPeakBytes`\n * (process-wide, sampled) and session-lock wait statistics.\n *\n * Retrieve via `await bridge.stats()` — returns `undefined` at `'off'`.\n */\n statsLevel?: StatsLevel;\n\n /**\n * Filesystem sync policy for bridge-driven wire-protocol calls.\n *\n * Default `'auto'`: disables per-query sync for clearly in-memory PGlite\n * instances and keeps it enabled otherwise. Set `true` to prefer durability\n * on persistent stores, or `false` to prefer lower RSS / higher throughput.\n *\n * If you provide a custom persistent PGlite `fs` without a meaningful\n * `dataDir`, pass `true` explicitly.\n */\n syncToFs?: SyncToFsMode;\n\n /**\n * Maximum milliseconds to wait for the PGlite instance to become ready\n * before each bridge operation. Defaults to no timeout (waits indefinitely),\n * matching the previous unbounded `await pglite.waitReady` behavior.\n */\n timeout?: number;\n}\n\nexport class PGliteBridge {\n /** Prisma adapter — pass directly to `new PrismaClient({ adapter })`. */\n readonly adapter: PrismaPg;\n\n /**\n * The PGlite instance this bridge wraps. Created internally when no\n * `pglite` option was supplied; otherwise the caller-supplied instance.\n * Exposed so helpers like {@link pushMigrations} can run SQL directly\n * through `pglite.exec(...)` without going through the bridge pool.\n */\n readonly pglite: PGlite | PGliteInterface;\n\n /**\n * Identity tag published on every `QUERY_CHANNEL` / `LOCK_WAIT_CHANNEL`\n * diagnostics event produced by this bridge. External subscribers\n * filter on it to isolate events from this bridge in multi-bridge\n * processes. Stable for the lifetime of the bridge instance — the same\n * `symbol` reference appears on every event from the same bridge.\n */\n readonly bridgeId: symbol;\n\n readonly #pool: PgBridgePool;\n readonly #stats: BridgeStats | undefined;\n readonly #snapshot: SnapshotManager;\n readonly #leakToken: object = {};\n readonly #ownsPglite: boolean;\n #closing: Promise<void> | undefined;\n\n constructor(options: PGliteBridgeOptions = {}) {\n const statsLevel = options.statsLevel ?? 'off';\n if (statsLevel !== 'off' && statsLevel !== 'basic' && statsLevel !== 'full') {\n throw new Error(`statsLevel must be 'off', 'basic', or 'full'; got ${String(statsLevel)}`);\n }\n\n this.#ownsPglite = !options.pglite;\n this.pglite = options.pglite ?? new PGlite();\n this.bridgeId = options.bridgeId ?? Symbol('bridge');\n\n this.#stats = statsLevel === 'off' ? undefined : new BridgeStats(statsLevel);\n this.#pool = new PgBridgePool({\n ...options,\n pglite: this.pglite,\n bridgeId: this.bridgeId,\n telemetry: this.#stats,\n });\n this.#snapshot = new SnapshotManager(this.pglite);\n\n this.adapter = new PrismaPg(this.#pool);\n\n leakRegistry.register(this.adapter, undefined, this.#leakToken);\n }\n\n /**\n * Clear all user tables and discard session-local state. Call in\n * `beforeEach` for per-test isolation. When a snapshot has been taken\n * via {@link snapshotDb}, restores from that snapshot instead of\n * truncating to empty.\n *\n * Throws if any pool client is currently checked out — the operation\n * runs raw SQL on the PGlite instance bypassing the pool, so concurrent\n * pool traffic would interleave unsafely. Await all pending Prisma\n * queries first.\n */\n resetDb = async (): Promise<void> => {\n this.#assertPoolIdle('resetDb');\n this.#stats?.incrementResetDb();\n return this.#snapshot.resetDb();\n };\n\n /**\n * Snapshot the current DB state into an internal `_pglite_snapshot`\n * schema. Subsequent `resetDb` calls restore from this snapshot instead\n * of truncating to empty.\n *\n * Throws if any pool client is currently checked out — the operation\n * runs multiple `exec()` statements directly against the PGlite\n * instance, bypassing the pool's `SessionLock`. Call from a test\n * `beforeAll` after migrations but before Prisma traffic starts.\n */\n snapshotDb = async (): Promise<void> => {\n this.#assertPoolIdle('snapshotDb');\n return this.#snapshot.snapshotDb();\n };\n\n /**\n * Discard the current snapshot. Subsequent `resetDb` calls truncate to\n * empty. Same concurrency requirements as {@link snapshotDb} — throws if\n * any pool client is currently checked out.\n */\n resetSnapshot = async (): Promise<void> => {\n this.#assertPoolIdle('resetSnapshot');\n return this.#snapshot.resetSnapshot();\n };\n\n #assertPoolIdle(method: string): void {\n const inFlight = this.#pool.totalCount - this.#pool.idleCount;\n if (inFlight > 0) {\n throw new Error(\n `${method}() requires no in-flight pool queries; got ${inFlight}. ` +\n 'Await all pending Prisma queries (or end an open `$transaction`) before calling.',\n );\n }\n }\n\n /**\n * Shut down the pool. When the bridge created its own PGlite (no `pglite`\n * option at construction), also closes that instance. When the caller\n * supplied a `pglite`, it is left open — the caller is responsible for\n * closing it.\n *\n * When `statsLevel` is not `'off'`, call {@link stats} *after* `close()`\n * to collect the frozen snapshot — `durationMs` and `dbSizeBytes` are\n * cached at the moment `close()` is invoked, and subsequent `stats()`\n * calls are safe.\n */\n close = async (): Promise<void> => {\n if (!this.#closing) {\n this.#closing = (async () => {\n const closeEntry = this.#stats ? process.hrtime.bigint() : undefined;\n await this.#pool.end();\n if (closeEntry !== undefined) {\n await this.#stats?.freeze(this.pglite, closeEntry);\n }\n leakRegistry.unregister(this.#leakToken);\n if (this.#ownsPglite && !this.pglite.closed) {\n await this.pglite.close();\n }\n })();\n }\n return this.#closing;\n };\n\n /**\n * Retrieve collected telemetry. Returns `undefined` when `statsLevel`\n * was `'off'` (or omitted). Never throws — field-level failures surface\n * as `undefined` values (see {@link Stats}).\n */\n stats = async (): Promise<Stats | undefined> => {\n return this.#stats ? this.#stats.snapshot(this.pglite) : undefined;\n };\n}\n","/**\n * TCP / Unix-socket server that exposes a PGlite instance to standard\n * PostgreSQL clients (`psql`, Prisma CLI shadow database, DBeaver, Studio,\n * etc.).\n *\n * Each accepted socket gets its own {@link PGliteDuplex}, all sharing one\n * {@link SessionLock} so transactions across connections serialize correctly.\n * SSL / GSS pre-negotiation is rejected with a single `N` byte so clients\n * fall back to plaintext; there is no authentication. Bind to loopback only\n * — this is a development tool, not a hardened endpoint.\n *\n * **Ownership:** When no `pglite` option is supplied the server creates its\n * own in-memory `PGlite` instance and owns it — `close()` shuts down the\n * listener _and_ the PGlite instance. When you supply a `pglite` the server\n * treats it as caller-owned and `close()` leaves it open.\n *\n * The constructor is synchronous; the network bind is exposed as an\n * explicit async `listen()` (mirroring `net.Server`'s API) which awaits\n * `pglite.waitReady` internally and resolves to the connection URL.\n *\n * ```typescript\n * // Server creates and owns its own in-memory PGlite:\n * const server = new PGliteServer();\n * const url = await server.listen();\n * console.log(url); // → postgres://postgres@127.0.0.1:54321/postgres\n * await server.close(); // closes listener + pglite (server owns it)\n *\n * // Caller-supplied PGlite — caller owns the lifecycle:\n * import { PGlite } from '@electric-sql/pglite';\n * const pglite = new PGlite();\n * const server = new PGliteServer({ pglite });\n * await server.close(); // closes listener only; pglite stays open\n * await pglite.close(); // caller is responsible\n * ```\n */\nimport net from 'node:net';\nimport nodePath from 'node:path';\nimport { PGlite, type PGliteInterface } from '@electric-sql/pglite';\n\nimport { PGliteDuplex } from '../duplex';\nimport { resolveSyncToFs, type SyncToFsMode } from '../utils/resolve-sync-to-fs.ts';\nimport { SessionLock } from '../utils/session-lock.ts';\n\nconst SSL_REQUEST_CODE = 80877103;\nconst GSSENC_REQUEST_CODE = 80877104;\nconst CANCEL_REQUEST_CODE = 80877102;\nconst PRELUDE_HEADER_BYTES = 8;\nconst DEFAULT_SOCKET_PORT = 5432;\n\ntype RequiredBy<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;\n\nexport interface PGliteServerOptions {\n /**\n * PGlite instance to expose. When omitted the server creates its own\n * in-memory `PGlite` and owns its lifecycle — `close()` shuts it down.\n * When provided the caller owns the lifecycle — `close()` leaves it open.\n *\n * `listen()` awaits `pglite.waitReady` internally, so the instance does\n * not have to be ready at construction time.\n */\n pglite?: PGlite | PGliteInterface;\n /** Bind host. Default `'127.0.0.1'` (loopback only). Ignored when `dataDir` is set. */\n host?: string;\n /**\n * In TCP mode: the listen port. Default `0` (ephemeral — read back from\n * the URL returned by `listen()`).\n *\n * In Unix-socket mode (when `dataDir` is set): the libpq port-suffix used\n * to build the socket filename `<dataDir>/.s.PGSQL.<port>`. Default `5432`.\n */\n port?: number;\n /**\n * If set, switches to Unix-socket mode. The server binds the\n * libpq-conventional path `<dataDir>/.s.PGSQL.<port>` so `psql` and\n * `pg.Client` can connect with just `host=<dataDir>`. Takes precedence\n * over `host`. The caller is responsible for the directory's writability\n * and access mode; `close()` does not remove the socket file, and\n * `listen()` will fail with `EADDRINUSE` if a stale socket remains —\n * unlink first.\n */\n dataDir?: string;\n /** Filesystem sync policy. See {@link SyncToFsMode}. Default `'auto'`. */\n syncToFs?: SyncToFsMode;\n /**\n * Maximum milliseconds to wait for the PGlite instance to become ready\n * before each bridge operation. Defaults to no timeout (waits indefinitely).\n */\n timeout?: number;\n /**\n * Username embedded in the connection URL returned by `listen()`.\n * Default `'postgres'`. PGlite ignores this — there is no authentication —\n * but Prisma 7's schema engine rejects URLs without a user (P1010), so a\n * value is always emitted.\n */\n user?: string;\n}\n\ntype BridgedSocket = net.Socket & { duplex?: PGliteDuplex };\n\nexport class PGliteServer {\n /**\n * The PGlite instance this server fronts. Created internally when no\n * `pglite` option was supplied; otherwise the caller-supplied instance.\n * Exposed so scripts can reach `pglite.dataDir`, `pglite.waitReady`, and\n * pass the same handle to helpers like {@link pushMigrations} without\n * threading a separate variable.\n */\n readonly pglite: PGlite | PGliteInterface;\n\n readonly #options: RequiredBy<Omit<PGliteServerOptions, 'pglite'>, 'host' | 'port' | 'user'>;\n readonly #server: net.Server;\n readonly #sessionLock = new SessionLock();\n readonly #sockets = new Set<BridgedSocket>();\n readonly #ownsPglite: boolean;\n\n #connectionString: string | undefined;\n\n constructor(options: PGliteServerOptions = {}) {\n const { pglite, ...rest } = options;\n\n this.#ownsPglite = !pglite;\n this.pglite = pglite ?? new PGlite();\n this.#options = {\n ...rest,\n host: rest.host || '127.0.0.1',\n port: rest.port ?? (!rest.dataDir ? 0 : DEFAULT_SOCKET_PORT),\n user: rest.user ? encodeURIComponent(rest.user) : 'postgres',\n };\n this.#server = net.createServer((socket) => this.#onConnection(socket));\n }\n\n listen = async (): Promise<string> => {\n if (this.#connectionString) return this.#connectionString;\n if (this.pglite.closed) {\n throw new Error('PGliteServer requires an open PGlite instance; got a closed one.');\n }\n\n return new Promise<string>((resolve, reject) => {\n const onError = (err: Error): void => reject(err);\n this.#server.once('error', onError);\n\n // Socket\n if (this.#options.dataDir) {\n const { dataDir, port, user } = this.#options;\n\n this.#server.listen(nodePath.join(dataDir, `.s.PGSQL.${port}`), () => {\n this.#server.removeListener('error', onError);\n this.#connectionString = `postgres://${user}@/postgres?host=${encodeURIComponent(dataDir)}&port=${port}`;\n return resolve(this.#connectionString);\n });\n\n return;\n }\n\n // TCP\n this.#server.listen(this.#options.port, this.#options.host, () => {\n this.#server.removeListener('error', onError);\n\n const { user } = this.#options;\n const { address, family, port } = this.#server.address() as net.AddressInfo;\n\n this.#connectionString =\n family === 'IPv6'\n ? `postgres://${user}@/postgres?host=${encodeURIComponent(address)}&port=${port}`\n : `postgres://${user}@${address}:${port}/postgres`;\n this.#options.port = port; // re-assign used port\n\n return resolve(this.#connectionString);\n });\n });\n };\n\n /**\n * Shut down the listener. When the server created its own PGlite (no\n * `pglite` option at construction), also closes that instance. When the\n * caller supplied a `pglite`, it is left open — the caller is responsible\n * for closing it.\n */\n close = async (): Promise<void> => {\n const sockets = [...this.#sockets];\n for (const socket of sockets) socket.destroy();\n await new Promise<void>((resolve, reject) => {\n this.#server.close((err) => (err ? reject(err) : resolve()));\n });\n await Promise.all(sockets.map(({ duplex }) => duplex?.onClose));\n if (this.#ownsPglite && !this.pglite.closed) {\n await this.pglite.close();\n }\n };\n\n #initDuplex(socket: BridgedSocket): PGliteDuplex {\n socket.duplex = new PGliteDuplex(this.pglite, {\n sessionLock: this.#sessionLock,\n timeout: this.#options.timeout,\n syncToFs: resolveSyncToFs(this.pglite, this.#options.syncToFs),\n });\n socket.duplex.on('error', () => socket.destroy());\n socket.once('close', () => {\n const { duplex } = socket;\n if (duplex && !duplex.destroyed) duplex.destroy();\n });\n socket.pipe(socket.duplex).pipe(socket);\n return socket.duplex;\n }\n\n #onConnection(socket: BridgedSocket): void {\n socket.setNoDelay(true);\n this.#sockets.add(socket);\n socket.on('close', () => this.#sockets.delete(socket));\n socket.on('error', () => socket.destroy());\n\n let buffer: Buffer = Buffer.alloc(0);\n\n const onData = (chunk: Buffer): void => {\n buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk]);\n\n // Drain SSL/GSS preludes; libpq may chain them before StartupMessage.\n while (buffer.length >= PRELUDE_HEADER_BYTES) {\n const len = buffer.readInt32BE(0);\n const code = buffer.readInt32BE(4);\n if (len === 8 && (code === SSL_REQUEST_CODE || code === GSSENC_REQUEST_CODE)) {\n socket.write('N');\n buffer = buffer.subarray(8);\n continue;\n }\n break;\n }\n if (buffer.length < PRELUDE_HEADER_BYTES) return;\n\n const len = buffer.readInt32BE(0);\n const code = buffer.readInt32BE(4);\n\n if (len === 16 && code === CANCEL_REQUEST_CODE) {\n if (buffer.length < 16) return;\n socket.removeListener('data', onData);\n socket.end();\n return;\n }\n\n socket.removeListener('data', onData);\n\n // Hand over to duplex stream\n socket.pause();\n this.#initDuplex(socket).write(buffer);\n socket.resume();\n };\n\n socket.on('data', onData);\n }\n}\n","/**\n * Apply a Prisma schema to a PGlite database in-process via\n * `@prisma/schema-engine-wasm`. No native schema-engine binary, no TCP.\n *\n * `pushSchema` mirrors `prisma db push --skip-generate`: diff the schema\n * against the live database and apply the result. `resetSchema` drops\n * everything reachable through the connection.\n *\n * The schema engine WASM module is dynamically imported so consumers\n * who only use the bridge never load it.\n *\n * @example\n * ```typescript\n * import { PGliteBridge, pushSchema } from 'prisma-pglite-bridge';\n *\n * const bridge = new PGliteBridge();\n *\n * await pushSchema(bridge.adapter, {\n * schema: await fs.readFile('prisma/schema.prisma', 'utf8'),\n * });\n *\n * // teardown\n * await bridge.close(); // closes pool + pglite (bridge owns it)\n * ```\n */\nimport type { PrismaPg } from '@prisma/adapter-pg';\n\nexport interface PushSchemaOptions {\n /** Inline Prisma schema source. */\n schema: string;\n /**\n * Drop everything reachable through the adapter before applying.\n * Issued as a separate `engine.reset(...)` call. Distinct from\n * {@link acceptDataLoss}. Default: `false`.\n */\n forceReset?: boolean;\n /**\n * Forwarded to `SchemaPushInput.force`. Apply the schema even when\n * the engine reports destructive-change warnings. Has no effect on\n * `unexecutable` steps. Default: `false`.\n */\n acceptDataLoss?: boolean;\n /** Logical filename used in schema-engine error messages. Default: `'schema.prisma'`. */\n filename?: string;\n}\n\nexport interface PushSchemaResult {\n /** Number of migration steps the engine applied. */\n executedSteps: number;\n /**\n * Destructive-change warnings reported by the engine. Suppressed\n * (i.e. applied anyway) when {@link PushSchemaOptions.acceptDataLoss} is true.\n */\n warnings: string[];\n /**\n * Steps the engine refused to run regardless of `acceptDataLoss`.\n * The caller must reshape the schema (e.g. add a default value) and retry.\n */\n unexecutable: string[];\n}\n\nconst bindAdapter = async (adapter: PrismaPg): Promise<object> => {\n const { bindMigrationAwareSqlAdapterFactory } = await import('@prisma/driver-adapter-utils');\n return bindMigrationAwareSqlAdapterFactory(adapter);\n};\n\nconst emptyFilter = (): { externalTables: string[]; externalEnums: string[] } => ({\n externalTables: [],\n externalEnums: [],\n});\n\nconst quoteIdent = (name: string): string => `\"${name.replace(/\"/g, '\"\"')}\"`;\n\n/**\n * Drop every non-system schema (and recreate `public`). Issued as raw SQL\n * through the adapter rather than `engine.reset(...)` because the engine only\n * clears schemas declared in its datamodel — anything outside that (`base`,\n * test fixtures, etc.) would otherwise leak between resets.\n *\n * Each DROP runs through `executeRaw` rather than a single `executeScript`,\n * because `executeScript` splits on `;` and would mis-parse schema names that\n * contain a semicolon.\n */\nconst dropAllUserSchemas = async (adapter: PrismaPg): Promise<void> => {\n const conn = await adapter.connect();\n try {\n const result = await conn.queryRaw({\n sql: `SELECT nspname FROM pg_namespace\n WHERE nspname NOT LIKE 'pg_%' AND nspname <> 'information_schema'`,\n args: [],\n argTypes: [],\n });\n const idx = result.columnNames.indexOf('nspname');\n const names = result.rows.map((row) => String(row[idx]));\n for (const name of names) {\n await conn.executeRaw({\n sql: `DROP SCHEMA IF EXISTS ${quoteIdent(name)} CASCADE`,\n args: [],\n argTypes: [],\n });\n }\n await conn.executeRaw({\n sql: `CREATE SCHEMA IF NOT EXISTS \"public\"`,\n args: [],\n argTypes: [],\n });\n } finally {\n await conn.dispose();\n }\n};\n\nexport const pushSchema = async (\n adapter: PrismaPg,\n options: PushSchemaOptions,\n): Promise<PushSchemaResult> => {\n const filename = options.filename ?? 'schema.prisma';\n if (options.forceReset) {\n await dropAllUserSchemas(adapter);\n }\n const { SchemaEngine } = await import('@prisma/schema-engine-wasm');\n const bound = await bindAdapter(adapter);\n\n const engine = await SchemaEngine.new(\n { datamodels: [[filename, options.schema]] },\n () => {},\n bound,\n );\n try {\n const result = await engine.schemaPush({\n force: options.acceptDataLoss ?? false,\n schema: { files: [{ path: filename, content: options.schema }] },\n filters: emptyFilter(),\n });\n return {\n executedSteps: result.executedSteps,\n warnings: result.warnings,\n unexecutable: result.unexecutable,\n };\n } finally {\n engine.free();\n }\n};\n\nexport const resetSchema = async (adapter: PrismaPg): Promise<void> => {\n await dropAllUserSchemas(adapter);\n};\n","/**\n * Apply pre-generated SQL (raw or from `prisma/migrations/`) to a PGlite\n * database. Sibling of {@link pushSchema} — both populate a database, but\n * `pushMigrations` takes a `PGlite` directly and bypasses\n * `@prisma/schema-engine-wasm`. Use when you already have generated SQL\n * and don't need a live schema diff.\n *\n * @example\n * ```typescript\n * import { PGlite } from '@electric-sql/pglite';\n * import { pushMigrations } from 'prisma-pglite-bridge';\n *\n * const pglite = new PGlite();\n * await pushMigrations(pglite, { migrationsPath: './prisma/migrations' });\n * ```\n */\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { PGlite, PGliteInterface } from '@electric-sql/pglite';\n\nexport interface PushMigrationsOptions {\n /** Pre-generated SQL to apply directly. */\n sql?: string;\n /** Path to a `prisma/migrations/` directory (auto-discovered via prisma.config.ts if omitted). */\n migrationsPath?: string;\n /** Root for prisma.config.ts discovery (default: process.cwd()). Set in monorepos where tests run from the workspace root. */\n configRoot?: string;\n}\n\nexport interface PushMigrationsResult {\n /** Wall-clock time of the SQL apply, in milliseconds. */\n durationMs: number;\n}\n\n/**\n * Resolve the migrations directory via Prisma's config API. Uses the same\n * resolution as `prisma migrate dev` — reads prisma.config.ts and resolves\n * paths relative to the config file's location.\n *\n * Returns undefined if @prisma/config is not available or the config\n * cannot be loaded.\n */\nexport const getMigrationsPath = async (configRoot?: string): Promise<string | undefined> => {\n try {\n const { loadConfigFromFile } = await import('@prisma/config');\n const { config, error } = await loadConfigFromFile({ configRoot: configRoot ?? process.cwd() });\n if (error) return undefined;\n\n if (config.migrations?.path) return config.migrations.path;\n\n const schemaPath = config.schema;\n if (schemaPath) return join(dirname(schemaPath), 'migrations');\n\n return undefined;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Read and concatenate every `migration.sql` under a migrations directory in\n * directory order. Returns undefined if the directory doesn't exist or has no\n * migration files.\n */\nexport const readMigrationFiles = (migrationsPath: string): string | undefined => {\n if (!existsSync(migrationsPath)) return undefined;\n\n const dirs = readdirSync(migrationsPath)\n .filter((directory) => statSync(join(migrationsPath, directory)).isDirectory())\n .sort();\n\n const sqlParts: string[] = [];\n for (const directory of dirs) {\n const sqlPath = join(migrationsPath, directory, 'migration.sql');\n if (existsSync(sqlPath)) {\n sqlParts.push(readFileSync(sqlPath, 'utf8'));\n }\n }\n\n return sqlParts.length > 0 ? sqlParts.join('\\n') : undefined;\n};\n\n/**\n * Resolve schema SQL from {@link PushMigrationsOptions}. Priority:\n * 1. Explicit `sql`\n * 2. Explicit `migrationsPath` — read migration files\n * 3. Auto-discovered migrations via prisma.config.ts\n * 4. Throw — tell the caller to generate migration files\n */\nexport const getMigrationSQL = async (options: PushMigrationsOptions): Promise<string> => {\n if (options.sql) return options.sql;\n\n if (options.migrationsPath) {\n const sql = readMigrationFiles(options.migrationsPath);\n if (sql) return sql;\n throw new Error(\n `No migration.sql files found in ${options.migrationsPath}. Run \\`prisma migrate dev\\` to generate migration files.`,\n );\n }\n\n const migrationsPath = await getMigrationsPath(options.configRoot);\n if (migrationsPath) {\n const sql = readMigrationFiles(migrationsPath);\n if (sql) return sql;\n\n throw new Error(\n `No migration.sql files found in auto-discovered path ${migrationsPath}. ` +\n 'Run `prisma migrate dev` to generate migration files, ' +\n 'or pass pre-generated SQL via the `sql` option.',\n );\n }\n\n if (options.configRoot) {\n throw new Error(\n `prisma.config.ts loaded from configRoot (${options.configRoot}) but no schema ` +\n 'or migrations path could be resolved. Ensure your config specifies a schema path, ' +\n 'or pass pre-generated SQL via the `sql` option.',\n );\n }\n\n throw new Error(\n 'No migration files found and no prisma.config.ts could be loaded. ' +\n 'Run `prisma migrate dev` to generate them, ' +\n 'or pass pre-generated SQL via the `sql` option.',\n );\n};\n\n/**\n * Apply pre-generated SQL to a PGlite instance.\n *\n * Runs the SQL through `pglite.exec(...)` directly, bypassing any bridge\n * pool. No schema engine, no WASM module, no diffing — useful when you\n * already have a `prisma/migrations` directory or pre-generated SQL.\n *\n * Pass the same PGlite instance you handed to {@link PGliteBridge}\n * (i.e. `bridge.pglite`) — or any standalone `PGlite` you own.\n */\nexport const pushMigrations = async (\n pglite: PGlite | PGliteInterface,\n options: PushMigrationsOptions = {},\n): Promise<PushMigrationsResult> => {\n const sql = await getMigrationSQL(options);\n const start = process.hrtime.bigint();\n try {\n await pglite.exec(sql);\n } catch (err) {\n const where = pglite.dataDir ? `PGlite(dataDir=${pglite.dataDir})` : 'in-memory PGlite';\n throw new Error(\n `Failed to apply schema SQL to ${where}. Check your schema or migration files.`,\n { cause: err },\n );\n }\n return { durationMs: Number(process.hrtime.bigint() - start) / 1e6 };\n};\n\n/**\n * Returns `true` when the `_prisma_migrations` table exists and has at\n * least one row with `finished_at IS NOT NULL`. Useful as a \"first run\"\n * guard for persistent dataDirs:\n *\n * ```typescript\n * if (!(await hasMigrations(pglite))) {\n * await pushMigrations(pglite, { migrationsPath: './prisma/migrations' });\n * }\n * ```\n *\n * Awaits `pglite.waitReady` implicitly via `pglite.query(...)`. Detects only\n * Prisma-managed migrations — `pushSchema` (WASM diff) does not populate\n * `_prisma_migrations`, so this returns `false` for adapter-applied schemas.\n */\nexport const hasMigrations = async (pglite: PGlite | PGliteInterface): Promise<boolean> => {\n const { rows } = await pglite.query<{ exists: boolean }>(\n `SELECT to_regclass('public._prisma_migrations') IS NOT NULL AS exists`,\n );\n if (!rows[0]?.exists) return false;\n\n const { rows: applied } = await pglite.query<{ count: number }>(\n `SELECT count(*)::int AS count FROM _prisma_migrations WHERE finished_at IS NOT NULL`,\n );\n return (applied[0] as { count: number }).count > 0;\n};\n\n/**\n * Returns `true` when the `public` schema contains at least one user table.\n * Broader sibling of {@link hasMigrations} — fires for any DDL, regardless of\n * whether it came from {@link pushMigrations}, {@link pushSchema}, or hand-rolled\n * SQL. Use as a \"first run\" guard when you are not using a Prisma migrations\n * directory:\n *\n * ```typescript\n * if (!(await hasSchema(pglite))) {\n * await pushSchema(bridge.adapter, { schema });\n * }\n * ```\n *\n * Awaits `pglite.waitReady` implicitly via `pglite.query(...)`. The internal\n * `_pglite_snapshot` schema used by `bridge.snapshotDb()` is excluded — only\n * the `public` schema is inspected.\n */\nexport const hasSchema = async (pglite: PGlite | PGliteInterface): Promise<boolean> => {\n const { rows } = await pglite.query<{ exists: boolean }>(\n `SELECT EXISTS (\n SELECT 1 FROM information_schema.tables\n WHERE table_schema = 'public' AND table_type = 'BASE TABLE'\n ) AS exists`,\n );\n return rows[0]?.exists === true;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["diagnostics_channel","Duplex","#ownsPglite","PGlite","#pglite","#hasSnapshot","#snapshotSchemaExists","#getTables","#withReplicationRoleReplica","#pool","#stats","#snapshot","#leakToken","#ownsPglite","PGlite","PrismaPg","#assertPoolIdle","#closing","#options","#server","#sessionLock","#sockets","#ownsPglite","PGlite","net","#onConnection","#connectionString","nodePath","#initDuplex"],"sources":["../src/telemetry/diagnostics.ts","../src/utils/time.ts","../src/utils/wait-pglite-ready.ts","../src/duplex/constants.ts","../src/duplex/row-description.ts","../src/duplex/backend-framer.ts","../src/duplex/frontend-buffer.ts","../src/duplex/index.ts","../src/utils/resolve-sync-to-fs.ts","../src/utils/session-lock.ts","../src/pool/fast-array-parsers.ts","../src/pool/pg-bridge-client.ts","../src/pool/index.ts","../src/telemetry/bridge-stats.ts","../src/utils/quote-ident.ts","../src/pglite-bridge/snapshot-manager.ts","../src/pglite-bridge/index.ts","../src/pglite-server/index.ts","../src/schema/pg18-not-null.ts","../src/schema/index.ts","../src/schema/migrations.ts"],"sourcesContent":["/**\n * Public `node:diagnostics_channel` surface.\n *\n * The bridge publishes per-query and per-lock-wait events to named\n * channels. External consumers (OpenTelemetry, APM tools, custom\n * loggers) can subscribe without touching the library API. Built-in\n * bridge stats are updated directly from the bridge and are\n * independent of these public channels.\n *\n * Publication is gated by `channel.hasSubscribers`, so the hot path\n * pays no cost when nobody is listening. Subscribing opts the consumer\n * in to the timing/publication overhead.\n *\n * Filter on `bridgeId` to distinguish events from different bridges\n * in the same process — read `bridge.bridgeId` (on `PGliteBridge`) or\n * `pool.bridgeId` (on `PgBridgePool`).\n */\nimport diagnostics_channel from 'node:diagnostics_channel';\n\n/**\n * `node:diagnostics_channel` name for per-query events. Subscribers receive\n * a {@link QueryEvent} each time the bridge completes a query.\n */\nexport const QUERY_CHANNEL = 'prisma-pglite-bridge:query';\n\n/**\n * `node:diagnostics_channel` name for per-acquisition session-lock wait\n * events. Subscribers receive a {@link LockWaitEvent} after each\n * acquisition completes.\n */\nexport const LOCK_WAIT_CHANNEL = 'prisma-pglite-bridge:lock-wait';\n\n/** Payload published to {@link QUERY_CHANNEL}. */\nexport interface QueryEvent {\n /** Bridge identity tag — filter on this to isolate one bridge's events. */\n bridgeId: symbol;\n /** Wall-clock duration of the query in milliseconds. */\n durationMs: number;\n /** `false` when the query rejected (protocol or SQL error). */\n succeeded: boolean;\n}\n\n/** Payload published to {@link LOCK_WAIT_CHANNEL}. */\nexport interface LockWaitEvent {\n /** Bridge identity tag — filter on this to isolate one bridge's events. */\n bridgeId: symbol;\n /** Time spent waiting to acquire the session lock, in milliseconds. */\n durationMs: number;\n}\n\nexport const queryChannel: diagnostics_channel.Channel = diagnostics_channel.channel(QUERY_CHANNEL);\nexport const lockWaitChannel: diagnostics_channel.Channel =\n diagnostics_channel.channel(LOCK_WAIT_CHANNEL);\n","/** Convert a nanosecond `bigint` (as returned by `process.hrtime.bigint()`) to milliseconds. */\nexport const nsToMs = (ns: bigint): number => Number(ns) / 1_000_000;\n","import type { PGlite, PGliteInterface } from '@electric-sql/pglite';\n\nconst withTimeout = async <T>(promise: Promise<T>, ms: number): Promise<T> => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => {\n reject(new Error(`Operation timed out after ${ms}ms`));\n }, ms);\n });\n\n return Promise.race([promise, timeout]).finally(() => {\n clearTimeout(timer);\n });\n};\n\nexport const waitPGliteReady = async (\n pglite: PGlite | PGliteInterface,\n ms: number = Number.POSITIVE_INFINITY,\n): Promise<void> => {\n if (pglite.ready) return;\n if (pglite.closed) throw new Error('PGlite instance closed');\n\n try {\n await (Number.isFinite(ms) ? withTimeout(pglite.waitReady, ms) : pglite.waitReady);\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n throw new Error(`PGlite instance not ready: ${msg}`);\n }\n};\n","// Frontend message types\nconst PARSE = 0x50; // P\nconst BIND = 0x42; // B\nconst DESCRIBE = 0x44; // D\nconst EXECUTE = 0x45; // E\nconst CLOSE = 0x43; // C\nconst FLUSH = 0x48; // H\nexport const SYNC = 0x53; // S (frontend)\nexport const TERMINATE = 0x58; // X\n\n// Backend message types\nexport const READY_FOR_QUERY = 0x5a; // Z — 6 bytes: Z + length(5) + status\nexport const ERROR_RESPONSE = 0x45; // E — signals in-band SQL error (not a JS throw)\nexport const ROW_DESCRIPTION = 0x54; // T — column metadata; rewritten to widen oid 18\n\n// ReadyForQuery transaction status bytes (RFC: pg wire protocol §53.7)\nexport const RFQ_STATUS_IDLE = 0x49; // 'I' — no transaction\nexport const RFQ_STATUS_IN_TRANSACTION = 0x54; // 'T' — in transaction block\nexport const RFQ_STATUS_FAILED = 0x45; // 'E' — failed transaction block\n\n// pg_catalog system columns (pg_constraint.contype, pg_type.typtype, pg_class.relkind,\n// etc.) report type oid 18 (\"char\", 1-byte). @prisma/adapter-pg's fieldToColumnType\n// has no case for oid 18 and throws UnsupportedNativeDataType. The bytes on the wire\n// for these single-ASCII-char values decode identically as text, so we widen oid 18\n// to oid 25 (text) in RowDescription frames before pg.Client sees them — but only\n// when the field originates from a pg_catalog relation (tableOID below\n// FirstNormalObjectId). User-defined \"char\" columns are left untouched so the\n// upstream type-mapping gap surfaces instead of being silently papered over with a\n// possibly-lossy text decode. Remove this rewrite once @prisma/adapter-pg gains a\n// fieldToColumnType case for oid 18.\nexport const PG_TYPE_OID_CHAR = 18;\nexport const PG_TYPE_OID_TEXT = 25;\n// Mirrors PostgreSQL's FirstNormalObjectId (src/include/access/transam.h): every\n// system catalog relation has an OID below this; user-created objects start at it.\nexport const PG_FIRST_USER_OID = 16384;\n\n// Extended Query Protocol message types — must be batched until Sync\nexport const EQP_MESSAGES: ReadonlySet<number> = new Set([\n PARSE,\n BIND,\n DESCRIBE,\n EXECUTE,\n CLOSE,\n FLUSH,\n]);\n\n/**\n * Upper bound on a single backend message length declared in its 4-byte\n * header. PostgreSQL's own wire protocol maxes out around 1 GiB per\n * message; anything larger indicates a corrupted or hostile stream and\n * must not be allocated against.\n */\nexport const MAX_BACKEND_MESSAGE_LENGTH = 1_073_741_824;\n","import { PG_FIRST_USER_OID, PG_TYPE_OID_CHAR, PG_TYPE_OID_TEXT } from './constants.ts';\n\nconst readUInt32BE = (buf: Uint8Array, offset: number): number => {\n /* c8 ignore start — callers guard fixed-width reads */\n const b1 = buf[offset] ?? 0;\n const b2 = buf[offset + 1] ?? 0;\n const b3 = buf[offset + 2] ?? 0;\n const b4 = buf[offset + 3] ?? 0;\n /* c8 ignore stop */\n return ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) >>> 0;\n};\n\nconst readUInt16BE = (buf: Uint8Array, offset: number): number => {\n /* c8 ignore start — callers guard fixed-width reads */\n const b1 = buf[offset] ?? 0;\n const b2 = buf[offset + 1] ?? 0;\n /* c8 ignore stop */\n return (b1 << 8) | b2;\n};\n\nexport const rowDescriptionNeedsRewrite = (\n buf: Uint8Array,\n start: number = 0,\n end: number = buf.length,\n): boolean => {\n if (end - start < 7) return false;\n\n const fieldCount = readUInt16BE(buf, start + 5);\n let p = start + 7;\n for (let i = 0; i < fieldCount; i++) {\n while (p < end && buf[p] !== 0) p++;\n p++; // NUL terminator\n /* c8 ignore next — defense-in-depth: framer caller passes a complete frame */\n if (p + 18 > end) return false;\n const tableOID = readUInt32BE(buf, p);\n p += 4 + 2; // tableOID, columnAttr\n const oid = readUInt32BE(buf, p);\n if (oid === PG_TYPE_OID_CHAR && tableOID !== 0 && tableOID < PG_FIRST_USER_OID) {\n return true;\n }\n p += 4 + 2 + 4 + 2; // dataTypeOID, dataTypeSize, typeModifier, formatCode\n }\n\n return false;\n};\n\n/**\n * Widens any field whose dataTypeOID is 18 (\"char\") to oid 25 (text) — but\n * only when the field originates from a pg_catalog relation (tableOID is\n * non-zero and below FirstNormalObjectId). Single-ASCII-byte payloads from\n * pg_catalog views (pg_constraint.contype, pg_type.typtype, etc.) decode\n * identically as text, so the row data needs no transformation. Fields with\n * tableOID === 0 (computed expressions) and user-table fields are left\n * untouched, since arbitrary bytes in user \"char\" data can't safely be\n * relabelled as text. Frame length is unchanged because all rewrites are\n * fixed-size in place.\n */\nexport const rewriteRowDescriptionInPlace = (buf: Buffer): void => {\n if (!rowDescriptionNeedsRewrite(buf)) return;\n\n const fieldCount = buf.readInt16BE(5);\n let p = 7;\n for (let i = 0; i < fieldCount; i++) {\n while (p < buf.length && buf[p] !== 0) p++;\n p++; // NUL terminator\n // Per-field fixed suffix is 18 bytes: tableOID(4) + columnAttr(2) +\n // dataTypeOID(4) + dataTypeSize(2) + typeModifier(4) + formatCode(2).\n // Bail if the frame is truncated rather than throwing RangeError mid-loop.\n /* c8 ignore next — defense-in-depth: framer caller passes a complete frame */\n if (p + 18 > buf.length) return;\n const tableOID = buf.readUInt32BE(p);\n p += 4 + 2; // tableOID, columnAttr\n const oid = buf.readUInt32BE(p);\n if (oid === PG_TYPE_OID_CHAR && tableOID !== 0 && tableOID < PG_FIRST_USER_OID) {\n buf.writeUInt32BE(PG_TYPE_OID_TEXT, p);\n buf.writeInt16BE(-1, p + 4);\n }\n p += 4 + 2 + 4 + 2; // dataTypeOID, dataTypeSize, typeModifier, formatCode\n }\n};\n","import {\n ERROR_RESPONSE,\n MAX_BACKEND_MESSAGE_LENGTH,\n READY_FOR_QUERY,\n ROW_DESCRIPTION,\n} from './constants.ts';\nimport { rewriteRowDescriptionInPlace, rowDescriptionNeedsRewrite } from './row-description.ts';\n\ntype BackendMessageFramerOptions = {\n suppressIntermediateReadyForQuery?: boolean;\n /**\n * Widen system-catalog \"char\" columns (OID 18) to text (OID 25) in\n * RowDescription frames. Required for `@prisma/adapter-pg`, which rejects\n * OID 18 — but native clients (Prisma CLI engine, psql) need the real OID;\n * with the rewrite, the engine's constraint introspection misreads\n * `pg_constraint.contype` values as text. Defaults to true (bridge path).\n */\n rewriteSystemCatalogCharOids?: boolean;\n onChunk: (chunk: Uint8Array) => void;\n onErrorResponse?: () => void;\n onReadyForQuery?: (status: number) => void;\n};\n\n/**\n * Streams backend protocol messages without materializing whole responses.\n *\n * Non-RFQ payload bytes are forwarded as they arrive. ReadyForQuery frames are\n * tracked only once complete; when suppression is enabled, only the final RFQ\n * is emitted.\n *\n * @internal — exported for testing only\n */\nexport class BackendMessageFramer {\n private readonly suppressIntermediateReadyForQuery: boolean;\n private readonly rewriteSystemCatalogCharOids: boolean;\n private readonly onChunk: (chunk: Uint8Array) => void;\n private readonly onErrorResponse?: () => void;\n private readonly onReadyForQuery?: (status: number) => void;\n private readonly headerScratch = new Uint8Array(4);\n private readonly heldRfq = new Uint8Array(6);\n private messageType?: number;\n private headerBytesRead = 0;\n private payloadBytesRemaining = 0;\n private rfqBytesRead = 0;\n /** Slow-path RowDescription accumulator. Set once the T-frame header is decoded\n * and a multi-chunk payload starts arriving; cleared after rewrite + emit. */\n private rowDescBuffer?: Buffer;\n private rowDescOffset = 0;\n\n constructor(options: BackendMessageFramerOptions) {\n this.suppressIntermediateReadyForQuery = options.suppressIntermediateReadyForQuery ?? false;\n this.rewriteSystemCatalogCharOids = options.rewriteSystemCatalogCharOids ?? true;\n this.onChunk = options.onChunk;\n this.onErrorResponse = options.onErrorResponse;\n this.onReadyForQuery = options.onReadyForQuery;\n }\n\n write(chunk: Uint8Array): void {\n if (chunk.length === 0) return;\n\n let offset = 0;\n let passthroughStart = -1;\n const flushPassthrough = (end: number): void => {\n if (passthroughStart >= 0 && end > passthroughStart) {\n this.emitChunkSlice(chunk, passthroughStart, end);\n passthroughStart = -1;\n }\n };\n while (offset < chunk.length) {\n if (this.messageType === undefined) {\n // Fast path: if type + 4-byte header + full payload are all in this\n // chunk, emit the whole message as one slice. Avoids the per-message\n // prefix allocation + two downstream pushes that the byte-state-machine\n // path below performs. Falls through to the slow path when the message\n // spans chunks.\n const available = chunk.length - offset;\n if (available >= 5) {\n /* c8 ignore start — bounds guaranteed by `available >= 5` */\n const msgType = chunk[offset] ?? 0;\n const b1 = chunk[offset + 1] ?? 0;\n const b2 = chunk[offset + 2] ?? 0;\n const b3 = chunk[offset + 3] ?? 0;\n const b4 = chunk[offset + 4] ?? 0;\n /* c8 ignore stop */\n const messageLength = ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) >>> 0;\n if (messageLength < 4) {\n throw new Error(`Malformed backend message length: ${messageLength}`);\n }\n if (messageLength > MAX_BACKEND_MESSAGE_LENGTH) {\n throw new Error(\n `Backend message length ${messageLength} exceeds sanity cap ${MAX_BACKEND_MESSAGE_LENGTH}`,\n );\n }\n const totalLen = 1 + messageLength;\n if (available >= totalLen) {\n if (msgType === ERROR_RESPONSE) {\n this.onErrorResponse?.();\n }\n if (msgType === READY_FOR_QUERY && messageLength === 5) {\n flushPassthrough(offset);\n this.dropStaleHeldReadyForQuery();\n /* c8 ignore next — messageLength === 5 for RFQ; payload is 1 byte */\n const status = chunk[offset + 5] ?? 0;\n this.heldRfq[0] = msgType;\n this.heldRfq[1] = b1;\n this.heldRfq[2] = b2;\n this.heldRfq[3] = b3;\n this.heldRfq[4] = b4;\n this.heldRfq[5] = status;\n this.rfqBytesRead = 6;\n this.onReadyForQuery?.(status);\n if (!this.suppressIntermediateReadyForQuery) {\n this.emitReadyForQuery();\n this.rfqBytesRead = 0;\n }\n } else if (\n msgType === ROW_DESCRIPTION &&\n this.rewriteSystemCatalogCharOids &&\n rowDescriptionNeedsRewrite(chunk, offset, offset + totalLen)\n ) {\n flushPassthrough(offset);\n this.dropStaleHeldReadyForQuery();\n this.emitRewrittenRowDescription(\n Buffer.from(chunk.subarray(offset, offset + totalLen)),\n );\n } else {\n this.dropStaleHeldReadyForQuery();\n if (passthroughStart < 0) {\n passthroughStart = offset;\n }\n }\n offset += totalLen;\n continue;\n }\n }\n\n flushPassthrough(offset);\n this.dropStaleHeldReadyForQuery();\n /* c8 ignore next — offset < chunk.length guaranteed by outer while */\n this.messageType = chunk[offset] ?? 0;\n this.headerBytesRead = 0;\n this.payloadBytesRemaining = 0;\n this.rfqBytesRead = this.messageType === READY_FOR_QUERY ? 1 : 0;\n if (this.rfqBytesRead === 1) {\n this.heldRfq[0] = this.messageType;\n }\n offset++;\n continue;\n }\n\n if (this.headerBytesRead < 4) {\n const bytesToCopy = Math.min(4 - this.headerBytesRead, chunk.length - offset);\n const headerChunk = chunk.subarray(offset, offset + bytesToCopy);\n this.headerScratch.set(headerChunk, this.headerBytesRead);\n if (this.messageType === READY_FOR_QUERY) {\n this.heldRfq.set(headerChunk, this.rfqBytesRead);\n this.rfqBytesRead += bytesToCopy;\n }\n this.headerBytesRead += bytesToCopy;\n offset += bytesToCopy;\n if (this.headerBytesRead < 4) continue;\n\n /* c8 ignore start — header bytes all populated before read */\n const b1 = this.headerScratch[0] ?? 0;\n const b2 = this.headerScratch[1] ?? 0;\n const b3 = this.headerScratch[2] ?? 0;\n const b4 = this.headerScratch[3] ?? 0;\n /* c8 ignore stop */\n const messageLength = ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) >>> 0;\n if (messageLength < 4) {\n throw new Error(`Malformed backend message length: ${messageLength}`);\n }\n if (messageLength > MAX_BACKEND_MESSAGE_LENGTH) {\n throw new Error(\n `Backend message length ${messageLength} exceeds sanity cap ${MAX_BACKEND_MESSAGE_LENGTH}`,\n );\n }\n\n this.payloadBytesRemaining = messageLength - 4;\n\n if (this.messageType === ERROR_RESPONSE) {\n this.onErrorResponse?.();\n }\n\n if (this.isReadyForQueryFrame()) {\n continue;\n }\n\n this.dropHeldReadyForQuery();\n if (this.messageType === ROW_DESCRIPTION && this.rewriteSystemCatalogCharOids) {\n // Valid RowDescription always carries at least a 2-byte fieldCount,\n // so payloadBytesRemaining is > 0 here — no zero-payload finalize needed.\n this.rowDescBuffer = Buffer.alloc(5 + this.payloadBytesRemaining);\n this.rowDescBuffer[0] = ROW_DESCRIPTION;\n this.rowDescBuffer.set(this.headerScratch, 1);\n this.rowDescOffset = 5;\n continue;\n }\n this.emitPrefix();\n if (this.payloadBytesRemaining === 0) {\n this.finishMessage();\n }\n continue;\n }\n\n if (this.isReadyForQueryFrame()) {\n const bytesToCopy = Math.min(this.payloadBytesRemaining, chunk.length - offset);\n const payloadChunk = chunk.subarray(offset, offset + bytesToCopy);\n this.heldRfq.set(payloadChunk, this.rfqBytesRead);\n this.rfqBytesRead += bytesToCopy;\n this.payloadBytesRemaining -= bytesToCopy;\n offset += bytesToCopy;\n /* c8 ignore next 3 — bytesToCopy ≥ 1 consumes the 1-byte RFQ payload */\n if (this.payloadBytesRemaining === 0) {\n this.finishReadyForQuery();\n }\n continue;\n }\n\n const bytesToEmit = Math.min(this.payloadBytesRemaining, chunk.length - offset);\n /* c8 ignore next — bytesToEmit always ≥ 1 when reached */\n if (bytesToEmit > 0) {\n if (this.rowDescBuffer !== undefined) {\n this.rowDescBuffer.set(chunk.subarray(offset, offset + bytesToEmit), this.rowDescOffset);\n this.rowDescOffset += bytesToEmit;\n } else {\n this.emitChunkSlice(chunk, offset, offset + bytesToEmit);\n }\n this.payloadBytesRemaining -= bytesToEmit;\n offset += bytesToEmit;\n }\n if (this.payloadBytesRemaining === 0) {\n if (this.rowDescBuffer !== undefined) {\n const buf = this.rowDescBuffer;\n this.rowDescBuffer = undefined;\n this.emitRewrittenRowDescription(buf);\n }\n this.finishMessage();\n }\n }\n\n flushPassthrough(offset);\n }\n\n flush(options?: { dropHeldReadyForQuery?: boolean }): void {\n if (options?.dropHeldReadyForQuery === true) {\n this.dropHeldReadyForQuery();\n } else if (this.suppressIntermediateReadyForQuery && this.rfqBytesRead === 6) {\n this.emitReadyForQuery();\n this.rfqBytesRead = 0;\n }\n }\n\n private isReadyForQueryFrame(): boolean {\n return this.messageType === READY_FOR_QUERY && this.payloadBytesRemaining === 1;\n }\n\n private finishReadyForQuery(): void {\n const status = this.heldRfq[5];\n /* c8 ignore next — heldRfq[5] always populated before finishReadyForQuery */\n if (status !== undefined) {\n this.onReadyForQuery?.(status);\n }\n\n if (!this.suppressIntermediateReadyForQuery) {\n this.emitReadyForQuery();\n }\n\n this.finishMessage();\n }\n\n private emitReadyForQuery(): void {\n this.onChunk(this.heldRfq.slice(0, 6));\n }\n\n private dropHeldReadyForQuery(): void {\n this.rfqBytesRead = 0;\n }\n\n /** Drop a complete held RFQ once a following frame proves it was an\n * intermediate one. No-op unless suppressing and a full RFQ is buffered. */\n private dropStaleHeldReadyForQuery(): void {\n if (this.suppressIntermediateReadyForQuery && this.rfqBytesRead === 6) {\n this.dropHeldReadyForQuery();\n }\n }\n\n private emitPrefix(): void {\n const prefix = new Uint8Array(5);\n /* c8 ignore next — messageType always set when emitPrefix is called */\n prefix[0] = this.messageType ?? 0;\n prefix.set(this.headerScratch, 1);\n this.onChunk(prefix);\n }\n\n private emitRewrittenRowDescription(buf: Buffer): void {\n rewriteRowDescriptionInPlace(buf);\n this.onChunk(buf);\n }\n\n private emitChunkSlice(chunk: Uint8Array, start: number, end: number): void {\n const length = end - start;\n /* c8 ignore next — callers pass end > start */\n if (length <= 0) return;\n\n // PGlite already hands us standalone Uint8Array chunks copied out of the\n // WASM heap, so when this chunk owns its full backing store we can hand pg\n // zero-copy Buffer views for arbitrary subranges. We still copy when the\n // chunk is a view into a larger backing buffer (to avoid pinning unrelated\n // trailing bytes) or when the backing store is shared (to prevent the WASM\n // runtime from mutating bytes pg is still consuming).\n if (\n chunk.byteOffset === 0 &&\n chunk.byteLength === chunk.buffer.byteLength &&\n !(chunk.buffer instanceof SharedArrayBuffer)\n ) {\n this.onChunk(Buffer.from(chunk.buffer, start, length));\n return;\n }\n\n const exact = Buffer.from(chunk.subarray(start, end));\n this.onChunk(exact);\n }\n\n private finishMessage(): void {\n this.messageType = undefined;\n this.headerBytesRead = 0;\n this.payloadBytesRemaining = 0;\n if (!this.suppressIntermediateReadyForQuery) {\n this.rfqBytesRead = 0;\n }\n }\n}\n","/**\n * Frontend chunk queue that frames messages without repeatedly compacting\n * the full buffered input.\n *\n * @internal — exported for testing only\n */\nexport class FrontendMessageBuffer {\n private chunks: Uint8Array[] = [];\n private headIndex = 0;\n private headOffset = 0;\n private totalLength = 0;\n\n get length(): number {\n return this.totalLength;\n }\n\n push(chunk: Uint8Array): void {\n if (chunk.length === 0) return;\n this.chunks.push(chunk);\n this.totalLength += chunk.length;\n }\n\n clear(): void {\n this.chunks = [];\n this.headIndex = 0;\n this.headOffset = 0;\n this.totalLength = 0;\n }\n\n readInt32BE(offset: number): number | undefined {\n if (offset < 0 || offset + 4 > this.totalLength) return undefined;\n\n const head = this.chunks[this.headIndex];\n /* c8 ignore next — head defined when totalLength > 0 */\n if (head !== undefined) {\n const start = this.headOffset + offset;\n if (start + 4 <= head.length) {\n /* c8 ignore start — bounds guaranteed by `start + 4 <= head.length` */\n const b1 = head[start] ?? 0;\n const b2 = head[start + 1] ?? 0;\n const b3 = head[start + 2] ?? 0;\n const b4 = head[start + 3] ?? 0;\n /* c8 ignore stop */\n return ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) >>> 0;\n }\n }\n\n let remaining = this.headOffset + offset;\n const bytes = new Uint8Array(4);\n let writeOffset = 0;\n\n for (let i = this.headIndex; i < this.chunks.length && writeOffset < 4; i++) {\n const chunk = this.chunks[i];\n /* c8 ignore next — chunks between headIndex and end are always populated */\n if (chunk === undefined) return undefined;\n if (remaining >= chunk.length) {\n remaining -= chunk.length;\n continue;\n }\n\n const bytesToCopy = Math.min(4 - writeOffset, chunk.length - remaining);\n bytes.set(chunk.subarray(remaining, remaining + bytesToCopy), writeOffset);\n writeOffset += bytesToCopy;\n remaining = 0;\n }\n\n /* c8 ignore start — bytes is a fixed 4-byte Uint8Array */\n const b1 = bytes[0] ?? 0;\n const b2 = bytes[1] ?? 0;\n const b3 = bytes[2] ?? 0;\n const b4 = bytes[3] ?? 0;\n /* c8 ignore stop */\n return ((b1 << 24) | (b2 << 16) | (b3 << 8) | b4) >>> 0;\n }\n\n consume(length: number): Uint8Array {\n if (length < 0 || length > this.totalLength) {\n throw new Error(`Cannot consume ${length} bytes from ${this.totalLength}-byte buffer`);\n }\n if (length === 0) return new Uint8Array(0);\n\n const head = this.chunks[this.headIndex];\n /* c8 ignore next — head defined when totalLength > 0 */\n if (head !== undefined) {\n const headRemaining = head.length - this.headOffset;\n if (headRemaining >= length) {\n const slice = head.subarray(this.headOffset, this.headOffset + length);\n this.headOffset += length;\n this.totalLength -= length;\n if (this.headOffset === head.length) {\n this.headIndex++;\n this.headOffset = 0;\n this.compactChunks();\n }\n return slice;\n }\n }\n\n const result = new Uint8Array(length);\n let writeOffset = 0;\n let remaining = length;\n\n while (remaining > 0) {\n const chunk = this.chunks[this.headIndex];\n /* c8 ignore next 3 — guarded by line-116 length check */\n if (chunk === undefined) {\n throw new Error('FrontendMessageBuffer underflow');\n }\n const available = chunk.length - this.headOffset;\n const bytesToCopy = Math.min(remaining, available);\n result.set(chunk.subarray(this.headOffset, this.headOffset + bytesToCopy), writeOffset);\n writeOffset += bytesToCopy;\n remaining -= bytesToCopy;\n this.headOffset += bytesToCopy;\n this.totalLength -= bytesToCopy;\n if (this.headOffset === chunk.length) {\n this.headIndex++;\n this.headOffset = 0;\n this.compactChunks();\n }\n }\n\n return result;\n }\n\n private compactChunks(): void {\n if (this.headIndex === this.chunks.length) {\n this.chunks = [];\n this.headIndex = 0;\n return;\n }\n\n if (this.headIndex >= 32 && this.headIndex * 2 >= this.chunks.length) {\n this.chunks = this.chunks.slice(this.headIndex);\n this.headIndex = 0;\n }\n }\n}\n","/**\n * PGlite duplex stream.\n *\n * A Duplex stream that replaces the TCP socket in pg.Client, routing\n * wire protocol messages directly to an in-process PGlite instance.\n *\n * pg.Client writes wire protocol bytes → duplex frames messages →\n * PGlite processes via execProtocolRawStream → duplex pushes responses back.\n *\n * Extended Query Protocol pipelines (Parse→Bind→Describe→Execute→Sync) are\n * concatenated into a single buffer and sent as one atomic execProtocolRawStream\n * call within one runExclusive. This prevents portal interleaving between\n * concurrent streams AND reduces async overhead (1 WASM call instead of 5).\n *\n * The response from a batched pipeline contains spurious ReadyForQuery messages\n * after each sub-message (PGlite's single-user mode). These are stripped,\n * keeping only the final ReadyForQuery after Sync.\n */\nimport { Duplex } from 'node:stream';\n\nimport type { PGlite, PGliteInterface } from '@electric-sql/pglite';\n\nimport type { TelemetrySink } from '../telemetry/bridge-stats.ts';\nimport { lockWaitChannel, queryChannel } from '../telemetry/diagnostics.ts';\nimport type { SessionLock } from '../utils/session-lock.ts';\nimport { nsToMs } from '../utils/time.ts';\nimport { waitPGliteReady } from '../utils/wait-pglite-ready.ts';\n\nimport { BackendMessageFramer } from './backend-framer.ts';\nimport {\n EQP_MESSAGES,\n RFQ_STATUS_FAILED,\n RFQ_STATUS_IDLE,\n RFQ_STATUS_IN_TRANSACTION,\n SYNC,\n TERMINATE,\n} from './constants.ts';\nimport { FrontendMessageBuffer } from './frontend-buffer.ts';\n\n// PGlite's execProtocolRawSync short-circuits X/Terminate before entering\n// the Postgres backend loop, while execProtocolStream still clears its\n// parsed-message array. Verified through PGlite 0.4.6 and 0.5.1;\n// re-verify on upgrades.\nconst TERMINATE_MESSAGE = new Uint8Array([TERMINATE, 0x00, 0x00, 0x00, 0x04]);\n// Keep cleanup infrequent on small queries, but bounded for large read bursts;\n// these thresholds came from the adapter comparison memory profile.\nconst PROTOCOL_CLEANUP_RAW_BYTES = 8 * 1024 * 1024;\nconst PROTOCOL_CLEANUP_CALLS = 32;\n\ntype PGliteProtocolCleanupCapable = PGliteInterface & {\n execProtocolStream?: PGlite['execProtocolStream'];\n};\n\nexport interface PGliteDuplexOptions {\n /**\n * Shared lock that serialises access to the PGlite runtime across\n * multiple duplex streams. Omit for a standalone duplex.\n */\n sessionLock?: SessionLock;\n /**\n * Identity tag published with diagnostics-channel events. Omit to\n * disable channel publication for this duplex.\n */\n bridgeId?: symbol;\n /**\n * Internal sink used by `PGliteBridge` for built-in stats. Not a public\n * extension point — subscribe via `node:diagnostics_channel` instead.\n */\n telemetry?: TelemetrySink;\n /**\n * Maximum milliseconds to wait for the PGlite instance to become ready\n * before each bridge operation. Defaults to no timeout (waits indefinitely).\n */\n timeout?: number;\n /**\n * Whether each wire-protocol call should force a filesystem sync before\n * returning. Disable only when higher throughput / lower RSS is worth\n * weaker durability. Default `true`.\n */\n syncToFs?: boolean;\n /**\n * Widen system-catalog \"char\" columns (OID 18) to text (OID 25) in\n * RowDescription frames. Required for `@prisma/adapter-pg`, which rejects\n * OID 18. Native clients (Prisma CLI engine, psql) need the real OID —\n * `PGliteServer` disables this. Default `true`.\n */\n rewriteSystemCatalogCharOids?: boolean;\n}\n\n/**\n * Duplex stream that bridges `pg.Client` to an in-process PGlite instance.\n *\n * **Most users should reach for {@link PGliteBridge} instead** — it\n * bundles `PGliteDuplex` with a pool, telemetry, snapshot/reset\n * lifecycle, and the Prisma adapter. Use `PGliteDuplex` directly only\n * when wiring a custom `pg.Client` setup that's outside the\n * `PgBridgePool` model.\n *\n * Replaces the TCP socket in `pg.Client` via the `stream` option. Speaks\n * PostgreSQL wire protocol directly to PGlite — no TCP, no serialization\n * overhead beyond what the wire protocol requires. When using multiple\n * duplexes against one PGlite, pass a shared {@link SessionLock} so\n * cross-duplex transactions don't interleave.\n *\n * ```typescript\n * const client = new pg.Client({\n * stream: () => new PGliteDuplex(pglite),\n * });\n * ```\n */\nexport class PGliteDuplex extends Duplex {\n private readonly pglite: PGliteProtocolCleanupCapable;\n private readonly sessionLock?: SessionLock;\n private readonly bridgeId?: symbol;\n private readonly telemetry?: TelemetrySink;\n private readonly syncToFs: boolean;\n private readonly rewriteSystemCatalogCharOids: boolean;\n private readonly timeout?: number;\n private readonly duplexId: symbol;\n /** Incoming bytes framed directly from a queued chunk buffer */\n private readonly input = new FrontendMessageBuffer();\n private phase: 'pre_startup' | 'ready' = 'pre_startup';\n private draining = false;\n private tornDown = false;\n /** Callbacks waiting for drain to process their data */\n private drainQueue: Array<(error?: Error | null) => void> = [];\n /** Buffered EQP messages awaiting Sync */\n private pipeline: Uint8Array[] = [];\n /** Last RFQ status byte observed in a backend response — independent of\n * SessionLock so rollback works for max=1 pools (no lock) and survives\n * the in-flight BEGIN race (lock owner is set only on RFQ arrival). */\n private lastSeenRfqStatus?: number;\n /** In-flight `op()` from the runExclusive callback, if any. Set only once\n * the callback has actually entered `op()` — a queued callback that fires\n * after `tornDown` returns immediately and is never registered here, so\n * teardown does not block on its slot. Rollback awaits this to catch the\n * RFQ from a BEGIN that started before destroy. */\n private currentPGliteCall?: Promise<unknown>;\n /** Memoized rollback so concurrent teardown paths (e.g., `_final` then\n * `_destroy`) don't issue duplicate `ROLLBACK` statements. */\n private rollbackPromise?: Promise<void>;\n private pendingProtocolCleanupBytes = 0;\n private pendingProtocolCleanupCalls = 0;\n private protocolCleanupUnsupported = false;\n /** Resolves once the stream has fully torn down (post-`_final` rollback,\n * post-`_destroy`). Single-shot, mirroring the `'close'` event. */\n readonly onClose: Promise<void>;\n\n /**\n * @param pglite PGlite instance to bridge to. The caller owns its lifecycle.\n * @param options See {@link PGliteDuplexOptions}.\n */\n constructor(pglite: PGlite | PGliteInterface, options: PGliteDuplexOptions = {}) {\n super();\n this.pglite = pglite;\n this.sessionLock = options.sessionLock;\n this.bridgeId = options.bridgeId;\n this.telemetry = options.telemetry;\n this.timeout = options.timeout;\n this.syncToFs = options.syncToFs ?? true;\n this.rewriteSystemCatalogCharOids = options.rewriteSystemCatalogCharOids ?? true;\n\n this.duplexId = Symbol('duplex');\n this.onClose = new Promise<void>((resolve) => this.once('close', () => resolve()));\n }\n\n // ── Socket compatibility (called by pg's Connection) ──\n\n connect(): this {\n setImmediate(() => this.emit('connect'));\n return this;\n }\n\n setKeepAlive(): this {\n return this;\n }\n\n setNoDelay(): this {\n return this;\n }\n\n setTimeout(): this {\n return this;\n }\n\n ref(): this {\n return this;\n }\n\n unref(): this {\n return this;\n }\n\n // ── Duplex implementation ──\n\n override _read(): void {\n // Data is pushed proactively when PGlite responses arrive\n }\n\n override _write(\n chunk: Buffer,\n _encoding: BufferEncoding,\n callback: (error?: Error | null) => void,\n ): void {\n this.input.push(chunk);\n this.enqueue(callback);\n }\n\n /** Handles corked batches — pg.Client corks during prepared queries (P+B+D+E+S) */\n override _writev(\n chunks: Array<{ chunk: Buffer; encoding: BufferEncoding }>,\n callback: (error?: Error | null) => void,\n ): void {\n for (const { chunk } of chunks) {\n this.input.push(chunk);\n }\n this.enqueue(callback);\n }\n\n override _final(callback: (error?: Error | null) => void): void {\n // Forced disconnects (raw socket close, no Terminate) reach us here. Roll\n // back any open transaction before releasing the session lock — otherwise\n // the next bridge can run queries while PGlite is still in 'T' state and\n // observe rows from the dead transaction.\n this.rollbackIfInTransaction()\n /* v8 ignore start */\n .catch(() => {})\n /* v8 ignore stop */\n .then(() => {\n this.sessionLock?.release(this.duplexId);\n this.push(null);\n callback();\n });\n }\n\n override _destroy(error: Error | null, callback: (error?: Error | null) => void): void {\n this.tornDown = true;\n this.pipeline.length = 0;\n this.input.clear();\n\n // Fail queued write callbacks promptly — rollback below may await an\n // in-flight pglite call, and pg.Client must not see those writes resolve\n // successfully when the bridge is being destroyed.\n const destroyError = error ?? new Error('Bridge destroyed');\n const callbacks = this.drainQueue;\n this.drainQueue = [];\n for (const cb of callbacks) {\n cb(destroyError);\n }\n\n // Roll back BEFORE cancelling the session lock — cancel() unblocks waiters,\n // so any rollback after that would race with the next bridge's queries.\n this.rollbackIfInTransaction()\n /* v8 ignore start */\n .catch(() => {})\n /* v8 ignore stop */\n .then(() => {\n this.sessionLock?.cancel(this.duplexId, destroyError);\n callback(error);\n });\n }\n\n // ── Drain loop ──\n\n /**\n * Enqueue a write callback and start draining if not already running.\n * The callback is NOT called until drain has processed the data.\n */\n private enqueue(callback: (error?: Error | null) => void): void {\n this.drainQueue.push(callback);\n if (!this.draining) {\n // Errors are propagated through drainQueue callbacks, not through this promise\n this.drain().catch(/* c8 ignore next */ () => {});\n }\n }\n\n /**\n * Process all pending data, looping until no new data arrives.\n * Fires all queued callbacks on completion or error.\n */\n private async drain(): Promise<void> {\n /* c8 ignore next — enqueue only starts drain when !draining */\n if (this.draining) return;\n this.draining = true;\n\n let error: Error | null = null;\n\n try {\n // Loop until no more pending data to process\n while (this.input.length > 0) {\n /* c8 ignore start — race-only: destroy after a drain iteration resolves */\n if (this.tornDown) break;\n const beforeLength = this.input.length;\n\n if (this.phase === 'pre_startup') {\n await this.processPreStartup();\n }\n if (this.phase === 'ready') {\n await this.processMessages();\n }\n /* c8 ignore stop */\n\n // If processMessages couldn't consume anything (incomplete message),\n // stop looping — more data will arrive via _write\n /* c8 ignore next — loop-continue unreachable: no new input arrives mid-drain */\n if (this.input.length === 0 || this.input.length === beforeLength) break;\n }\n } catch (err) {\n error = err instanceof Error ? err : new Error(String(err));\n // Release session lock on error — prevents permanent deadlock if\n // PGlite crashes mid-transaction (other bridges would wait forever)\n this.sessionLock?.release(this.duplexId);\n } finally {\n this.draining = false;\n\n // Fire all waiting callbacks\n const callbacks = this.drainQueue;\n this.drainQueue = [];\n for (const cb of callbacks) {\n cb(error);\n }\n }\n }\n\n // ── Message framing ──\n\n /**\n * Frames and processes the startup message.\n *\n * Format: [4 bytes: total length] [4 bytes: protocol version] [key\\0value\\0 pairs]\n * No type byte — length includes itself.\n */\n private async processPreStartup(): Promise<void> {\n if (this.input.length < 4) return;\n const len = this.input.readInt32BE(0);\n /* c8 ignore next — len === undefined unreachable once length ≥ 4 */\n if (len === undefined || this.input.length < len) return;\n\n const message = this.input.consume(len);\n\n await this.runUntimed(async () => {\n await this.streamProtocol(message, { detectErrors: false, suppressIntermediateRfq: false });\n });\n\n this.phase = 'ready';\n }\n\n /**\n * Wrap a `pglite.runExclusive` call so the in-flight operation is tracked\n * via `currentPGliteCall`. Skip the work entirely if teardown happened\n * before our turn — otherwise a queued BEGIN would still run against PGlite\n * after `_destroy` has already returned, leaking 'T' state into the next\n * bridge's session.\n *\n * `currentPGliteCall` is set only once `op()` actually starts. A callback\n * that fires after `tornDown` returns immediately and was never observed by\n * rollback — there is no RFQ state to wait for, so blocking teardown on the\n * queued no-op would needlessly stall stream close (and any SessionLock\n * waiters behind it).\n */\n private async runUnderRunExclusive(op: () => Promise<void>): Promise<void> {\n await waitPGliteReady(this.pglite, this.timeout);\n\n await this.pglite.runExclusive(async () => {\n if (this.tornDown) return;\n const opPromise = op();\n this.currentPGliteCall = opPromise;\n try {\n await opPromise;\n } finally {\n this.currentPGliteCall = undefined;\n }\n });\n }\n\n /** Acquire the session, then run `op` under runExclusive without timing —\n * the untimed path shared by startup and the no-telemetry query branch. */\n private async runUntimed(op: () => Promise<void>): Promise<void> {\n const session = this.acquireSession();\n if (session) await session;\n await this.runUnderRunExclusive(op);\n }\n\n /**\n * Frames and processes regular wire protocol messages.\n *\n * Extended Query Protocol messages (Parse, Bind, Describe, Execute, Close,\n * Flush) are buffered in `this.pipeline`. When Sync arrives, the entire\n * pipeline is concatenated and sent to PGlite as one atomic\n * execProtocolRawStream call within one runExclusive.\n *\n * SimpleQuery messages are sent directly (they're self-contained).\n */\n private async processMessages(): Promise<void> {\n while (this.input.length >= 5) {\n const msgLen = this.input.readInt32BE(1);\n /* c8 ignore next — input.length ≥ 5 guarantees readable int32 */\n if (msgLen === undefined) break;\n const len = 1 + msgLen;\n if (len < 5 || this.input.length < len) break;\n\n const message = this.input.consume(len);\n /* c8 ignore next — consume(len ≥ 5) returns non-empty */\n const msgType = message[0] ?? 0;\n\n if (msgType === TERMINATE) {\n await this.rollbackIfInTransaction();\n this.sessionLock?.release(this.duplexId);\n this.push(null);\n return;\n }\n\n if (EQP_MESSAGES.has(msgType)) {\n this.pipeline.push(message);\n continue;\n }\n\n if (msgType === SYNC) {\n this.pipeline.push(message);\n await this.flushPipeline();\n continue;\n }\n\n // SimpleQuery or other standalone message\n await this.runWithTiming((detectErrors) =>\n this.streamProtocol(message, { detectErrors, suppressIntermediateRfq: false }),\n );\n }\n }\n\n /**\n * Sends the accumulated EQP pipeline as one atomic operation.\n *\n * All buffered messages are concatenated into a single buffer and sent\n * as one execProtocolRawStream call. This is both correct (prevents\n * portal interleaving) and fast (1 WASM call + 1 async boundary instead\n * of 5). A streaming framer suppresses intermediate ReadyForQuery\n * messages while forwarding the rest of the response without\n * materializing it.\n */\n private async flushPipeline(): Promise<void> {\n const messages = this.pipeline;\n this.pipeline = [];\n let batch: Uint8Array;\n /* c8 ignore next 3 — flushPipeline only runs after Sync is appended */\n if (messages.length === 1) {\n batch = messages[0] ?? new Uint8Array(0);\n } else {\n batch = this.tryContiguousPipelineBatch(messages) ?? this.concatPipeline(messages);\n }\n await this.runWithTiming((detectErrors) =>\n this.streamProtocol(batch, { detectErrors, suppressIntermediateRfq: true }),\n );\n }\n\n private tryContiguousPipelineBatch(messages: Uint8Array[]): Uint8Array | undefined {\n const first = messages[0];\n /* c8 ignore next — caller only passes non-empty pipelines */\n if (first === undefined) return undefined;\n\n const buffer = first.buffer;\n const start = first.byteOffset;\n let end = start + first.byteLength;\n for (let i = 1; i < messages.length; i++) {\n const part = messages[i];\n /* c8 ignore next — pipeline array has no holes */\n if (part === undefined) return undefined;\n if (part.buffer !== buffer || part.byteOffset !== end) {\n return undefined;\n }\n end += part.byteLength;\n }\n\n return new Uint8Array(buffer, start, end - start);\n }\n\n private concatPipeline(messages: Uint8Array[]): Uint8Array {\n const total = messages.reduce((sum, p) => sum + p.length, 0);\n const batch = new Uint8Array(total);\n let offset = 0;\n for (const part of messages) {\n batch.set(part, offset);\n offset += part.length;\n }\n return batch;\n }\n\n // ── PGlite execution ──\n\n /**\n * Acquires the session, runs the op under `pglite.runExclusive`, and\n * updates internal stats and/or publishes diagnostics events when enabled.\n * When neither internal telemetry nor diagnostics subscribers need timing,\n * skips timing entirely.\n *\n * `op` returns `false` when an `ErrorResponse` was seen without throwing\n * (protocol-level failure). Combined with the catch branch, both failure\n * modes flip `succeeded` so both `BridgeStats` and `QUERY_CHANNEL`\n * payloads stay accurate. `detectErrors` is therefore tied to whether\n * either of those consumers is active, not to timing in general.\n */\n private async runWithTiming(op: (detectErrors: boolean) => Promise<boolean>): Promise<void> {\n const wantTelemetry = this.telemetry !== undefined;\n const publishQuery = this.bridgeId !== undefined && queryChannel.hasSubscribers;\n const publishLockWait = this.bridgeId !== undefined && lockWaitChannel.hasSubscribers;\n const wantTiming = wantTelemetry || publishQuery || publishLockWait;\n const detectErrors = wantTelemetry || publishQuery;\n\n if (!wantTiming) {\n await this.runUntimed(async () => {\n await op(false);\n });\n return;\n }\n\n const lockStart = process.hrtime.bigint();\n const session = this.acquireSession();\n if (session) await session;\n const queryStart = process.hrtime.bigint();\n const lockWaitMs = nsToMs(queryStart - lockStart);\n if (wantTelemetry) {\n this.telemetry?.recordLockWait(lockWaitMs);\n }\n if (publishLockWait) {\n lockWaitChannel.publish({\n bridgeId: this.bridgeId,\n durationMs: lockWaitMs,\n });\n }\n\n let succeeded = true;\n try {\n await this.runUnderRunExclusive(async () => {\n succeeded = await op(detectErrors);\n });\n } catch (err) {\n succeeded = false;\n throw err;\n } finally {\n const queryMs = nsToMs(process.hrtime.bigint() - queryStart);\n if (wantTelemetry) {\n this.telemetry?.recordQuery(queryMs, succeeded);\n }\n if (publishQuery) {\n queryChannel.publish({\n bridgeId: this.bridgeId,\n durationMs: queryMs,\n succeeded,\n });\n }\n }\n }\n\n /**\n * Sends a message (or pipelined batch) to PGlite and pushes the raw protocol\n * response to the stream.\n *\n * For pipelined Extended Query batches, pass `suppressIntermediateRfq`\n * so only the final ReadyForQuery reaches the client.\n *\n * Must be called inside runExclusive.\n */\n private async streamProtocol(\n message: Uint8Array,\n options: { detectErrors: boolean; suppressIntermediateRfq: boolean },\n ): Promise<boolean> {\n const { detectErrors, suppressIntermediateRfq } = options;\n let errSeen = false;\n const framer = new BackendMessageFramer({\n suppressIntermediateReadyForQuery: suppressIntermediateRfq,\n rewriteSystemCatalogCharOids: this.rewriteSystemCatalogCharOids,\n onChunk: (chunk) => {\n /* c8 ignore next — race-only: tornDown becomes true mid-stream */\n if (!this.tornDown && chunk.length > 0) {\n this.push(chunk);\n }\n },\n onErrorResponse: () => {\n if (detectErrors) errSeen = true;\n },\n onReadyForQuery: (status) => {\n this.lastSeenRfqStatus = status;\n if (this.sessionLock) {\n this.sessionLock.updateStatus(this.duplexId, status);\n }\n },\n });\n\n // Re-check readiness inside the runExclusive mutex: the caller could\n // have closed pglite between `runUnderRunExclusive`'s pre-check and\n // our turn to run. Without this, `execProtocolRawStream` below would\n // throw a less informative error from inside the WASM runtime.\n await waitPGliteReady(this.pglite, this.timeout);\n\n // PGlite's raw streaming API still parses backend messages internally, but\n // does not clear that parsed-message array after returning. Keep the fast\n // streaming path, then clear PGlite's internal parsed-message state with an\n // ignored Terminate frame. `throwOnError: false` keeps the cleanup\n // best-effort if PGlite changes how that frame is handled.\n let rawBytes = 0;\n let streamFailed = false;\n try {\n await this.pglite.execProtocolRawStream(message, {\n syncToFs: this.syncToFs,\n onRawData: (chunk: Uint8Array) => {\n rawBytes += chunk.byteLength;\n framer.write(chunk);\n },\n });\n } catch (err) {\n streamFailed = true;\n throw err;\n } finally {\n this.pendingProtocolCleanupBytes += rawBytes;\n this.pendingProtocolCleanupCalls++;\n if (\n !this.protocolCleanupUnsupported &&\n (streamFailed ||\n this.pendingProtocolCleanupBytes >= PROTOCOL_CLEANUP_RAW_BYTES ||\n this.pendingProtocolCleanupCalls >= PROTOCOL_CLEANUP_CALLS)\n ) {\n await this.clearPGliteProtocolMessages();\n this.pendingProtocolCleanupBytes = 0;\n this.pendingProtocolCleanupCalls = 0;\n }\n }\n\n framer.flush({ dropHeldReadyForQuery: this.tornDown });\n return !errSeen;\n }\n\n private async clearPGliteProtocolMessages(): Promise<void> {\n if (this.protocolCleanupUnsupported) return;\n\n const { execProtocolStream } = this.pglite;\n if (typeof execProtocolStream !== 'function') {\n this.protocolCleanupUnsupported = true;\n return;\n }\n\n try {\n await execProtocolStream.call(this.pglite, TERMINATE_MESSAGE, {\n syncToFs: false,\n throwOnError: false,\n });\n } catch {\n // Best-effort cleanup for PGlite internals; preserve the original query\n // outcome if the no-op cleanup is unavailable or rejected. Disable\n // future attempts so a PGlite API change costs only one failed cleanup.\n this.protocolCleanupUnsupported = true;\n }\n }\n\n // ── Session lock & rollback ──\n\n private acquireSession(): Promise<void> | undefined {\n return this.sessionLock?.acquire(this.duplexId);\n }\n\n /**\n * Issue `ROLLBACK` against PGlite when the duplex's last observed\n * ReadyForQuery status indicates an open transaction (`T` or `E`). Safe\n * to call when no transaction is active — resolves with no effect.\n *\n * Transaction detection lives on the duplex (not on `SessionLock`) so it\n * works for both locked pools (max>1, TCP server) and standalone duplexes\n * (max=1 pool, no lock). When a `SessionLock` is present, ownership is an\n * additional safety check — a duplex whose lock was released via an error\n * path must not roll back another bridge's transaction.\n *\n * Used by `_final`, `_destroy`, and the Terminate handler. PGlite is\n * single-session, so leftover `T` state would otherwise leak into the\n * next connection.\n */\n async rollbackIfInTransaction(): Promise<void> {\n // Memoize so concurrent teardown paths (e.g., `_final` racing `_destroy`)\n // share one ROLLBACK instead of issuing duplicates.\n if (this.rollbackPromise !== undefined) return this.rollbackPromise;\n this.rollbackPromise = this.runRollback();\n return this.rollbackPromise;\n }\n\n private async runRollback(): Promise<void> {\n // Wait for any in-flight pglite call so its RFQ has been processed —\n // otherwise destroying mid-BEGIN would skip cleanup because the status\n // hasn't transitioned to 'T' yet.\n // The in-flight pglite call is awaited only to sequence its RFQ; the\n // result and any error are intentionally swallowed. Under current\n // orchestration runRollback always observes currentPGliteCall as falsy,\n // but the guard remains for the rare mid-call destroy path.\n /* v8 ignore start */\n if (this.currentPGliteCall) {\n await this.currentPGliteCall.catch(() => undefined);\n }\n /* v8 ignore stop */\n\n const status = this.lastSeenRfqStatus;\n if (status !== RFQ_STATUS_IN_TRANSACTION && status !== RFQ_STATUS_FAILED) return;\n\n // Defensive: rollback only runs while this duplex still owns the session\n // lock (or has none). The non-owner short-circuit cannot be reached under\n // current orchestration but stays as a guardrail.\n /* v8 ignore next */\n if (this.sessionLock !== undefined && !this.sessionLock.isOwner(this.duplexId)) return;\n\n try {\n // pglite.query acquires runExclusive internally — do not wrap it.\n await this.pglite.query('ROLLBACK');\n this.lastSeenRfqStatus = RFQ_STATUS_IDLE;\n } catch {\n // Best-effort cleanup. PGlite may reject ROLLBACK (e.g., already\n // auto-rolled back during shutdown) — nothing recoverable here.\n }\n }\n}\n","import type { PGlite, PGliteInterface } from '@electric-sql/pglite';\n\nexport type SyncToFsMode = 'auto' | boolean;\n\nexport const resolveSyncToFs = (pglite: PGlite | PGliteInterface, mode?: SyncToFsMode): boolean => {\n if (typeof mode === 'boolean') return mode;\n return !!pglite.dataDir && !pglite.dataDir.startsWith('memory://');\n};\n","/**\n * Session-level lock for PGlite's single-session model.\n *\n * PGlite runs PostgreSQL in single-user mode — one session shared by all\n * bridges. runExclusive serializes individual operations, but transactions\n * span multiple operations. Without session-level locking, Bridge A's BEGIN\n * and Bridge B's query interleave, corrupting transaction boundaries.\n *\n * The session lock tracks which bridge owns the session. When PGlite enters\n * transaction state (ReadyForQuery status 'T' or 'E'), the owning bridge\n * gets exclusive access until the transaction completes (status returns to 'I').\n *\n * Non-transactional operations from any bridge are allowed when no transaction\n * is active — they serialize naturally through runExclusive.\n */\n\n// ReadyForQuery status bytes\nconst STATUS_IDLE = 0x49; // 'I' — no transaction\nconst STATUS_IN_TRANSACTION = 0x54; // 'T' — in transaction block\nconst STATUS_FAILED = 0x45; // 'E' — failed transaction block\n\n/**\n * Coordinates PGlite access across concurrent pool connections.\n *\n * @remarks\n * PGlite runs PostgreSQL in single-user mode — one session shared by all\n * bridges. The session lock tracks which bridge owns the session during\n * transactions, preventing interleaving. Used internally by {@link PGliteDuplex}\n * and created automatically by {@link PgBridgePool}. Only instantiate directly\n * if building a custom pool setup.\n */\nexport class SessionLock {\n private owner?: symbol;\n private waitQueue: Array<{ id: symbol; resolve: () => void; reject: (error: Error) => void }> =\n [];\n\n /**\n * Acquire access to PGlite. Resolves immediately if no transaction is\n * active or if this bridge owns the current transaction. Queues otherwise.\n */\n async acquire(id: symbol): Promise<void> {\n // Free slot or re-entrant — pass through\n if (this.owner === undefined || this.owner === id) return;\n\n // Another bridge owns the session — wait\n return new Promise<void>((resolve, reject) => {\n this.waitQueue.push({\n id,\n resolve,\n reject,\n });\n });\n }\n\n /** Returns `true` if `id` currently holds the session (e.g., is mid-transaction). */\n isOwner(id: symbol): boolean {\n return this.owner === id;\n }\n\n /**\n * Update session state based on the ReadyForQuery status byte.\n * Call after every PGlite response that contains RFQ.\n *\n * @returns `true` if ownership transitioned on this call (acquired or\n * released). `false` for no-op updates (e.g., re-entrant status within\n * the same transaction, or IDLE from a non-owning bridge).\n */\n updateStatus(id: symbol, status: number): boolean {\n if (status === STATUS_IN_TRANSACTION || status === STATUS_FAILED) {\n if (this.owner === id) return false;\n this.owner = id;\n return true;\n }\n\n // Transaction complete — release ownership\n if (status === STATUS_IDLE && this.owner === id) {\n this.owner = undefined;\n this.drainWaitQueue();\n return true;\n }\n\n return false;\n }\n\n /**\n * Release ownership (e.g., when a bridge is destroyed mid-transaction).\n *\n * @returns `true` if this bridge held ownership and released it. `false`\n * if another bridge (or no one) owned the session.\n */\n release(id: symbol): boolean {\n if (this.owner === id) {\n this.owner = undefined;\n this.drainWaitQueue();\n return true;\n }\n\n return false;\n }\n\n /**\n * Cancel this bridge's pending or active claim on the session.\n *\n * Used when a bridge is torn down while blocked in `acquire()` so it cannot\n * later be granted ownership after destruction.\n */\n cancel(id: symbol, error: Error = new Error('Session lock acquire cancelled')): boolean {\n let cancelled = false;\n\n if (this.owner === id) {\n this.owner = undefined;\n this.drainWaitQueue();\n cancelled = true;\n }\n\n const remaining: typeof this.waitQueue = [];\n for (const waiter of this.waitQueue) {\n if (waiter.id === id) {\n waiter.reject(error);\n cancelled = true;\n } else {\n remaining.push(waiter);\n }\n }\n this.waitQueue = remaining;\n\n return cancelled;\n }\n\n /** Grant ownership to the next waiter, if any. */\n private drainWaitQueue(): void {\n // Release one waiter at a time and grant ownership before resolving.\n // The waiter's operation will call updateStatus when it completes —\n // if IDLE, ownership is cleared and the next waiter is released.\n // This prevents interleaving where multiple waiters race past acquire\n // and one starts a transaction while others proceed unserialized.\n const next = this.waitQueue.shift();\n if (!next) return;\n\n this.owner = next.id;\n next.resolve();\n }\n}\n","/**\n * Wraps a pg-style `{ getTypeParser }` object so array text-format reads\n * use `postgres-array@3` instead of pg-types' transitive `postgres-array@2`.\n *\n * `@prisma/adapter-pg` already uses postgres-array@3 for its own normalized\n * arrays (NUMERIC[], JSON[], TIMESTAMP[], etc.) but falls back to pg-types'\n * slow parser for:\n * - BYTEA[] (captured at adapter-pg module load from pg-types)\n * - everything else not in its customParsers map: BOOL[], INT2/4/8[],\n * FLOAT4/8[], OID[], TEXT/VARCHAR/CHAR/UUID[], CIDR/INET/MACADDR[],\n * INTERVAL[], TIMETZ[], NUMRANGE[], POINT[]\n *\n * The pg-types path costs O(n) JS object allocations per character of the\n * array literal — turning a 19MB BYTEA[] into 19M+ allocations. v3 uses\n * `indexOf` + `slice`, dropping the same workload to milliseconds.\n *\n * We don't override the *element* parser — we ask the wrapped object for\n * whatever parser it would have used. So adapter-pg's customParsers for\n * scalars (e.g. its `convertBytes` for OID 17) is preserved.\n */\nimport { parse as parseArrayV3 } from 'postgres-array';\n\nexport const isObject = (val: unknown): val is Record<string, unknown> => {\n return typeof val === 'object' && val !== null && !Array.isArray(val);\n};\n\ntype Parser = (raw: string) => unknown;\ntype GetTypeParser = (oid: number, format?: string) => Parser;\n\nexport interface TypesLike {\n getTypeParser: GetTypeParser;\n}\nexport const isTypesLike = (value: unknown): value is TypesLike => {\n return isObject(value) && 'getTypeParser' in value && typeof value.getTypeParser === 'function';\n};\n\n// Array OIDs whose elements pg-types parses as raw strings (no transform).\n// We can hand back a cached parser without consulting `originalGetTypeParser`.\nconst ARRAY_OID_TEXT_LIKE: ReadonlySet<number> = new Set([\n 1002, // _char\n 1009, // _text\n 1014, // _bpchar\n 1015, // _varchar\n 1040, // _macaddr\n 1041, // _inet\n 1270, // _timetz\n 2951, // _uuid\n 651, // _cidr\n 3907, // _numrange\n]);\n\n// Array OID → element scalar OID for OIDs that need a real element parser.\nconst ARRAY_OID_WITH_ELEMENT: ReadonlyMap<number, number> = new Map([\n [1000, 16], // _bool\n [1001, 17], // _bytea\n [1005, 21], // _int2\n [1007, 23], // _int4\n [1016, 20], // _int8\n [1017, 600], // _point\n [1021, 700], // _float4\n [1022, 701], // _float8\n [1028, 26], // _oid\n [1187, 1186], // _interval\n]);\n\nconst FAST_TEXT_ARRAY_PARSER: Parser = (raw) => parseArrayV3(raw);\n\nexport const wrapTypesWithFastArrayParsers = <T extends TypesLike>(types: T): T => {\n const original = types.getTypeParser;\n const wrapped: GetTypeParser = (oid, format = 'text') => {\n if (format === 'text') {\n if (ARRAY_OID_TEXT_LIKE.has(oid)) return FAST_TEXT_ARRAY_PARSER;\n const elementOid = ARRAY_OID_WITH_ELEMENT.get(oid);\n if (elementOid !== undefined) {\n const elementParser = original(elementOid, 'text');\n return (raw) => parseArrayV3(raw, elementParser);\n }\n }\n return original(oid, format);\n };\n return { ...types, getTypeParser: wrapped };\n};\n","import type { PGlite, PGliteInterface } from '@electric-sql/pglite';\nimport pg from 'pg';\nimport { PGliteDuplex } from '../duplex';\nimport type { TelemetrySink } from '../telemetry/bridge-stats.ts';\nimport type { SessionLock } from '../utils/session-lock.ts';\nimport { isObject, isTypesLike, wrapTypesWithFastArrayParsers } from './fast-array-parsers.ts';\n\nexport interface PgBridgeClientOptions {\n pglite: PGlite | PGliteInterface;\n bridgeId: symbol;\n sessionLock?: SessionLock;\n telemetry?: TelemetrySink;\n syncToFs: boolean;\n timeout?: number;\n}\n\ntype PgBridgeClientConfig = pg.ClientConfig & {\n [PgBridgeClient.OptionsKey]: PgBridgeClientOptions;\n};\n\nexport class PgBridgeClient extends pg.Client {\n private querySubmissionChain?: Promise<void>;\n\n static readonly OptionsKey: unique symbol = Symbol('PgBridgeClientOptions');\n\n constructor(config?: PgBridgeClientConfig) {\n const resolved = config ?? ({} as PgBridgeClientConfig);\n const { [PgBridgeClient.OptionsKey]: bridge, ...clientConfig } = resolved;\n if (!bridge) {\n throw new Error('PgBridgeClient requires bridge options');\n }\n\n super({\n ...clientConfig,\n user: 'postgres',\n database: 'postgres',\n stream: () => new PGliteDuplex(bridge.pglite, bridge),\n });\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: satisfy pg.Client.query's overload union\n override query(...args: unknown[]): any {\n const first = args[0];\n const submit = () => {\n // biome-ignore lint/suspicious/noExplicitAny: pg.Client.query has 7 overloads\n return (super.query as any).apply(this, args) as Promise<unknown>;\n };\n\n // Preserve pg's synchronous TypeError for null/undefined query.\n if (first === null || first === undefined) return submit();\n\n // Submittable: terminal signaling isn't uniform across the pg contract.\n // Let pg's internal queue handle it unserialized. adapter-pg never uses\n // this form; users mixing Submittable + Promise forms on one client may\n // still trip the pg queue deprecation.\n if (typeof (first as { submit?: unknown }).submit === 'function') {\n return submit();\n }\n\n const cbIndex = args.findIndex((arg) => typeof arg === 'function');\n if (cbIndex !== -1) {\n const origCb = args[cbIndex] as (err: unknown, res: unknown) => void;\n const promiseArgs = args.slice();\n promiseArgs.splice(cbIndex, 1);\n\n try {\n this.query(...promiseArgs).then(\n (res: unknown) => origCb(null, res),\n (err: unknown) => origCb(err, undefined),\n );\n } catch (err) {\n origCb(err, undefined);\n }\n return undefined;\n }\n\n if (isObject(first) && isTypesLike(first.types)) {\n args[0] = {\n ...first,\n types: wrapTypesWithFastArrayParsers(first.types),\n };\n }\n\n const prior = this.querySubmissionChain;\n let p: Promise<unknown>;\n if (prior === undefined) {\n try {\n p = submit();\n } catch (err) {\n return Promise.reject(err);\n }\n } else {\n p = prior.then(submit);\n }\n let done: Promise<void>;\n const clearChain = () => {\n if (this.querySubmissionChain === done) {\n this.querySubmissionChain = undefined;\n }\n };\n done = p.then(clearChain, clearChain);\n this.querySubmissionChain = done;\n return p;\n }\n}\n","/**\n * `PgBridgePool` — a `pg.Pool` subclass backed by a PGlite instance.\n *\n * Each pool connection gets its own PGliteDuplex stream, all sharing the\n * same PGlite WASM instance. Pools with multiple connections also share a\n * SessionLock. The session lock ensures transaction isolation: when one\n * bridge starts a transaction (BEGIN), it gets exclusive PGlite access until\n * COMMIT/ROLLBACK. Non-transactional operations from any bridge serialize\n * through PGlite's runExclusive mutex.\n */\nimport { PGlite, type PGliteInterface } from '@electric-sql/pglite';\nimport pg from 'pg';\nimport type { TelemetrySink } from '../telemetry/bridge-stats.ts';\nimport { resolveSyncToFs, type SyncToFsMode } from '../utils/resolve-sync-to-fs.ts';\nimport { SessionLock } from '../utils/session-lock.ts';\nimport { PgBridgeClient, type PgBridgeClientOptions } from './pg-bridge-client.ts';\n\nexport interface PgBridgePoolOptions\n extends Omit<PgBridgeClientOptions, 'pglite' | 'bridgeId' | 'syncToFs' | 'telemetry'> {\n /**\n * PGlite instance to back the pool. When omitted the pool creates its own\n * in-memory `PGlite` and owns its lifecycle — `end()` shuts it down. When\n * provided the caller owns the lifecycle — `end()` leaves it open.\n */\n pglite?: PGlite | PGliteInterface;\n\n /**\n * Identity tag published with every diagnostics-channel event. Subscribers\n * filter on this to distinguish events from different bridges in the\n * same process. A fresh `Symbol('bridge')` is generated if omitted; the\n * same `symbol` reference is reused on every event from this pool.\n * Hold a reference if you need to filter from outside.\n */\n bridgeId?: symbol;\n\n /**\n * Maximum pool connections (default: 1). Compatibility knob, not a\n * throughput knob.\n *\n * PGlite's WASM runtime executes queries serially behind a single mutex.\n * Raising `max` above 1 therefore does not add parallelism — queries still\n * run one at a time — and each extra connection costs a full `PGliteDuplex`\n * (its framers and scratch buffers) plus shared session-lock coordination\n * in memory. Leave this at `1` unless your code specifically needs to check\n * out multiple `pg` clients or you are deliberately exercising wait-queue\n * behaviour in a test.\n */\n max?: number;\n\n /**\n * Filesystem sync policy for bridge-driven wire-protocol calls.\n *\n * - `'auto'` (default): disable per-query sync for clearly in-memory PGlite\n * instances, keep it enabled otherwise.\n * - `true`: always sync before the bridge returns a query result.\n * - `false`: never sync on bridge protocol calls; fastest, but weaker durability.\n *\n * `auto` uses `pglite.dataDir` as a heuristic. If you provide a custom\n * persistent `fs` without a meaningful `dataDir`, pass `true` explicitly.\n */\n syncToFs?: SyncToFsMode;\n\n /**\n * Maximum milliseconds to wait for the PGlite instance to become ready\n * before each bridge operation. Defaults to no timeout (waits indefinitely).\n */\n timeout?: number;\n}\n\n/**\n * A pg.Pool where every connection is an in-process PGlite bridge.\n *\n * **Ownership:** when no `pglite` is supplied the pool creates its own\n * in-memory `PGlite` and owns it — `end()` shuts it down. When you supply\n * a `pglite`, the pool treats it as caller-owned and `end()` leaves it open.\n *\n * Most users should prefer {@link PGliteBridge}, which wraps this class and\n * also handles schema application and reset/snapshot lifecycle.\n *\n * ```typescript\n * import { PgBridgePool } from 'prisma-pglite-bridge';\n * import { PrismaPg } from '@prisma/adapter-pg';\n * import { PrismaClient } from '@prisma/client';\n *\n * // Pool creates and owns its own in-memory PGlite:\n * const pool = new PgBridgePool();\n * const adapter = new PrismaPg(pool);\n * const prisma = new PrismaClient({ adapter });\n * await prisma.$disconnect();\n * await pool.end(); // closes pool + pglite (pool owns it)\n *\n * // Caller-supplied PGlite — caller owns the lifecycle:\n * import { PGlite } from '@electric-sql/pglite';\n * const pglite = new PGlite();\n * const pool = new PgBridgePool({ pglite });\n * await pool.end(); // closes pool only; pglite stays open\n * await pglite.close(); // caller is responsible\n * ```\n *\n * @see {@link PGliteBridge} for the higher-level API with schema management\n * and reset/snapshot lifecycle.\n */\nexport class PgBridgePool extends pg.Pool {\n /**\n * Identity tag published on every diagnostics-channel event from this\n * pool. Stable for the lifetime of the pool — the same `symbol`\n * reference appears on every event.\n */\n readonly bridgeId: symbol;\n\n /**\n * The PGlite instance this pool wraps. Created internally when no `pglite`\n * option was supplied; otherwise the caller-supplied instance.\n */\n readonly pglite: PGlite | PGliteInterface;\n\n readonly #ownsPglite: boolean;\n\n constructor({\n bridgeId = Symbol('bridge'),\n max = 1,\n pglite,\n telemetry,\n timeout,\n syncToFs,\n }: PgBridgePoolOptions & { telemetry?: TelemetrySink } = {}) {\n const resolvedPglite = pglite ?? new PGlite();\n\n // Load-bearing: pg.Pool forwards this config object verbatim to\n // `new Client(config)`, including the symbol-keyed property below.\n // PgBridgeClient reads its bridge options from the same symbol.\n const poolConfig = {\n Client: PgBridgeClient,\n max,\n [PgBridgeClient.OptionsKey]: {\n pglite: resolvedPglite,\n sessionLock: max > 1 ? new SessionLock() : undefined,\n bridgeId,\n telemetry,\n syncToFs: resolveSyncToFs(resolvedPglite, syncToFs),\n timeout,\n },\n };\n\n super(poolConfig);\n\n this.bridgeId = bridgeId;\n this.pglite = resolvedPglite;\n this.#ownsPglite = !pglite;\n }\n\n /**\n * Drain the pool and close all connections. When the pool created its own\n * PGlite (no `pglite` option at construction), also closes that instance.\n * When the caller supplied a `pglite`, it is left open.\n */\n override end(): Promise<void>;\n override end(callback: () => void): void;\n override end(callback?: () => void): Promise<void> | void {\n const cleanup = async (): Promise<void> => {\n if (this.#ownsPglite && !this.pglite.closed) {\n await this.pglite.close();\n }\n };\n if (callback) {\n super.end(() => {\n cleanup().then(callback, callback);\n });\n return;\n }\n return super.end().then(cleanup);\n }\n}\n","/**\n * Private per-bridge stats state used to power `stats()`.\n *\n * Instantiated at level `'basic'` or `'full'` (level `'off'` means no stats\n * object exists).\n * Query-level timing is recorded directly by the bridge; one-shot lifecycle\n * signals (`incrementResetDb`, `freeze`) remain direct method calls invoked\n * by the bridge itself.\n *\n * Percentiles use nearest-rank (no interpolation) over a sliding window of\n * the most recent {@link QUERY_DURATION_WINDOW_SIZE} queries. Lifetime\n * counters (`queryCount`, `totalQueryMs`, `avgQueryMs`) are not windowed.\n * `durationMs` is frozen at the instant `close()` was invoked, via the\n * `closeEntryHrtime` the bridge records as its very first action and\n * passes to {@link freeze}.\n */\nimport type { PGlite } from '@electric-sql/pglite';\nimport { nsToMs } from '../utils/time.ts';\n\ntype DbSizeQueryable = Pick<PGlite, 'query'>;\n\n/**\n * Stats collection level.\n *\n * - `'off'` — `stats()` returns `undefined`. Zero hot-path overhead.\n * - `'basic'` — timing (`durationMs`), query percentiles, counters, and\n * `dbSizeBytes`.\n * - `'full'` — `'basic'` plus `processRssPeakBytes` and session-lock\n * waits.\n */\nexport type StatsLevel = 'off' | 'basic' | 'full';\n\n/** Internal bridge-facing telemetry contract. */\nexport interface TelemetrySink {\n recordQuery(durationMs: number, succeeded: boolean): void;\n recordLockWait(durationMs: number): void;\n}\n\n/**\n * Maximum number of recent query durations retained for percentile\n * computation. Beyond this window, `recentP50QueryMs`, `recentP95QueryMs`,\n * and `recentMaxQueryMs` reflect only the most recent N queries — lifetime\n * counters (`queryCount`, `totalQueryMs`, `avgQueryMs`) remain complete.\n */\nexport const QUERY_DURATION_WINDOW_SIZE = 10_000;\n\nconst QUERY_DURATION_TRIM_THRESHOLD = QUERY_DURATION_WINDOW_SIZE * 2;\n\ninterface StatsBase {\n durationMs: number;\n /** Lifetime count of recorded queries. Not windowed. */\n queryCount: number;\n failedQueryCount: number;\n /** Lifetime sum of query durations. Not windowed. */\n totalQueryMs: number;\n /** Lifetime mean query duration. Not windowed. */\n avgQueryMs: number;\n /**\n * Nearest-rank 50th percentile over the most recent\n * {@link QUERY_DURATION_WINDOW_SIZE} queries. Compare to `avgQueryMs`\n * with care: the two fields describe different populations on long-lived\n * bridges.\n */\n recentP50QueryMs: number;\n /** Nearest-rank 95th percentile over the recent-query window. */\n recentP95QueryMs: number;\n /** Maximum query duration within the recent-query window. */\n recentMaxQueryMs: number;\n resetDbCalls: number;\n /** Undefined only when the `pg_database_size` query rejected. */\n dbSizeBytes?: number;\n}\n\nexport interface StatsBasic extends StatsBase {\n statsLevel: 'basic';\n}\n\nexport interface StatsFull extends StatsBase {\n statsLevel: 'full';\n /**\n * Process-wide RSS high-water mark since process start, read from\n * `process.resourceUsage().maxRSS` (kernel-tracked, lossless). Reflects\n * the entire Node process — parallel test runners, other bridges, and\n * prior work in the same process all contribute. Use only as an\n * ordering signal, not an absolute measurement.\n *\n * `undefined` on runtimes that don't expose `process.resourceUsage`\n * (e.g. Bun, Deno, edge workers) — matches the field-level-undefined\n * contract of every other `Stats` member.\n */\n processRssPeakBytes: number | undefined;\n totalSessionLockWaitMs: number;\n sessionLockAcquisitionCount: number;\n avgSessionLockWaitMs: number;\n maxSessionLockWaitMs: number;\n}\n\n/**\n * Snapshot returned by {@link PGliteBridge.stats}. Discriminated by\n * `statsLevel` — narrow before reading `'full'`-only fields.\n *\n * @example\n * ```typescript\n * const stats = await bridge.stats();\n * if (stats?.statsLevel === 'full') {\n * console.log(stats.processRssPeakBytes); // typed as number | undefined\n * }\n * ```\n */\nexport type Stats = StatsBasic | StatsFull;\n\nconst DB_SIZE_QUERY_TIMEOUT_MS = 5_000;\n\n/**\n * `process.resourceUsage().maxRSS` returns kilobytes on every platform\n * Node supports — we convert to bytes so the public `processRssPeakBytes`\n * field matches its name. Returns `undefined` on runtimes that don't\n * expose `resourceUsage` (Bun, Deno, edge workers).\n */\nconst readProcessRssPeakBytes = (): number | undefined => {\n try {\n return process.resourceUsage().maxRSS * 1024;\n } catch {\n return undefined;\n }\n};\n\nconst percentile = (sorted: readonly number[], p: number): number => {\n const n = sorted.length;\n if (n === 0) return 0;\n const index = Math.min(n - 1, Math.max(0, Math.ceil((p / 100) * n) - 1));\n /* c8 ignore next */\n return sorted[index] ?? 0;\n};\n\nexport class BridgeStats implements TelemetrySink {\n private readonly level: 'basic' | 'full';\n private readonly createdAtHrtime: bigint;\n\n private queryDurations: number[] = [];\n private totalQueryMs = 0;\n private queryCount = 0;\n private failedQueryCount = 0;\n private resetDbCalls = 0;\n\n private totalSessionLockWaitMs = 0;\n private maxSessionLockWaitMs = 0;\n private sessionLockAcquisitionCount = 0;\n\n private frozen = false;\n private cachedDurationMs?: number;\n private cachedDbSizeBytes?: number;\n private dbSizeFrozen = false;\n\n constructor(level: 'basic' | 'full') {\n this.level = level;\n this.createdAtHrtime = process.hrtime.bigint();\n }\n\n recordQuery(durationMs: number, succeeded: boolean): void {\n if (this.frozen) return;\n this.queryCount += 1;\n this.totalQueryMs += durationMs;\n this.queryDurations.push(durationMs);\n if (this.queryDurations.length > QUERY_DURATION_TRIM_THRESHOLD) {\n this.queryDurations = this.queryDurations.slice(-QUERY_DURATION_WINDOW_SIZE);\n }\n if (!succeeded) this.failedQueryCount += 1;\n }\n\n recordLockWait(durationMs: number): void {\n if (this.frozen) return;\n if (this.level !== 'full') return;\n this.totalSessionLockWaitMs += durationMs;\n this.sessionLockAcquisitionCount += 1;\n if (durationMs > this.maxSessionLockWaitMs) this.maxSessionLockWaitMs = durationMs;\n }\n\n incrementResetDb(): void {\n if (this.frozen) return;\n this.resetDbCalls += 1;\n }\n\n async snapshot(pglite: DbSizeQueryable): Promise<Stats> {\n const durationMs =\n this.cachedDurationMs ?? nsToMs(process.hrtime.bigint() - this.createdAtHrtime);\n const dbSizeBytes = this.dbSizeFrozen ? this.cachedDbSizeBytes : await this.queryDbSize(pglite);\n\n const sorted = [...this.queryDurations].sort((a, b) => a - b);\n const avgQueryMs = this.queryCount === 0 ? 0 : this.totalQueryMs / this.queryCount;\n\n const base: StatsBase = {\n durationMs,\n queryCount: this.queryCount,\n failedQueryCount: this.failedQueryCount,\n totalQueryMs: this.totalQueryMs,\n avgQueryMs,\n recentP50QueryMs: percentile(sorted, 50),\n recentP95QueryMs: percentile(sorted, 95),\n recentMaxQueryMs: percentile(sorted, 100),\n resetDbCalls: this.resetDbCalls,\n dbSizeBytes,\n };\n\n if (this.level === 'basic') {\n return { ...base, statsLevel: 'basic' };\n }\n\n const count = this.sessionLockAcquisitionCount;\n return {\n ...base,\n statsLevel: 'full',\n processRssPeakBytes: readProcessRssPeakBytes(),\n totalSessionLockWaitMs: this.totalSessionLockWaitMs,\n sessionLockAcquisitionCount: count,\n avgSessionLockWaitMs: count === 0 ? 0 : this.totalSessionLockWaitMs / count,\n maxSessionLockWaitMs: this.maxSessionLockWaitMs,\n };\n }\n\n async freeze(pglite: DbSizeQueryable, closeEntryHrtime: bigint): Promise<void> {\n if (this.frozen) return;\n this.frozen = true;\n\n this.cachedDurationMs = nsToMs(closeEntryHrtime - this.createdAtHrtime);\n try {\n this.cachedDbSizeBytes = await this.queryDbSize(pglite);\n } finally {\n this.dbSizeFrozen = true;\n }\n }\n\n private async queryDbSize(pglite: DbSizeQueryable): Promise<number | undefined> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error('pg_database_size query timed out')),\n DB_SIZE_QUERY_TIMEOUT_MS,\n );\n timer.unref?.();\n });\n const { rows } = await Promise.race([\n pglite.query<{ size: string | null }>(\n 'SELECT pg_database_size(current_database())::text AS size',\n ),\n timeout,\n ]);\n const size = rows[0]?.size;\n if (size == null) return undefined;\n const n = Number(size);\n return Number.isFinite(n) ? n : undefined;\n } catch {\n return undefined;\n } finally {\n clearTimeout(timer);\n }\n }\n}\n","/** JS equivalent of PostgreSQL's `quote_ident()`; matches its escaping rules. */\nexport const quoteIdent = (identifier: string): string => `\"${identifier.replace(/\"/g, '\"\"')}\"`;\n","import type { PGlite, PGliteInterface } from '@electric-sql/pglite';\nimport { quoteIdent } from '../utils/quote-ident.ts';\n\nconst SNAPSHOT_SCHEMA = '_pglite_snapshot';\n\nconst SYSTEM_SCHEMA_EXCLUSION = `schemaname NOT IN ('pg_catalog', 'information_schema')\n AND schemaname != '${SNAPSHOT_SCHEMA}'`;\n\nconst USER_TABLES_WHERE = `${SYSTEM_SCHEMA_EXCLUSION}\n AND tablename NOT LIKE '_prisma%'`;\n\nconst escapeLiteral = (s: string): string => `'${s.replace(/'/g, \"''\")}'`;\n\nconst SNAPSHOT_SCHEMA_IDENT = quoteIdent(SNAPSHOT_SCHEMA);\nconst SNAPSHOT_SCHEMA_LITERAL = escapeLiteral(SNAPSHOT_SCHEMA);\n\n/**\n * Snapshot helpers backing `PGliteBridge`'s `snapshotDb` / `resetDb` /\n * `resetSnapshot` functions. Stores a copy of user tables and sequence\n * values in a dedicated `_pglite_snapshot` schema so tests can reset to a\n * known seed state without re-running migrations.\n *\n * @internal\n */\nexport class SnapshotManager {\n readonly #pglite: PGlite | PGliteInterface;\n #hasSnapshot = false;\n\n constructor(pglite: PGlite | PGliteInterface) {\n this.#pglite = pglite;\n }\n\n /**\n * Capture the current state of all user tables plus sequence values into\n * the `_pglite_snapshot` schema. Replaces any previous snapshot.\n */\n async snapshotDb(): Promise<void> {\n await this.#pglite.exec(`DROP SCHEMA IF EXISTS ${SNAPSHOT_SCHEMA_IDENT} CASCADE`);\n\n try {\n await this.#pglite.exec('BEGIN');\n await this.#pglite.exec(`CREATE SCHEMA ${SNAPSHOT_SCHEMA_IDENT}`);\n\n const { rows: tables } = await this.#pglite.query<{\n schemaname: string;\n tablename: string;\n qualified: string;\n }>(\n `SELECT schemaname, tablename,\n quote_ident(schemaname) || '.' || quote_ident(tablename) AS qualified\n FROM pg_tables\n WHERE ${USER_TABLES_WHERE}`,\n );\n\n await this.#pglite.exec(\n `CREATE TABLE ${SNAPSHOT_SCHEMA_IDENT}.__tables (snap_name text, source_schema text, source_table text)`,\n );\n\n for (const [i, { schemaname, tablename, qualified }] of tables.entries()) {\n const snapName = `_snap_${i}`;\n await this.#pglite.exec(\n `CREATE TABLE ${SNAPSHOT_SCHEMA_IDENT}.${quoteIdent(snapName)} AS SELECT * FROM ${qualified}`,\n );\n await this.#pglite.exec(\n `INSERT INTO ${SNAPSHOT_SCHEMA_IDENT}.__tables VALUES (${escapeLiteral(snapName)}, ${escapeLiteral(schemaname)}, ${escapeLiteral(tablename)})`,\n );\n }\n\n const { rows: seqs } = await this.#pglite.query<{ name: string; value: string }>(\n `SELECT quote_literal(quote_ident(schemaname) || '.' || quote_ident(sequencename)) AS name, last_value::text AS value\n FROM pg_sequences\n WHERE ${SYSTEM_SCHEMA_EXCLUSION}\n AND last_value IS NOT NULL`,\n );\n\n await this.#pglite.exec(\n `CREATE TABLE ${SNAPSHOT_SCHEMA_IDENT}.__sequences (name text, value bigint)`,\n );\n for (const { name, value } of seqs) {\n await this.#pglite.exec(\n `INSERT INTO ${SNAPSHOT_SCHEMA_IDENT}.__sequences VALUES (${name}, ${value})`,\n );\n }\n\n await this.#pglite.exec('COMMIT');\n } catch (err) {\n await this.#pglite.exec('ROLLBACK');\n await this.#pglite.exec(`DROP SCHEMA IF EXISTS ${SNAPSHOT_SCHEMA_IDENT} CASCADE`);\n throw err;\n }\n\n this.#hasSnapshot = true;\n }\n\n /** Drop the saved snapshot, reverting `resetDb` to plain truncation. */\n async resetSnapshot(): Promise<void> {\n this.#hasSnapshot = false;\n await this.#pglite.exec(`DROP SCHEMA IF EXISTS ${SNAPSHOT_SCHEMA_IDENT} CASCADE`);\n }\n\n /**\n * Truncate all user tables. If a snapshot exists, restore its contents and\n * sequence values afterwards; otherwise just truncate and `DISCARD ALL`.\n */\n async resetDb(): Promise<void> {\n if (this.#hasSnapshot) await this.#snapshotSchemaExists();\n\n const tables = await this.#getTables();\n\n if (tables) {\n await this.#withReplicationRoleReplica(async () => {\n await this.#pglite.exec(`TRUNCATE TABLE ${tables} RESTART IDENTITY CASCADE`);\n\n if (!this.#hasSnapshot) return;\n\n const { rows: snapshotTables } = await this.#pglite.query<{\n snap_name_ident: string;\n qualified: string;\n }>(\n `SELECT quote_ident(snap_name) AS snap_name_ident,\n quote_ident(source_schema) || '.' || quote_ident(source_table) AS qualified\n FROM ${SNAPSHOT_SCHEMA_IDENT}.__tables`,\n );\n\n for (const { snap_name_ident, qualified } of snapshotTables) {\n await this.#pglite.exec(\n `INSERT INTO ${qualified} SELECT * FROM ${SNAPSHOT_SCHEMA_IDENT}.${snap_name_ident}`,\n );\n }\n\n const { rows: seqs } = await this.#pglite.query<{ name: string; value: string }>(\n `SELECT quote_literal(name) AS name, value::text AS value FROM ${SNAPSHOT_SCHEMA_IDENT}.__sequences`,\n );\n\n for (const { name, value } of seqs) {\n await this.#pglite.exec(`SELECT setval(${name}, ${value})`);\n }\n });\n }\n\n await this.#pglite.exec('DISCARD ALL');\n }\n\n async #getTables(): Promise<string> {\n const { rows } = await this.#pglite.query<{ qualified: string }>(\n `SELECT quote_ident(schemaname) || '.' || quote_ident(tablename) AS qualified\n FROM pg_tables\n WHERE ${USER_TABLES_WHERE}`,\n );\n return rows.map((row) => row.qualified).join(', ');\n }\n\n /**\n * Self-heal: someone (e.g. `resetSchema`) may drop `_pglite_snapshot`\n * out from under us. Returns whether the schema actually exists right now,\n * and clears `#hasSnapshot` if it doesn't.\n */\n async #snapshotSchemaExists(): Promise<boolean> {\n const { rows } = await this.#pglite.query<{ exists: boolean }>(\n `SELECT to_regnamespace(${SNAPSHOT_SCHEMA_LITERAL}) IS NOT NULL AS exists`,\n );\n const exists = rows[0]?.exists;\n if (!exists) this.#hasSnapshot = false;\n return !!exists;\n }\n\n async #withReplicationRoleReplica(fn: () => Promise<void>): Promise<void> {\n try {\n await this.#pglite.exec('SET session_replication_role = replica');\n await fn();\n } finally {\n await this.#pglite.exec('SET session_replication_role = DEFAULT');\n }\n }\n}\n","/**\n * `PGliteBridge` bundles a Prisma driver adapter, the underlying PGlite\n * instance, and lifecycle helpers. No TCP, no Docker, no worker threads —\n * everything runs in the same process. Suitable for testing, development,\n * seeding, and scripts.\n *\n * **Ownership:** When no `pglite` option is supplied the bridge creates its\n * own in-memory `PGlite` instance and owns it — `close()` shuts down the\n * pool _and_ the PGlite instance. When you supply a `pglite` the bridge\n * treats it as caller-owned and `close()` leaves it open.\n *\n * Schema application is a separate concern: call {@link pushMigrations}\n * (raw SQL / migrations directory) or {@link pushSchema} (WASM-engine diff)\n * before issuing Prisma traffic. When reopening a persistent `dataDir`, the\n * PGlite instance is assumed to already hold the schema.\n *\n * ```typescript\n * import { PGliteBridge, pushMigrations } from 'prisma-pglite-bridge';\n * import { PrismaClient } from '@prisma/client';\n *\n * // Bridge creates and owns its own in-memory PGlite:\n * const bridge = new PGliteBridge();\n * await pushMigrations(bridge.pglite, { migrationsPath: './prisma/migrations' });\n * const prisma = new PrismaClient({ adapter: bridge.adapter });\n * beforeEach(() => bridge.resetDb());\n * afterAll(async () => {\n * await prisma.$disconnect();\n * await bridge.close(); // closes pool + pglite (bridge owns it)\n * });\n *\n * // Caller-supplied PGlite — caller owns the lifecycle:\n * import { PGlite } from '@electric-sql/pglite';\n * const pglite = new PGlite();\n * const bridge = new PGliteBridge({ pglite });\n * afterAll(async () => {\n * await bridge.close(); // closes pool only; pglite stays open\n * await pglite.close(); // caller is responsible\n * });\n * ```\n *\n * Public methods are arrow-function class fields so destructuring stays safe:\n * `const { resetDb } = bridge; await resetDb();` works as expected.\n */\nimport { PGlite, type PGliteInterface } from '@electric-sql/pglite';\nimport { PrismaPg } from '@prisma/adapter-pg';\n\nimport { PgBridgePool } from '../pool';\nimport { BridgeStats, type Stats, type StatsLevel } from '../telemetry/bridge-stats.ts';\nimport type { SyncToFsMode } from '../utils/resolve-sync-to-fs.ts';\nimport { SnapshotManager } from './snapshot-manager.ts';\n\n/** @internal Exported for testing. */\nexport const emitBridgeLeakWarning = (): void => {\n process.emitWarning(\n 'PGliteBridge was garbage-collected before close() was called. ' +\n 'Call bridge.close() to release the pool and finalize stats().',\n { type: 'PGliteBridgeLeakWarning' },\n );\n};\n\nconst leakRegistry = new FinalizationRegistry<void>(emitBridgeLeakWarning);\n\nexport interface PGliteBridgeOptions {\n /**\n * Identity tag published with every diagnostics-channel event. Subscribers\n * filter on this to distinguish events from different bridges in the\n * same process. A fresh `Symbol('bridge')` is generated if omitted.\n */\n bridgeId?: symbol;\n\n /**\n * PGlite instance to bridge to. When omitted the bridge creates its own\n * in-memory `PGlite` and owns its lifecycle — `close()` shuts it down.\n * When provided the caller owns the lifecycle — `close()` leaves it open.\n */\n pglite?: PGlite | PGliteInterface;\n\n /**\n * Maximum pool connections (default: 1). Compatibility knob, not a\n * throughput knob.\n *\n * PGlite serialises queries inside its WASM runtime. Extra pool connections\n * do not add parallelism; they only add bridge/client memory and\n * session-lock coordination. Leave this at `1` unless the code under test\n * specifically needs multiple checked-out `pg` clients.\n */\n max?: number;\n\n /**\n * Collect bridge/query telemetry. Default `'off'` (zero overhead).\n *\n * - `'basic'` — timing (`durationMs`, query percentiles) and counters\n * (`queryCount`, `failedQueryCount`, `resetDbCalls`), plus\n * `dbSizeBytes`.\n * - `'full'` — everything in `'basic'`, plus `processRssPeakBytes`\n * (process-wide, sampled) and session-lock wait statistics.\n *\n * Retrieve via `await bridge.stats()` — returns `undefined` at `'off'`.\n */\n statsLevel?: StatsLevel;\n\n /**\n * Filesystem sync policy for bridge-driven wire-protocol calls.\n *\n * Default `'auto'`: disables per-query sync for clearly in-memory PGlite\n * instances and keeps it enabled otherwise. Set `true` to prefer durability\n * on persistent stores, or `false` to prefer lower RSS / higher throughput.\n *\n * If you provide a custom persistent PGlite `fs` without a meaningful\n * `dataDir`, pass `true` explicitly.\n */\n syncToFs?: SyncToFsMode;\n\n /**\n * Maximum milliseconds to wait for the PGlite instance to become ready\n * before each bridge operation. Defaults to no timeout (waits indefinitely),\n * matching the previous unbounded `await pglite.waitReady` behavior.\n */\n timeout?: number;\n}\n\nexport class PGliteBridge {\n /** Prisma adapter — pass directly to `new PrismaClient({ adapter })`. */\n readonly adapter: PrismaPg;\n\n /**\n * The PGlite instance this bridge wraps. Created internally when no\n * `pglite` option was supplied; otherwise the caller-supplied instance.\n * Exposed so helpers like {@link pushMigrations} can run SQL directly\n * through `pglite.exec(...)` without going through the bridge pool.\n */\n readonly pglite: PGlite | PGliteInterface;\n\n /**\n * Identity tag published on every `QUERY_CHANNEL` / `LOCK_WAIT_CHANNEL`\n * diagnostics event produced by this bridge. External subscribers\n * filter on it to isolate events from this bridge in multi-bridge\n * processes. Stable for the lifetime of the bridge instance — the same\n * `symbol` reference appears on every event from the same bridge.\n */\n readonly bridgeId: symbol;\n\n readonly #pool: PgBridgePool;\n readonly #stats: BridgeStats | undefined;\n readonly #snapshot: SnapshotManager;\n readonly #leakToken: object = {};\n readonly #ownsPglite: boolean;\n #closing: Promise<void> | undefined;\n\n constructor(options: PGliteBridgeOptions = {}) {\n const statsLevel = options.statsLevel ?? 'off';\n if (statsLevel !== 'off' && statsLevel !== 'basic' && statsLevel !== 'full') {\n throw new Error(`statsLevel must be 'off', 'basic', or 'full'; got ${String(statsLevel)}`);\n }\n\n this.#ownsPglite = !options.pglite;\n this.pglite = options.pglite ?? new PGlite();\n this.bridgeId = options.bridgeId ?? Symbol('bridge');\n\n this.#stats = statsLevel === 'off' ? undefined : new BridgeStats(statsLevel);\n this.#pool = new PgBridgePool({\n ...options,\n pglite: this.pglite,\n bridgeId: this.bridgeId,\n telemetry: this.#stats,\n });\n this.#snapshot = new SnapshotManager(this.pglite);\n\n this.adapter = new PrismaPg(this.#pool);\n\n leakRegistry.register(this.adapter, undefined, this.#leakToken);\n }\n\n /**\n * Clear all user tables and discard session-local state. Call in\n * `beforeEach` for per-test isolation. When a snapshot has been taken\n * via {@link snapshotDb}, restores from that snapshot instead of\n * truncating to empty.\n *\n * Throws if any pool client is currently checked out — the operation\n * runs raw SQL on the PGlite instance bypassing the pool, so concurrent\n * pool traffic would interleave unsafely. Await all pending Prisma\n * queries first.\n */\n resetDb = async (): Promise<void> => {\n this.#assertPoolIdle('resetDb');\n this.#stats?.incrementResetDb();\n return this.#snapshot.resetDb();\n };\n\n /**\n * Snapshot the current DB state into an internal `_pglite_snapshot`\n * schema. Subsequent `resetDb` calls restore from this snapshot instead\n * of truncating to empty.\n *\n * Throws if any pool client is currently checked out — the operation\n * runs multiple `exec()` statements directly against the PGlite\n * instance, bypassing the pool's `SessionLock`. Call from a test\n * `beforeAll` after migrations but before Prisma traffic starts.\n */\n snapshotDb = async (): Promise<void> => {\n this.#assertPoolIdle('snapshotDb');\n return this.#snapshot.snapshotDb();\n };\n\n /**\n * Discard the current snapshot. Subsequent `resetDb` calls truncate to\n * empty. Same concurrency requirements as {@link snapshotDb} — throws if\n * any pool client is currently checked out.\n */\n resetSnapshot = async (): Promise<void> => {\n this.#assertPoolIdle('resetSnapshot');\n return this.#snapshot.resetSnapshot();\n };\n\n #assertPoolIdle(method: string): void {\n const inFlight = this.#pool.totalCount - this.#pool.idleCount;\n if (inFlight > 0) {\n throw new Error(\n `${method}() requires no in-flight pool queries; got ${inFlight}. ` +\n 'Await all pending Prisma queries (or end an open `$transaction`) before calling.',\n );\n }\n }\n\n /**\n * Shut down the pool. When the bridge created its own PGlite (no `pglite`\n * option at construction), also closes that instance. When the caller\n * supplied a `pglite`, it is left open — the caller is responsible for\n * closing it.\n *\n * When `statsLevel` is not `'off'`, call {@link stats} *after* `close()`\n * to collect the frozen snapshot — `durationMs` and `dbSizeBytes` are\n * cached at the moment `close()` is invoked, and subsequent `stats()`\n * calls are safe.\n */\n close = async (): Promise<void> => {\n if (!this.#closing) {\n this.#closing = (async () => {\n const closeEntry = this.#stats ? process.hrtime.bigint() : undefined;\n await this.#pool.end();\n if (closeEntry !== undefined) {\n await this.#stats?.freeze(this.pglite, closeEntry);\n }\n leakRegistry.unregister(this.#leakToken);\n if (this.#ownsPglite && !this.pglite.closed) {\n await this.pglite.close();\n }\n })();\n }\n return this.#closing;\n };\n\n /**\n * Retrieve collected telemetry. Returns `undefined` when `statsLevel`\n * was `'off'` (or omitted). Never throws — field-level failures surface\n * as `undefined` values (see {@link Stats}).\n */\n stats = async (): Promise<Stats | undefined> => {\n return this.#stats ? this.#stats.snapshot(this.pglite) : undefined;\n };\n}\n","/**\n * TCP / Unix-socket server that exposes a PGlite instance to standard\n * PostgreSQL clients (`psql`, Prisma CLI shadow database, DBeaver, Studio,\n * etc.).\n *\n * Each accepted socket gets its own {@link PGliteDuplex}, all sharing one\n * {@link SessionLock} so transactions across connections serialize correctly.\n * SSL / GSS pre-negotiation is rejected with a single `N` byte so clients\n * fall back to plaintext; there is no authentication. Bind to loopback only\n * — this is a development tool, not a hardened endpoint.\n *\n * **Ownership:** When no `pglite` option is supplied the server creates its\n * own in-memory `PGlite` instance and owns it — `close()` shuts down the\n * listener _and_ the PGlite instance. When you supply a `pglite` the server\n * treats it as caller-owned and `close()` leaves it open.\n *\n * The constructor is synchronous; the network bind is exposed as an\n * explicit async `listen()` (mirroring `net.Server`'s API) which awaits\n * `pglite.waitReady` internally and resolves to the connection URL.\n *\n * ```typescript\n * // Server creates and owns its own in-memory PGlite:\n * const server = new PGliteServer();\n * const url = await server.listen();\n * console.log(url); // → postgres://postgres@127.0.0.1:54321/postgres\n * await server.close(); // closes listener + pglite (server owns it)\n *\n * // Caller-supplied PGlite — caller owns the lifecycle:\n * import { PGlite } from '@electric-sql/pglite';\n * const pglite = new PGlite();\n * const server = new PGliteServer({ pglite });\n * await server.close(); // closes listener only; pglite stays open\n * await pglite.close(); // caller is responsible\n * ```\n */\nimport net from 'node:net';\nimport nodePath from 'node:path';\nimport { PGlite, type PGliteInterface } from '@electric-sql/pglite';\n\nimport { PGliteDuplex } from '../duplex';\nimport { resolveSyncToFs, type SyncToFsMode } from '../utils/resolve-sync-to-fs.ts';\nimport { SessionLock } from '../utils/session-lock.ts';\n\nconst SSL_REQUEST_CODE = 80877103;\nconst GSSENC_REQUEST_CODE = 80877104;\nconst CANCEL_REQUEST_CODE = 80877102;\nconst PRELUDE_HEADER_BYTES = 8;\nconst DEFAULT_SOCKET_PORT = 5432;\n\ntype RequiredBy<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;\n\nexport interface PGliteServerOptions {\n /**\n * PGlite instance to expose. When omitted the server creates its own\n * in-memory `PGlite` and owns its lifecycle — `close()` shuts it down.\n * When provided the caller owns the lifecycle — `close()` leaves it open.\n *\n * `listen()` awaits `pglite.waitReady` internally, so the instance does\n * not have to be ready at construction time.\n */\n pglite?: PGlite | PGliteInterface;\n /** Bind host. Default `'127.0.0.1'` (loopback only). Ignored when `dataDir` is set. */\n host?: string;\n /**\n * In TCP mode: the listen port. Default `0` (ephemeral — read back from\n * the URL returned by `listen()`).\n *\n * In Unix-socket mode (when `dataDir` is set): the libpq port-suffix used\n * to build the socket filename `<dataDir>/.s.PGSQL.<port>`. Default `5432`.\n */\n port?: number;\n /**\n * If set, switches to Unix-socket mode. The server binds the\n * libpq-conventional path `<dataDir>/.s.PGSQL.<port>` so `psql` and\n * `pg.Client` can connect with just `host=<dataDir>`. Takes precedence\n * over `host`. The caller is responsible for the directory's writability\n * and access mode; `close()` does not remove the socket file, and\n * `listen()` will fail with `EADDRINUSE` if a stale socket remains —\n * unlink first.\n */\n dataDir?: string;\n /** Filesystem sync policy. See {@link SyncToFsMode}. Default `'auto'`. */\n syncToFs?: SyncToFsMode;\n /**\n * Maximum milliseconds to wait for the PGlite instance to become ready\n * before each bridge operation. Defaults to no timeout (waits indefinitely).\n */\n timeout?: number;\n /**\n * Username embedded in the connection URL returned by `listen()`.\n * Default `'postgres'`. PGlite ignores this — there is no authentication —\n * but Prisma 7's schema engine rejects URLs without a user (P1010), so a\n * value is always emitted.\n */\n user?: string;\n}\n\ntype BridgedSocket = net.Socket & { duplex?: PGliteDuplex };\n\nexport class PGliteServer {\n /**\n * The PGlite instance this server fronts. Created internally when no\n * `pglite` option was supplied; otherwise the caller-supplied instance.\n * Exposed so scripts can reach `pglite.dataDir`, `pglite.waitReady`, and\n * pass the same handle to helpers like {@link pushMigrations} without\n * threading a separate variable.\n */\n readonly pglite: PGlite | PGliteInterface;\n\n readonly #options: RequiredBy<Omit<PGliteServerOptions, 'pglite'>, 'host' | 'port' | 'user'>;\n readonly #server: net.Server;\n readonly #sessionLock = new SessionLock();\n readonly #sockets = new Set<BridgedSocket>();\n readonly #ownsPglite: boolean;\n\n #connectionString: string | undefined;\n\n constructor(options: PGliteServerOptions = {}) {\n const { pglite, ...rest } = options;\n\n this.#ownsPglite = !pglite;\n this.pglite = pglite ?? new PGlite();\n this.#options = {\n ...rest,\n host: rest.host || '127.0.0.1',\n port: rest.port ?? (!rest.dataDir ? 0 : DEFAULT_SOCKET_PORT),\n user: rest.user ? encodeURIComponent(rest.user) : 'postgres',\n };\n this.#server = net.createServer((socket) => this.#onConnection(socket));\n }\n\n listen = async (): Promise<string> => {\n if (this.#connectionString) return this.#connectionString;\n if (this.pglite.closed) {\n throw new Error('PGliteServer requires an open PGlite instance; got a closed one.');\n }\n\n return new Promise<string>((resolve, reject) => {\n const onError = (err: Error): void => reject(err);\n this.#server.once('error', onError);\n\n // Socket\n if (this.#options.dataDir) {\n const { dataDir, port, user } = this.#options;\n\n this.#server.listen(nodePath.join(dataDir, `.s.PGSQL.${port}`), () => {\n this.#server.removeListener('error', onError);\n this.#connectionString = `postgres://${user}@/postgres?host=${encodeURIComponent(dataDir)}&port=${port}`;\n return resolve(this.#connectionString);\n });\n\n return;\n }\n\n // TCP\n this.#server.listen(this.#options.port, this.#options.host, () => {\n this.#server.removeListener('error', onError);\n\n const { user } = this.#options;\n // Inside the TCP listen callback the server is bound to a port, so\n // address() is always AddressInfo — never the Unix-socket string or the\n // pre-listen null that Node's union return type also allows.\n const { address, family, port } = this.#server.address() as net.AddressInfo;\n\n this.#connectionString =\n family === 'IPv6'\n ? `postgres://${user}@/postgres?host=${encodeURIComponent(address)}&port=${port}`\n : `postgres://${user}@${address}:${port}/postgres`;\n this.#options.port = port; // re-assign used port\n\n return resolve(this.#connectionString);\n });\n });\n };\n\n /**\n * Shut down the listener. When the server created its own PGlite (no\n * `pglite` option at construction), also closes that instance. When the\n * caller supplied a `pglite`, it is left open — the caller is responsible\n * for closing it.\n */\n close = async (): Promise<void> => {\n const sockets = [...this.#sockets];\n for (const socket of sockets) socket.destroy();\n await new Promise<void>((resolve, reject) => {\n this.#server.close((err) => (err ? reject(err) : resolve()));\n });\n await Promise.all(sockets.map(({ duplex }) => duplex?.onClose));\n if (this.#ownsPglite && !this.pglite.closed) {\n await this.pglite.close();\n }\n };\n\n #initDuplex(socket: BridgedSocket): PGliteDuplex {\n socket.duplex = new PGliteDuplex(this.pglite, {\n sessionLock: this.#sessionLock,\n timeout: this.#options.timeout,\n syncToFs: resolveSyncToFs(this.pglite, this.#options.syncToFs),\n // Native clients (Prisma CLI engine, psql, GUIs) need real catalog\n // OIDs; the 18→25 widening is an @prisma/adapter-pg accommodation that\n // belongs to the bridge path only. With it, the CLI engine misreads\n // pg_constraint.contype as text and fails on PostgreSQL 18's\n // NOT NULL rows (contype 'n') — see prisma/prisma#29635.\n rewriteSystemCatalogCharOids: false,\n });\n socket.duplex.on('error', () => socket.destroy());\n socket.once('close', () => {\n const { duplex } = socket;\n if (duplex && !duplex.destroyed) duplex.destroy();\n });\n socket.pipe(socket.duplex).pipe(socket);\n return socket.duplex;\n }\n\n #onConnection(socket: BridgedSocket): void {\n socket.setNoDelay(true);\n this.#sockets.add(socket);\n socket.on('close', () => this.#sockets.delete(socket));\n socket.on('error', () => socket.destroy());\n\n let buffer: Buffer = Buffer.alloc(0);\n\n const onData = (chunk: Buffer): void => {\n buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk]);\n\n // Drain SSL/GSS preludes; libpq may chain them before StartupMessage.\n while (buffer.length >= PRELUDE_HEADER_BYTES) {\n const len = buffer.readInt32BE(0);\n const code = buffer.readInt32BE(4);\n if (len === 8 && (code === SSL_REQUEST_CODE || code === GSSENC_REQUEST_CODE)) {\n socket.write('N');\n buffer = buffer.subarray(8);\n continue;\n }\n break;\n }\n if (buffer.length < PRELUDE_HEADER_BYTES) return;\n\n const len = buffer.readInt32BE(0);\n const code = buffer.readInt32BE(4);\n\n if (len === 16 && code === CANCEL_REQUEST_CODE) {\n if (buffer.length < 16) return;\n socket.removeListener('data', onData);\n socket.end();\n return;\n }\n\n socket.removeListener('data', onData);\n\n // Hand over to duplex stream\n socket.pause();\n this.#initDuplex(socket).write(buffer);\n socket.resume();\n };\n\n socket.on('data', onData);\n }\n}\n","/**\n * Workaround for a Prisma schema-engine panic on PostgreSQL 18.\n *\n * PostgreSQL 18 represents column NOT NULL constraints as `pg_constraint`\n * rows with `contype = 'n'` (new in PG 18). The engine's constraint\n * introspection query (sql-schema-describer constraints_query.sql) filters\n * with the denylist `contype NOT IN ('p', 'u', 'f')`, so those rows reach a\n * row-parsing path that panics (\"…as char failed\") and `schemaPush` never\n * settles. Verified against @prisma/schema-engine-wasm\n * 7.8.0-6.3c6e1927 — the latest published build; no upstream fix exists.\n * Remove once the pinned engine handles contype 'n'.\n *\n * Appending 'n' to the denylist makes PG 18 introspection look like PG ≤ 17,\n * where NOT NULL never appears in pg_constraint — which is also what the\n * engine expects: column nullability is read from attnotnull, not from\n * constraint rows. On PG ≤ 17 the value never occurs, so the rewrite is a\n * semantic no-op there and needs no server-version gate.\n */\nimport type {\n SqlDriverAdapter,\n SqlMigrationAwareDriverAdapterFactory,\n SqlQuery,\n} from '@prisma/driver-adapter-utils';\n\nconst ENGINE_DENYLIST = \"contype NOT IN ('p', 'u', 'f')\";\nconst PATCHED_DENYLIST = \"contype NOT IN ('p', 'u', 'f', 'n')\";\n\nexport const rewritePg18ConstraintsSql = (sql: string): string => {\n if (!sql.includes('pg_constraint') || !sql.includes(ENGINE_DENYLIST)) {\n return sql;\n }\n return sql.replace(ENGINE_DENYLIST, PATCHED_DENYLIST);\n};\n\n/**\n * Proxies (rather than spreads) so class-based adapters keep working: every\n * untouched member is delegated with `this` bound to the original instance,\n * and members added in future @prisma/driver-adapter-utils versions pass\n * through without changes here.\n */\nconst wrapAdapter = (adapter: SqlDriverAdapter): SqlDriverAdapter =>\n new Proxy(adapter, {\n get(target, prop) {\n if (prop === 'queryRaw') {\n return (query: SqlQuery) =>\n target.queryRaw({ ...query, sql: rewritePg18ConstraintsSql(query.sql) });\n }\n const value = Reflect.get(target, prop);\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n\nexport const wrapFactoryForPg18 = (\n factory: SqlMigrationAwareDriverAdapterFactory,\n): SqlMigrationAwareDriverAdapterFactory =>\n new Proxy(factory, {\n get(target, prop) {\n if (prop === 'connect') {\n return async () => wrapAdapter(await target.connect());\n }\n if (prop === 'connectToShadowDb') {\n return async () => wrapAdapter(await target.connectToShadowDb());\n }\n const value = Reflect.get(target, prop);\n return typeof value === 'function' ? value.bind(target) : value;\n },\n });\n","/**\n * Apply a Prisma schema to a PGlite database in-process via\n * `@prisma/schema-engine-wasm`. No native schema-engine binary, no TCP.\n *\n * `pushSchema` mirrors `prisma db push --skip-generate`: diff the schema\n * against the live database and apply the result. `resetSchema` drops\n * everything reachable through the connection.\n *\n * The schema engine WASM module is dynamically imported so consumers\n * who only use the bridge never load it.\n *\n * @example\n * ```typescript\n * import { PGliteBridge, pushSchema } from 'prisma-pglite-bridge';\n *\n * const bridge = new PGliteBridge();\n *\n * await pushSchema(bridge.adapter, {\n * schema: await fs.readFile('prisma/schema.prisma', 'utf8'),\n * });\n *\n * // teardown\n * await bridge.close(); // closes pool + pglite (bridge owns it)\n * ```\n */\nimport type { PrismaPg } from '@prisma/adapter-pg';\nimport { quoteIdent } from '../utils/quote-ident.ts';\nimport { wrapFactoryForPg18 } from './pg18-not-null.ts';\n\nexport interface PushSchemaOptions {\n /** Inline Prisma schema source. */\n schema: string;\n /**\n * Drop everything reachable through the adapter before applying.\n * Issued as a separate `engine.reset(...)` call. Distinct from\n * {@link acceptDataLoss}. Default: `false`.\n */\n forceReset?: boolean;\n /**\n * Forwarded to `SchemaPushInput.force`. Apply the schema even when\n * the engine reports destructive-change warnings. Has no effect on\n * `unexecutable` steps. Default: `false`.\n */\n acceptDataLoss?: boolean;\n /** Logical filename used in schema-engine error messages. Default: `'schema.prisma'`. */\n filename?: string;\n}\n\nexport interface PushSchemaResult {\n /** Number of migration steps the engine applied. */\n executedSteps: number;\n /**\n * Destructive-change warnings reported by the engine. Suppressed\n * (i.e. applied anyway) when {@link PushSchemaOptions.acceptDataLoss} is true.\n */\n warnings: string[];\n /**\n * Steps the engine refused to run regardless of `acceptDataLoss`.\n * The caller must reshape the schema (e.g. add a default value) and retry.\n */\n unexecutable: string[];\n}\n\nconst bindAdapter = async (adapter: PrismaPg): Promise<object> => {\n const { bindMigrationAwareSqlAdapterFactory } = await import('@prisma/driver-adapter-utils');\n // wrapFactoryForPg18 shields the engine from PostgreSQL 18's contype 'n'\n // rows, which panic its constraint introspection — see pg18-not-null.ts.\n return bindMigrationAwareSqlAdapterFactory(wrapFactoryForPg18(adapter));\n};\n\nconst emptyFilter = (): { externalTables: string[]; externalEnums: string[] } => ({\n externalTables: [],\n externalEnums: [],\n});\n\n/**\n * Drop every non-system schema (and recreate `public`). Issued as raw SQL\n * through the adapter rather than `engine.reset(...)` because the engine only\n * clears schemas declared in its datamodel — anything outside that (`base`,\n * test fixtures, etc.) would otherwise leak between resets.\n *\n * Each DROP runs through `executeRaw` rather than a single `executeScript`,\n * because `executeScript` splits on `;` and would mis-parse schema names that\n * contain a semicolon.\n */\nconst dropAllUserSchemas = async (adapter: PrismaPg): Promise<void> => {\n const conn = await adapter.connect();\n try {\n const result = await conn.queryRaw({\n sql: `SELECT nspname FROM pg_namespace\n WHERE nspname NOT LIKE 'pg_%' AND nspname <> 'information_schema'`,\n args: [],\n argTypes: [],\n });\n const idx = result.columnNames.indexOf('nspname');\n const names = result.rows.map((row) => String(row[idx]));\n for (const name of names) {\n await conn.executeRaw({\n sql: `DROP SCHEMA IF EXISTS ${quoteIdent(name)} CASCADE`,\n args: [],\n argTypes: [],\n });\n }\n await conn.executeRaw({\n sql: `CREATE SCHEMA IF NOT EXISTS \"public\"`,\n args: [],\n argTypes: [],\n });\n } finally {\n await conn.dispose();\n }\n};\n\nexport const pushSchema = async (\n adapter: PrismaPg,\n options: PushSchemaOptions,\n): Promise<PushSchemaResult> => {\n const filename = options.filename ?? 'schema.prisma';\n if (options.forceReset) {\n await dropAllUserSchemas(adapter);\n }\n const { SchemaEngine } = await import('@prisma/schema-engine-wasm');\n const bound = await bindAdapter(adapter);\n\n const engine = await SchemaEngine.new(\n { datamodels: [[filename, options.schema]] },\n () => {},\n bound,\n );\n try {\n const result = await engine.schemaPush({\n force: options.acceptDataLoss ?? false,\n schema: { files: [{ path: filename, content: options.schema }] },\n filters: emptyFilter(),\n });\n return {\n executedSteps: result.executedSteps,\n warnings: result.warnings,\n unexecutable: result.unexecutable,\n };\n } finally {\n engine.free();\n }\n};\n\nexport const resetSchema = async (adapter: PrismaPg): Promise<void> => {\n await dropAllUserSchemas(adapter);\n};\n","/**\n * Apply pre-generated SQL (raw or from `prisma/migrations/`) to a PGlite\n * database. Sibling of {@link pushSchema} — both populate a database, but\n * `pushMigrations` takes a `PGlite` directly and bypasses\n * `@prisma/schema-engine-wasm`. Use when you already have generated SQL\n * and don't need a live schema diff.\n *\n * @example\n * ```typescript\n * import { PGlite } from '@electric-sql/pglite';\n * import { pushMigrations } from 'prisma-pglite-bridge';\n *\n * const pglite = new PGlite();\n * await pushMigrations(pglite, { migrationsPath: './prisma/migrations' });\n * ```\n */\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { PGlite, PGliteInterface } from '@electric-sql/pglite';\n\nexport interface PushMigrationsOptions {\n /** Pre-generated SQL to apply directly. */\n sql?: string;\n /** Path to a `prisma/migrations/` directory (auto-discovered via prisma.config.ts if omitted). */\n migrationsPath?: string;\n /** Root for prisma.config.ts discovery (default: process.cwd()). Set in monorepos where tests run from the workspace root. */\n configRoot?: string;\n}\n\nexport interface PushMigrationsResult {\n /** Wall-clock time of the SQL apply, in milliseconds. */\n durationMs: number;\n}\n\n/**\n * Resolve the migrations directory via Prisma's config API. Uses the same\n * resolution as `prisma migrate dev` — reads prisma.config.ts and resolves\n * paths relative to the config file's location.\n *\n * Returns undefined if @prisma/config is not available or the config\n * cannot be loaded.\n */\nexport const getMigrationsPath = async (configRoot?: string): Promise<string | undefined> => {\n try {\n const { loadConfigFromFile } = await import('@prisma/config');\n const { config, error } = await loadConfigFromFile({ configRoot: configRoot ?? process.cwd() });\n if (error) return undefined;\n\n if (config.migrations?.path) return config.migrations.path;\n\n const schemaPath = config.schema;\n if (schemaPath) return join(dirname(schemaPath), 'migrations');\n\n return undefined;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Read and concatenate every `migration.sql` under a migrations directory in\n * directory order. Returns undefined if the directory doesn't exist or has no\n * migration files.\n */\nexport const readMigrationFiles = (migrationsPath: string): string | undefined => {\n if (!existsSync(migrationsPath)) return undefined;\n\n const dirs = readdirSync(migrationsPath)\n .filter((directory) => statSync(join(migrationsPath, directory)).isDirectory())\n .sort();\n\n const sqlParts: string[] = [];\n for (const directory of dirs) {\n const sqlPath = join(migrationsPath, directory, 'migration.sql');\n if (existsSync(sqlPath)) {\n sqlParts.push(readFileSync(sqlPath, 'utf8'));\n }\n }\n\n return sqlParts.length > 0 ? sqlParts.join('\\n') : undefined;\n};\n\n/**\n * Resolve schema SQL from {@link PushMigrationsOptions}. Priority:\n * 1. Explicit `sql`\n * 2. Explicit `migrationsPath` — read migration files\n * 3. Auto-discovered migrations via prisma.config.ts\n * 4. Throw — tell the caller to generate migration files\n */\nexport const getMigrationSQL = async (options: PushMigrationsOptions): Promise<string> => {\n if (options.sql) return options.sql;\n\n if (options.migrationsPath) {\n const sql = readMigrationFiles(options.migrationsPath);\n if (sql) return sql;\n throw new Error(\n `No migration.sql files found in ${options.migrationsPath}. Run \\`prisma migrate dev\\` to generate migration files.`,\n );\n }\n\n const migrationsPath = await getMigrationsPath(options.configRoot);\n if (migrationsPath) {\n const sql = readMigrationFiles(migrationsPath);\n if (sql) return sql;\n\n throw new Error(\n `No migration.sql files found in auto-discovered path ${migrationsPath}. ` +\n 'Run `prisma migrate dev` to generate migration files, ' +\n 'or pass pre-generated SQL via the `sql` option.',\n );\n }\n\n if (options.configRoot) {\n throw new Error(\n `prisma.config.ts loaded from configRoot (${options.configRoot}) but no schema ` +\n 'or migrations path could be resolved. Ensure your config specifies a schema path, ' +\n 'or pass pre-generated SQL via the `sql` option.',\n );\n }\n\n throw new Error(\n 'No migration files found and no prisma.config.ts could be loaded. ' +\n 'Run `prisma migrate dev` to generate them, ' +\n 'or pass pre-generated SQL via the `sql` option.',\n );\n};\n\n/**\n * Apply pre-generated SQL to a PGlite instance.\n *\n * Runs the SQL through `pglite.exec(...)` directly, bypassing any bridge\n * pool. No schema engine, no WASM module, no diffing — useful when you\n * already have a `prisma/migrations` directory or pre-generated SQL.\n *\n * Pass the same PGlite instance you handed to {@link PGliteBridge}\n * (i.e. `bridge.pglite`) — or any standalone `PGlite` you own.\n */\nexport const pushMigrations = async (\n pglite: PGlite | PGliteInterface,\n options: PushMigrationsOptions = {},\n): Promise<PushMigrationsResult> => {\n const sql = await getMigrationSQL(options);\n const start = process.hrtime.bigint();\n try {\n await pglite.exec(sql);\n } catch (err) {\n const where = pglite.dataDir ? `PGlite(dataDir=${pglite.dataDir})` : 'in-memory PGlite';\n throw new Error(\n `Failed to apply schema SQL to ${where}. Check your schema or migration files.`,\n { cause: err },\n );\n }\n return { durationMs: Number(process.hrtime.bigint() - start) / 1e6 };\n};\n\n/**\n * Returns `true` when the `_prisma_migrations` table exists and has at\n * least one row with `finished_at IS NOT NULL`. Useful as a \"first run\"\n * guard for persistent dataDirs:\n *\n * ```typescript\n * if (!(await hasMigrations(pglite))) {\n * await pushMigrations(pglite, { migrationsPath: './prisma/migrations' });\n * }\n * ```\n *\n * Awaits `pglite.waitReady` implicitly via `pglite.query(...)`. Detects only\n * Prisma-managed migrations — `pushSchema` (WASM diff) does not populate\n * `_prisma_migrations`, so this returns `false` for adapter-applied schemas.\n */\nexport const hasMigrations = async (pglite: PGlite | PGliteInterface): Promise<boolean> => {\n const { rows } = await pglite.query<{ exists: boolean }>(\n `SELECT to_regclass('public._prisma_migrations') IS NOT NULL AS exists`,\n );\n if (!rows[0]?.exists) return false;\n\n const { rows: applied } = await pglite.query<{ count: number }>(\n `SELECT count(*)::int AS count FROM _prisma_migrations WHERE finished_at IS NOT NULL`,\n );\n return (applied[0]?.count ?? 0) > 0;\n};\n\n/**\n * Returns `true` when the `public` schema contains at least one user table.\n * Broader sibling of {@link hasMigrations} — fires for any DDL, regardless of\n * whether it came from {@link pushMigrations}, {@link pushSchema}, or hand-rolled\n * SQL. Use as a \"first run\" guard when you are not using a Prisma migrations\n * directory:\n *\n * ```typescript\n * if (!(await hasSchema(pglite))) {\n * await pushSchema(bridge.adapter, { schema });\n * }\n * ```\n *\n * Awaits `pglite.waitReady` implicitly via `pglite.query(...)`. The internal\n * `_pglite_snapshot` schema used by `bridge.snapshotDb()` is excluded — only\n * the `public` schema is inspected.\n */\nexport const hasSchema = async (pglite: PGlite | PGliteInterface): Promise<boolean> => {\n const { rows } = await pglite.query<{ exists: boolean }>(\n `SELECT EXISTS (\n SELECT 1 FROM information_schema.tables\n WHERE table_schema = 'public' AND table_type = 'BASE TABLE'\n ) AS exists`,\n );\n return rows[0]?.exists === true;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,gBAAgB;;;;;;AAO7B,MAAa,oBAAoB;AAoBjC,MAAa,eAA4CA,yBAAAA,QAAoB,QAAQ,aAAa;AAClG,MAAa,kBACXA,yBAAAA,QAAoB,QAAQ,iBAAiB;;;;ACnD/C,MAAa,UAAU,OAAuB,OAAO,EAAE,IAAI;;;ACC3D,MAAM,cAAc,OAAU,SAAqB,OAA2B;CAC5E,IAAI;CAEJ,MAAM,UAAU,IAAI,SAAgB,GAAG,WAAW;EAChD,QAAQ,iBAAiB;GACvB,uBAAO,IAAI,MAAM,6BAA6B,GAAG,GAAG,CAAC;EACvD,GAAG,EAAE;CACP,CAAC;CAED,OAAO,QAAQ,KAAK,CAAC,SAAS,OAAO,CAAC,EAAE,cAAc;EACpD,aAAa,KAAK;CACpB,CAAC;AACH;AAEA,MAAa,kBAAkB,OAC7B,QACA,KAAa,OAAO,sBACF;CAClB,IAAI,OAAO,OAAO;CAClB,IAAI,OAAO,QAAQ,MAAM,IAAI,MAAM,wBAAwB;CAE3D,IAAI;EACF,OAAO,OAAO,SAAS,EAAE,IAAI,YAAY,OAAO,WAAW,EAAE,IAAI,OAAO;CAC1E,SAAS,OAAgB;EACvB,MAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACjE,MAAM,IAAI,MAAM,8BAA8B,KAAK;CACrD;AACF;ACQA,MAAa,eAAoC,IAAI,IAAI;CACvD;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;;AAQD,MAAa,6BAA6B;;;AClD1C,MAAM,gBAAgB,KAAiB,WAA2B;;CAEhE,MAAM,KAAK,IAAI,WAAW;CAC1B,MAAM,KAAK,IAAI,SAAS,MAAM;CAC9B,MAAM,KAAK,IAAI,SAAS,MAAM;CAC9B,MAAM,KAAK,IAAI,SAAS,MAAM;;CAE9B,QAAS,MAAM,KAAO,MAAM,KAAO,MAAM,IAAK,QAAQ;AACxD;AAEA,MAAM,gBAAgB,KAAiB,WAA2B;;CAEhE,MAAM,KAAK,IAAI,WAAW;CAC1B,MAAM,KAAK,IAAI,SAAS,MAAM;;CAE9B,OAAQ,MAAM,IAAK;AACrB;AAEA,MAAa,8BACX,KACA,QAAgB,GAChB,MAAc,IAAI,WACN;CACZ,IAAI,MAAM,QAAQ,GAAG,OAAO;CAE5B,MAAM,aAAa,aAAa,KAAK,QAAQ,CAAC;CAC9C,IAAI,IAAI,QAAQ;CAChB,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;EACnC,OAAO,IAAI,OAAO,IAAI,OAAO,GAAG;EAChC;;EAEA,IAAI,IAAI,KAAK,KAAK,OAAO;EACzB,MAAM,WAAW,aAAa,KAAK,CAAC;EACpC,KAAK;EAEL,IADY,aAAa,KAAK,CACxB,MAAA,MAA0B,aAAa,KAAK,WAAA,OAChD,OAAO;EAET,KAAK;CACP;CAEA,OAAO;AACT;;;;;;;;;;;;AAaA,MAAa,gCAAgC,QAAsB;CACjE,IAAI,CAAC,2BAA2B,GAAG,GAAG;CAEtC,MAAM,aAAa,IAAI,YAAY,CAAC;CACpC,IAAI,IAAI;CACR,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;EACnC,OAAO,IAAI,IAAI,UAAU,IAAI,OAAO,GAAG;EACvC;;EAKA,IAAI,IAAI,KAAK,IAAI,QAAQ;EACzB,MAAM,WAAW,IAAI,aAAa,CAAC;EACnC,KAAK;EAEL,IADY,IAAI,aAAa,CACvB,MAAA,MAA0B,aAAa,KAAK,WAAA,OAA8B;GAC9E,IAAI,cAAA,IAAgC,CAAC;GACrC,IAAI,aAAa,IAAI,IAAI,CAAC;EAC5B;EACA,KAAK;CACP;AACF;;;;;;;;;;;;AC/CA,IAAa,uBAAb,MAAkC;CAChC;CACA;CACA;CACA;CACA;CACA,gBAAiC,IAAI,WAAW,CAAC;CACjD,UAA2B,IAAI,WAAW,CAAC;CAC3C;CACA,kBAA0B;CAC1B,wBAAgC;CAChC,eAAuB;;;CAGvB;CACA,gBAAwB;CAExB,YAAY,SAAsC;EAChD,KAAK,oCAAoC,QAAQ,qCAAqC;EACtF,KAAK,+BAA+B,QAAQ,gCAAgC;EAC5E,KAAK,UAAU,QAAQ;EACvB,KAAK,kBAAkB,QAAQ;EAC/B,KAAK,kBAAkB,QAAQ;CACjC;CAEA,MAAM,OAAyB;EAC7B,IAAI,MAAM,WAAW,GAAG;EAExB,IAAI,SAAS;EACb,IAAI,mBAAmB;EACvB,MAAM,oBAAoB,QAAsB;GAC9C,IAAI,oBAAoB,KAAK,MAAM,kBAAkB;IACnD,KAAK,eAAe,OAAO,kBAAkB,GAAG;IAChD,mBAAmB;GACrB;EACF;EACA,OAAO,SAAS,MAAM,QAAQ;GAC5B,IAAI,KAAK,gBAAgB,KAAA,GAAW;IAMlC,MAAM,YAAY,MAAM,SAAS;IACjC,IAAI,aAAa,GAAG;;KAElB,MAAM,UAAU,MAAM,WAAW;KACjC,MAAM,KAAK,MAAM,SAAS,MAAM;KAChC,MAAM,KAAK,MAAM,SAAS,MAAM;KAChC,MAAM,KAAK,MAAM,SAAS,MAAM;KAChC,MAAM,KAAK,MAAM,SAAS,MAAM;;KAEhC,MAAM,iBAAkB,MAAM,KAAO,MAAM,KAAO,MAAM,IAAK,QAAQ;KACrE,IAAI,gBAAgB,GAClB,MAAM,IAAI,MAAM,qCAAqC,eAAe;KAEtE,IAAI,gBAAA,YACF,MAAM,IAAI,MACR,0BAA0B,cAAc,sBAAsB,4BAChE;KAEF,MAAM,WAAW,IAAI;KACrB,IAAI,aAAa,UAAU;MACzB,IAAI,YAAA,IACF,KAAK,kBAAkB;MAEzB,IAAI,YAAA,MAA+B,kBAAkB,GAAG;OACtD,iBAAiB,MAAM;OACvB,KAAK,2BAA2B;;OAEhC,MAAM,SAAS,MAAM,SAAS,MAAM;OACpC,KAAK,QAAQ,KAAK;OAClB,KAAK,QAAQ,KAAK;OAClB,KAAK,QAAQ,KAAK;OAClB,KAAK,QAAQ,KAAK;OAClB,KAAK,QAAQ,KAAK;OAClB,KAAK,QAAQ,KAAK;OAClB,KAAK,eAAe;OACpB,KAAK,kBAAkB,MAAM;OAC7B,IAAI,CAAC,KAAK,mCAAmC;QAC3C,KAAK,kBAAkB;QACvB,KAAK,eAAe;OACtB;MACF,OAAO,IACL,YAAA,MACA,KAAK,gCACL,2BAA2B,OAAO,QAAQ,SAAS,QAAQ,GAC3D;OACA,iBAAiB,MAAM;OACvB,KAAK,2BAA2B;OAChC,KAAK,4BACH,OAAO,KAAK,MAAM,SAAS,QAAQ,SAAS,QAAQ,CAAC,CACvD;MACF,OAAO;OACL,KAAK,2BAA2B;OAChC,IAAI,mBAAmB,GACrB,mBAAmB;MAEvB;MACA,UAAU;MACV;KACF;IACF;IAEA,iBAAiB,MAAM;IACvB,KAAK,2BAA2B;;IAEhC,KAAK,cAAc,MAAM,WAAW;IACpC,KAAK,kBAAkB;IACvB,KAAK,wBAAwB;IAC7B,KAAK,eAAe,KAAK,gBAAA,KAAkC,IAAI;IAC/D,IAAI,KAAK,iBAAiB,GACxB,KAAK,QAAQ,KAAK,KAAK;IAEzB;IACA;GACF;GAEA,IAAI,KAAK,kBAAkB,GAAG;IAC5B,MAAM,cAAc,KAAK,IAAI,IAAI,KAAK,iBAAiB,MAAM,SAAS,MAAM;IAC5E,MAAM,cAAc,MAAM,SAAS,QAAQ,SAAS,WAAW;IAC/D,KAAK,cAAc,IAAI,aAAa,KAAK,eAAe;IACxD,IAAI,KAAK,gBAAA,IAAiC;KACxC,KAAK,QAAQ,IAAI,aAAa,KAAK,YAAY;KAC/C,KAAK,gBAAgB;IACvB;IACA,KAAK,mBAAmB;IACxB,UAAU;IACV,IAAI,KAAK,kBAAkB,GAAG;;IAG9B,MAAM,KAAK,KAAK,cAAc,MAAM;IACpC,MAAM,KAAK,KAAK,cAAc,MAAM;IACpC,MAAM,KAAK,KAAK,cAAc,MAAM;IACpC,MAAM,KAAK,KAAK,cAAc,MAAM;;IAEpC,MAAM,iBAAkB,MAAM,KAAO,MAAM,KAAO,MAAM,IAAK,QAAQ;IACrE,IAAI,gBAAgB,GAClB,MAAM,IAAI,MAAM,qCAAqC,eAAe;IAEtE,IAAI,gBAAA,YACF,MAAM,IAAI,MACR,0BAA0B,cAAc,sBAAsB,4BAChE;IAGF,KAAK,wBAAwB,gBAAgB;IAE7C,IAAI,KAAK,gBAAA,IACP,KAAK,kBAAkB;IAGzB,IAAI,KAAK,qBAAqB,GAC5B;IAGF,KAAK,sBAAsB;IAC3B,IAAI,KAAK,gBAAA,MAAmC,KAAK,8BAA8B;KAG7E,KAAK,gBAAgB,OAAO,MAAM,IAAI,KAAK,qBAAqB;KAChE,KAAK,cAAc,KAAA;KACnB,KAAK,cAAc,IAAI,KAAK,eAAe,CAAC;KAC5C,KAAK,gBAAgB;KACrB;IACF;IACA,KAAK,WAAW;IAChB,IAAI,KAAK,0BAA0B,GACjC,KAAK,cAAc;IAErB;GACF;GAEA,IAAI,KAAK,qBAAqB,GAAG;IAC/B,MAAM,cAAc,KAAK,IAAI,KAAK,uBAAuB,MAAM,SAAS,MAAM;IAC9E,MAAM,eAAe,MAAM,SAAS,QAAQ,SAAS,WAAW;IAChE,KAAK,QAAQ,IAAI,cAAc,KAAK,YAAY;IAChD,KAAK,gBAAgB;IACrB,KAAK,yBAAyB;IAC9B,UAAU;;IAEV,IAAI,KAAK,0BAA0B,GACjC,KAAK,oBAAoB;IAE3B;GACF;GAEA,MAAM,cAAc,KAAK,IAAI,KAAK,uBAAuB,MAAM,SAAS,MAAM;;GAE9E,IAAI,cAAc,GAAG;IACnB,IAAI,KAAK,kBAAkB,KAAA,GAAW;KACpC,KAAK,cAAc,IAAI,MAAM,SAAS,QAAQ,SAAS,WAAW,GAAG,KAAK,aAAa;KACvF,KAAK,iBAAiB;IACxB,OACE,KAAK,eAAe,OAAO,QAAQ,SAAS,WAAW;IAEzD,KAAK,yBAAyB;IAC9B,UAAU;GACZ;GACA,IAAI,KAAK,0BAA0B,GAAG;IACpC,IAAI,KAAK,kBAAkB,KAAA,GAAW;KACpC,MAAM,MAAM,KAAK;KACjB,KAAK,gBAAgB,KAAA;KACrB,KAAK,4BAA4B,GAAG;IACtC;IACA,KAAK,cAAc;GACrB;EACF;EAEA,iBAAiB,MAAM;CACzB;CAEA,MAAM,SAAqD;EACzD,IAAI,SAAS,0BAA0B,MACrC,KAAK,sBAAsB;OACtB,IAAI,KAAK,qCAAqC,KAAK,iBAAiB,GAAG;GAC5E,KAAK,kBAAkB;GACvB,KAAK,eAAe;EACtB;CACF;CAEA,uBAAwC;EACtC,OAAO,KAAK,gBAAA,MAAmC,KAAK,0BAA0B;CAChF;CAEA,sBAAoC;EAClC,MAAM,SAAS,KAAK,QAAQ;;EAE5B,IAAI,WAAW,KAAA,GACb,KAAK,kBAAkB,MAAM;EAG/B,IAAI,CAAC,KAAK,mCACR,KAAK,kBAAkB;EAGzB,KAAK,cAAc;CACrB;CAEA,oBAAkC;EAChC,KAAK,QAAQ,KAAK,QAAQ,MAAM,GAAG,CAAC,CAAC;CACvC;CAEA,wBAAsC;EACpC,KAAK,eAAe;CACtB;;;CAIA,6BAA2C;EACzC,IAAI,KAAK,qCAAqC,KAAK,iBAAiB,GAClE,KAAK,sBAAsB;CAE/B;CAEA,aAA2B;EACzB,MAAM,SAAS,IAAI,WAAW,CAAC;;EAE/B,OAAO,KAAK,KAAK,eAAe;EAChC,OAAO,IAAI,KAAK,eAAe,CAAC;EAChC,KAAK,QAAQ,MAAM;CACrB;CAEA,4BAAoC,KAAmB;EACrD,6BAA6B,GAAG;EAChC,KAAK,QAAQ,GAAG;CAClB;CAEA,eAAuB,OAAmB,OAAe,KAAmB;EAC1E,MAAM,SAAS,MAAM;;EAErB,IAAI,UAAU,GAAG;EAQjB,IACE,MAAM,eAAe,KACrB,MAAM,eAAe,MAAM,OAAO,cAClC,EAAE,MAAM,kBAAkB,oBAC1B;GACA,KAAK,QAAQ,OAAO,KAAK,MAAM,QAAQ,OAAO,MAAM,CAAC;GACrD;EACF;EAEA,MAAM,QAAQ,OAAO,KAAK,MAAM,SAAS,OAAO,GAAG,CAAC;EACpD,KAAK,QAAQ,KAAK;CACpB;CAEA,gBAA8B;EAC5B,KAAK,cAAc,KAAA;EACnB,KAAK,kBAAkB;EACvB,KAAK,wBAAwB;EAC7B,IAAI,CAAC,KAAK,mCACR,KAAK,eAAe;CAExB;AACF;;;;;;;;;ACtUA,IAAa,wBAAb,MAAmC;CACjC,SAA+B,CAAC;CAChC,YAAoB;CACpB,aAAqB;CACrB,cAAsB;CAEtB,IAAI,SAAiB;EACnB,OAAO,KAAK;CACd;CAEA,KAAK,OAAyB;EAC5B,IAAI,MAAM,WAAW,GAAG;EACxB,KAAK,OAAO,KAAK,KAAK;EACtB,KAAK,eAAe,MAAM;CAC5B;CAEA,QAAc;EACZ,KAAK,SAAS,CAAC;EACf,KAAK,YAAY;EACjB,KAAK,aAAa;EAClB,KAAK,cAAc;CACrB;CAEA,YAAY,QAAoC;EAC9C,IAAI,SAAS,KAAK,SAAS,IAAI,KAAK,aAAa,OAAO,KAAA;EAExD,MAAM,OAAO,KAAK,OAAO,KAAK;;EAE9B,IAAI,SAAS,KAAA,GAAW;GACtB,MAAM,QAAQ,KAAK,aAAa;GAChC,IAAI,QAAQ,KAAK,KAAK,QAAQ;;IAE5B,MAAM,KAAK,KAAK,UAAU;IAC1B,MAAM,KAAK,KAAK,QAAQ,MAAM;IAC9B,MAAM,KAAK,KAAK,QAAQ,MAAM;IAC9B,MAAM,KAAK,KAAK,QAAQ,MAAM;;IAE9B,QAAS,MAAM,KAAO,MAAM,KAAO,MAAM,IAAK,QAAQ;GACxD;EACF;EAEA,IAAI,YAAY,KAAK,aAAa;EAClC,MAAM,QAAQ,IAAI,WAAW,CAAC;EAC9B,IAAI,cAAc;EAElB,KAAK,IAAI,IAAI,KAAK,WAAW,IAAI,KAAK,OAAO,UAAU,cAAc,GAAG,KAAK;GAC3E,MAAM,QAAQ,KAAK,OAAO;;GAE1B,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;GAChC,IAAI,aAAa,MAAM,QAAQ;IAC7B,aAAa,MAAM;IACnB;GACF;GAEA,MAAM,cAAc,KAAK,IAAI,IAAI,aAAa,MAAM,SAAS,SAAS;GACtE,MAAM,IAAI,MAAM,SAAS,WAAW,YAAY,WAAW,GAAG,WAAW;GACzE,eAAe;GACf,YAAY;EACd;;EAGA,MAAM,KAAK,MAAM,MAAM;EACvB,MAAM,KAAK,MAAM,MAAM;EACvB,MAAM,KAAK,MAAM,MAAM;EACvB,MAAM,KAAK,MAAM,MAAM;;EAEvB,QAAS,MAAM,KAAO,MAAM,KAAO,MAAM,IAAK,QAAQ;CACxD;CAEA,QAAQ,QAA4B;EAClC,IAAI,SAAS,KAAK,SAAS,KAAK,aAC9B,MAAM,IAAI,MAAM,kBAAkB,OAAO,cAAc,KAAK,YAAY,aAAa;EAEvF,IAAI,WAAW,GAAG,OAAO,IAAI,WAAW,CAAC;EAEzC,MAAM,OAAO,KAAK,OAAO,KAAK;;EAE9B,IAAI,SAAS,KAAA;OACW,KAAK,SAAS,KAAK,cACpB,QAAQ;IAC3B,MAAM,QAAQ,KAAK,SAAS,KAAK,YAAY,KAAK,aAAa,MAAM;IACrE,KAAK,cAAc;IACnB,KAAK,eAAe;IACpB,IAAI,KAAK,eAAe,KAAK,QAAQ;KACnC,KAAK;KACL,KAAK,aAAa;KAClB,KAAK,cAAc;IACrB;IACA,OAAO;GACT;;EAGF,MAAM,SAAS,IAAI,WAAW,MAAM;EACpC,IAAI,cAAc;EAClB,IAAI,YAAY;EAEhB,OAAO,YAAY,GAAG;GACpB,MAAM,QAAQ,KAAK,OAAO,KAAK;;GAE/B,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MAAM,iCAAiC;GAEnD,MAAM,YAAY,MAAM,SAAS,KAAK;GACtC,MAAM,cAAc,KAAK,IAAI,WAAW,SAAS;GACjD,OAAO,IAAI,MAAM,SAAS,KAAK,YAAY,KAAK,aAAa,WAAW,GAAG,WAAW;GACtF,eAAe;GACf,aAAa;GACb,KAAK,cAAc;GACnB,KAAK,eAAe;GACpB,IAAI,KAAK,eAAe,MAAM,QAAQ;IACpC,KAAK;IACL,KAAK,aAAa;IAClB,KAAK,cAAc;GACrB;EACF;EAEA,OAAO;CACT;CAEA,gBAA8B;EAC5B,IAAI,KAAK,cAAc,KAAK,OAAO,QAAQ;GACzC,KAAK,SAAS,CAAC;GACf,KAAK,YAAY;GACjB;EACF;EAEA,IAAI,KAAK,aAAa,MAAM,KAAK,YAAY,KAAK,KAAK,OAAO,QAAQ;GACpE,KAAK,SAAS,KAAK,OAAO,MAAM,KAAK,SAAS;GAC9C,KAAK,YAAY;EACnB;CACF;AACF;;;;;;;;;;;;;;;;;;;;;AC9FA,MAAM,oBAAoB,IAAI,WAAW;;CAAY;CAAM;CAAM;CAAM;AAAI,CAAC;AAG5E,MAAM,6BAA6B,IAAI,OAAO;AAC9C,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;AA+D/B,IAAa,eAAb,cAAkCC,YAAAA,OAAO;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;;CAEA,QAAyB,IAAI,sBAAsB;CACnD,QAAyC;CACzC,WAAmB;CACnB,WAAmB;;CAEnB,aAA4D,CAAC;;CAE7D,WAAiC,CAAC;;;;CAIlC;;;;;;CAMA;;;CAGA;CACA,8BAAsC;CACtC,8BAAsC;CACtC,6BAAqC;;;CAGrC;;;;;CAMA,YAAY,QAAkC,UAA+B,CAAC,GAAG;EAC/E,MAAM;EACN,KAAK,SAAS;EACd,KAAK,cAAc,QAAQ;EAC3B,KAAK,WAAW,QAAQ;EACxB,KAAK,YAAY,QAAQ;EACzB,KAAK,UAAU,QAAQ;EACvB,KAAK,WAAW,QAAQ,YAAY;EACpC,KAAK,+BAA+B,QAAQ,gCAAgC;EAE5E,KAAK,WAAW,OAAO,QAAQ;EAC/B,KAAK,UAAU,IAAI,SAAe,YAAY,KAAK,KAAK,eAAe,QAAQ,CAAC,CAAC;CACnF;CAIA,UAAgB;EACd,mBAAmB,KAAK,KAAK,SAAS,CAAC;EACvC,OAAO;CACT;CAEA,eAAqB;EACnB,OAAO;CACT;CAEA,aAAmB;EACjB,OAAO;CACT;CAEA,aAAmB;EACjB,OAAO;CACT;CAEA,MAAY;EACV,OAAO;CACT;CAEA,QAAc;EACZ,OAAO;CACT;CAIA,QAAuB,CAEvB;CAEA,OACE,OACA,WACA,UACM;EACN,KAAK,MAAM,KAAK,KAAK;EACrB,KAAK,QAAQ,QAAQ;CACvB;;CAGA,QACE,QACA,UACM;EACN,KAAK,MAAM,EAAE,WAAW,QACtB,KAAK,MAAM,KAAK,KAAK;EAEvB,KAAK,QAAQ,QAAQ;CACvB;CAEA,OAAgB,UAAgD;EAK9D,KAAK,wBAAwB,EAE1B,YAAY,CAAC,CAAC,EAEd,WAAW;GACV,KAAK,aAAa,QAAQ,KAAK,QAAQ;GACvC,KAAK,KAAK,IAAI;GACd,SAAS;EACX,CAAC;CACL;CAEA,SAAkB,OAAqB,UAAgD;EACrF,KAAK,WAAW;EAChB,KAAK,SAAS,SAAS;EACvB,KAAK,MAAM,MAAM;EAKjB,MAAM,eAAe,yBAAS,IAAI,MAAM,kBAAkB;EAC1D,MAAM,YAAY,KAAK;EACvB,KAAK,aAAa,CAAC;EACnB,KAAK,MAAM,MAAM,WACf,GAAG,YAAY;EAKjB,KAAK,wBAAwB,EAE1B,YAAY,CAAC,CAAC,EAEd,WAAW;GACV,KAAK,aAAa,OAAO,KAAK,UAAU,YAAY;GACpD,SAAS,KAAK;EAChB,CAAC;CACL;;;;;CAQA,QAAgB,UAAgD;EAC9D,KAAK,WAAW,KAAK,QAAQ;EAC7B,IAAI,CAAC,KAAK,UAER,KAAK,MAAM,EAAE;;SAAiC,CAAC;EAAC;CAEpD;;;;;CAMA,MAAc,QAAuB;;EAEnC,IAAI,KAAK,UAAU;EACnB,KAAK,WAAW;EAEhB,IAAI,QAAsB;EAE1B,IAAI;GAEF,OAAO,KAAK,MAAM,SAAS,GAAG;;IAE5B,IAAI,KAAK,UAAU;IACnB,MAAM,eAAe,KAAK,MAAM;IAEhC,IAAI,KAAK,UAAU,eACjB,MAAM,KAAK,kBAAkB;IAE/B,IAAI,KAAK,UAAU,SACjB,MAAM,KAAK,gBAAgB;;;IAO7B,IAAI,KAAK,MAAM,WAAW,KAAK,KAAK,MAAM,WAAW,cAAc;GACrE;EACF,SAAS,KAAK;GACZ,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;GAG1D,KAAK,aAAa,QAAQ,KAAK,QAAQ;EACzC,UAAU;GACR,KAAK,WAAW;GAGhB,MAAM,YAAY,KAAK;GACvB,KAAK,aAAa,CAAC;GACnB,KAAK,MAAM,MAAM,WACf,GAAG,KAAK;EAEZ;CACF;;;;;;;CAUA,MAAc,oBAAmC;EAC/C,IAAI,KAAK,MAAM,SAAS,GAAG;EAC3B,MAAM,MAAM,KAAK,MAAM,YAAY,CAAC;;EAEpC,IAAI,QAAQ,KAAA,KAAa,KAAK,MAAM,SAAS,KAAK;EAElD,MAAM,UAAU,KAAK,MAAM,QAAQ,GAAG;EAEtC,MAAM,KAAK,WAAW,YAAY;GAChC,MAAM,KAAK,eAAe,SAAS;IAAE,cAAc;IAAO,yBAAyB;GAAM,CAAC;EAC5F,CAAC;EAED,KAAK,QAAQ;CACf;;;;;;;;;;;;;;CAeA,MAAc,qBAAqB,IAAwC;EACzE,MAAM,gBAAgB,KAAK,QAAQ,KAAK,OAAO;EAE/C,MAAM,KAAK,OAAO,aAAa,YAAY;GACzC,IAAI,KAAK,UAAU;GACnB,MAAM,YAAY,GAAG;GACrB,KAAK,oBAAoB;GACzB,IAAI;IACF,MAAM;GACR,UAAU;IACR,KAAK,oBAAoB,KAAA;GAC3B;EACF,CAAC;CACH;;;CAIA,MAAc,WAAW,IAAwC;EAC/D,MAAM,UAAU,KAAK,eAAe;EACpC,IAAI,SAAS,MAAM;EACnB,MAAM,KAAK,qBAAqB,EAAE;CACpC;;;;;;;;;;;CAYA,MAAc,kBAAiC;EAC7C,OAAO,KAAK,MAAM,UAAU,GAAG;GAC7B,MAAM,SAAS,KAAK,MAAM,YAAY,CAAC;;GAEvC,IAAI,WAAW,KAAA,GAAW;GAC1B,MAAM,MAAM,IAAI;GAChB,IAAI,MAAM,KAAK,KAAK,MAAM,SAAS,KAAK;GAExC,MAAM,UAAU,KAAK,MAAM,QAAQ,GAAG;;GAEtC,MAAM,UAAU,QAAQ,MAAM;GAE9B,IAAI,YAAA,IAAuB;IACzB,MAAM,KAAK,wBAAwB;IACnC,KAAK,aAAa,QAAQ,KAAK,QAAQ;IACvC,KAAK,KAAK,IAAI;IACd;GACF;GAEA,IAAI,aAAa,IAAI,OAAO,GAAG;IAC7B,KAAK,SAAS,KAAK,OAAO;IAC1B;GACF;GAEA,IAAI,YAAA,IAAkB;IACpB,KAAK,SAAS,KAAK,OAAO;IAC1B,MAAM,KAAK,cAAc;IACzB;GACF;GAGA,MAAM,KAAK,eAAe,iBACxB,KAAK,eAAe,SAAS;IAAE;IAAc,yBAAyB;GAAM,CAAC,CAC/E;EACF;CACF;;;;;;;;;;;CAYA,MAAc,gBAA+B;EAC3C,MAAM,WAAW,KAAK;EACtB,KAAK,WAAW,CAAC;EACjB,IAAI;;EAEJ,IAAI,SAAS,WAAW,GACtB,QAAQ,SAAS,MAAM,IAAI,WAAW,CAAC;OAEvC,QAAQ,KAAK,2BAA2B,QAAQ,KAAK,KAAK,eAAe,QAAQ;EAEnF,MAAM,KAAK,eAAe,iBACxB,KAAK,eAAe,OAAO;GAAE;GAAc,yBAAyB;EAAK,CAAC,CAC5E;CACF;CAEA,2BAAmC,UAAgD;EACjF,MAAM,QAAQ,SAAS;;EAEvB,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;EAEhC,MAAM,SAAS,MAAM;EACrB,MAAM,QAAQ,MAAM;EACpB,IAAI,MAAM,QAAQ,MAAM;EACxB,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;GACxC,MAAM,OAAO,SAAS;;GAEtB,IAAI,SAAS,KAAA,GAAW,OAAO,KAAA;GAC/B,IAAI,KAAK,WAAW,UAAU,KAAK,eAAe,KAChD;GAEF,OAAO,KAAK;EACd;EAEA,OAAO,IAAI,WAAW,QAAQ,OAAO,MAAM,KAAK;CAClD;CAEA,eAAuB,UAAoC;EACzD,MAAM,QAAQ,SAAS,QAAQ,KAAK,MAAM,MAAM,EAAE,QAAQ,CAAC;EAC3D,MAAM,QAAQ,IAAI,WAAW,KAAK;EAClC,IAAI,SAAS;EACb,KAAK,MAAM,QAAQ,UAAU;GAC3B,MAAM,IAAI,MAAM,MAAM;GACtB,UAAU,KAAK;EACjB;EACA,OAAO;CACT;;;;;;;;;;;;;CAgBA,MAAc,cAAc,IAAgE;EAC1F,MAAM,gBAAgB,KAAK,cAAc,KAAA;EACzC,MAAM,eAAe,KAAK,aAAa,KAAA,KAAa,aAAa;EACjE,MAAM,kBAAkB,KAAK,aAAa,KAAA,KAAa,gBAAgB;EACvE,MAAM,aAAa,iBAAiB,gBAAgB;EACpD,MAAM,eAAe,iBAAiB;EAEtC,IAAI,CAAC,YAAY;GACf,MAAM,KAAK,WAAW,YAAY;IAChC,MAAM,GAAG,KAAK;GAChB,CAAC;GACD;EACF;EAEA,MAAM,YAAY,QAAQ,OAAO,OAAO;EACxC,MAAM,UAAU,KAAK,eAAe;EACpC,IAAI,SAAS,MAAM;EACnB,MAAM,aAAa,QAAQ,OAAO,OAAO;EACzC,MAAM,aAAa,OAAO,aAAa,SAAS;EAChD,IAAI,eACF,KAAK,WAAW,eAAe,UAAU;EAE3C,IAAI,iBACF,gBAAgB,QAAQ;GACtB,UAAU,KAAK;GACf,YAAY;EACd,CAAC;EAGH,IAAI,YAAY;EAChB,IAAI;GACF,MAAM,KAAK,qBAAqB,YAAY;IAC1C,YAAY,MAAM,GAAG,YAAY;GACnC,CAAC;EACH,SAAS,KAAK;GACZ,YAAY;GACZ,MAAM;EACR,UAAU;GACR,MAAM,UAAU,OAAO,QAAQ,OAAO,OAAO,IAAI,UAAU;GAC3D,IAAI,eACF,KAAK,WAAW,YAAY,SAAS,SAAS;GAEhD,IAAI,cACF,aAAa,QAAQ;IACnB,UAAU,KAAK;IACf,YAAY;IACZ;GACF,CAAC;EAEL;CACF;;;;;;;;;;CAWA,MAAc,eACZ,SACA,SACkB;EAClB,MAAM,EAAE,cAAc,4BAA4B;EAClD,IAAI,UAAU;EACd,MAAM,SAAS,IAAI,qBAAqB;GACtC,mCAAmC;GACnC,8BAA8B,KAAK;GACnC,UAAU,UAAU;;IAElB,IAAI,CAAC,KAAK,YAAY,MAAM,SAAS,GACnC,KAAK,KAAK,KAAK;GAEnB;GACA,uBAAuB;IACrB,IAAI,cAAc,UAAU;GAC9B;GACA,kBAAkB,WAAW;IAC3B,KAAK,oBAAoB;IACzB,IAAI,KAAK,aACP,KAAK,YAAY,aAAa,KAAK,UAAU,MAAM;GAEvD;EACF,CAAC;EAMD,MAAM,gBAAgB,KAAK,QAAQ,KAAK,OAAO;EAO/C,IAAI,WAAW;EACf,IAAI,eAAe;EACnB,IAAI;GACF,MAAM,KAAK,OAAO,sBAAsB,SAAS;IAC/C,UAAU,KAAK;IACf,YAAY,UAAsB;KAChC,YAAY,MAAM;KAClB,OAAO,MAAM,KAAK;IACpB;GACF,CAAC;EACH,SAAS,KAAK;GACZ,eAAe;GACf,MAAM;EACR,UAAU;GACR,KAAK,+BAA+B;GACpC,KAAK;GACL,IACE,CAAC,KAAK,+BACL,gBACC,KAAK,+BAA+B,8BACpC,KAAK,+BAA+B,yBACtC;IACA,MAAM,KAAK,4BAA4B;IACvC,KAAK,8BAA8B;IACnC,KAAK,8BAA8B;GACrC;EACF;EAEA,OAAO,MAAM,EAAE,uBAAuB,KAAK,SAAS,CAAC;EACrD,OAAO,CAAC;CACV;CAEA,MAAc,8BAA6C;EACzD,IAAI,KAAK,4BAA4B;EAErC,MAAM,EAAE,uBAAuB,KAAK;EACpC,IAAI,OAAO,uBAAuB,YAAY;GAC5C,KAAK,6BAA6B;GAClC;EACF;EAEA,IAAI;GACF,MAAM,mBAAmB,KAAK,KAAK,QAAQ,mBAAmB;IAC5D,UAAU;IACV,cAAc;GAChB,CAAC;EACH,QAAQ;GAIN,KAAK,6BAA6B;EACpC;CACF;CAIA,iBAAoD;EAClD,OAAO,KAAK,aAAa,QAAQ,KAAK,QAAQ;CAChD;;;;;;;;;;;;;;;;CAiBA,MAAM,0BAAyC;EAG7C,IAAI,KAAK,oBAAoB,KAAA,GAAW,OAAO,KAAK;EACpD,KAAK,kBAAkB,KAAK,YAAY;EACxC,OAAO,KAAK;CACd;CAEA,MAAc,cAA6B;;EASzC,IAAI,KAAK,mBACP,MAAM,KAAK,kBAAkB,YAAY,KAAA,CAAS;;EAIpD,MAAM,SAAS,KAAK;EACpB,IAAI,WAAA,MAAwC,WAAA,IAA8B;;EAM1E,IAAI,KAAK,gBAAgB,KAAA,KAAa,CAAC,KAAK,YAAY,QAAQ,KAAK,QAAQ,GAAG;EAEhF,IAAI;GAEF,MAAM,KAAK,OAAO,MAAM,UAAU;GAClC,KAAK,oBAAA;EACP,QAAQ,CAGR;CACF;AACF;;;ACtsBA,MAAa,mBAAmB,QAAkC,SAAiC;CACjG,IAAI,OAAO,SAAS,WAAW,OAAO;CACtC,OAAO,CAAC,CAAC,OAAO,WAAW,CAAC,OAAO,QAAQ,WAAW,WAAW;AACnE;;;;;;;;;;;;;;;;;;ACUA,MAAM,cAAc;AACpB,MAAM,wBAAwB;AAC9B,MAAM,gBAAgB;;;;;;;;;;;AAYtB,IAAa,cAAb,MAAyB;CACvB;CACA,YACE,CAAC;;;;;CAMH,MAAM,QAAQ,IAA2B;EAEvC,IAAI,KAAK,UAAU,KAAA,KAAa,KAAK,UAAU,IAAI;EAGnD,OAAO,IAAI,SAAe,SAAS,WAAW;GAC5C,KAAK,UAAU,KAAK;IAClB;IACA;IACA;GACF,CAAC;EACH,CAAC;CACH;;CAGA,QAAQ,IAAqB;EAC3B,OAAO,KAAK,UAAU;CACxB;;;;;;;;;CAUA,aAAa,IAAY,QAAyB;EAChD,IAAI,WAAW,yBAAyB,WAAW,eAAe;GAChE,IAAI,KAAK,UAAU,IAAI,OAAO;GAC9B,KAAK,QAAQ;GACb,OAAO;EACT;EAGA,IAAI,WAAW,eAAe,KAAK,UAAU,IAAI;GAC/C,KAAK,QAAQ,KAAA;GACb,KAAK,eAAe;GACpB,OAAO;EACT;EAEA,OAAO;CACT;;;;;;;CAQA,QAAQ,IAAqB;EAC3B,IAAI,KAAK,UAAU,IAAI;GACrB,KAAK,QAAQ,KAAA;GACb,KAAK,eAAe;GACpB,OAAO;EACT;EAEA,OAAO;CACT;;;;;;;CAQA,OAAO,IAAY,wBAAe,IAAI,MAAM,gCAAgC,GAAY;EACtF,IAAI,YAAY;EAEhB,IAAI,KAAK,UAAU,IAAI;GACrB,KAAK,QAAQ,KAAA;GACb,KAAK,eAAe;GACpB,YAAY;EACd;EAEA,MAAM,YAAmC,CAAC;EAC1C,KAAK,MAAM,UAAU,KAAK,WACxB,IAAI,OAAO,OAAO,IAAI;GACpB,OAAO,OAAO,KAAK;GACnB,YAAY;EACd,OACE,UAAU,KAAK,MAAM;EAGzB,KAAK,YAAY;EAEjB,OAAO;CACT;;CAGA,iBAA+B;EAM7B,MAAM,OAAO,KAAK,UAAU,MAAM;EAClC,IAAI,CAAC,MAAM;EAEX,KAAK,QAAQ,KAAK;EAClB,KAAK,QAAQ;CACf;AACF;;;;;;;;;;;;;;;;;;;;;;;ACxHA,MAAa,YAAY,QAAiD;CACxE,OAAO,OAAO,QAAQ,YAAY,QAAQ,QAAQ,CAAC,MAAM,QAAQ,GAAG;AACtE;AAQA,MAAa,eAAe,UAAuC;CACjE,OAAO,SAAS,KAAK,KAAK,mBAAmB,SAAS,OAAO,MAAM,kBAAkB;AACvF;AAIA,MAAM,sBAA2C,IAAI,IAAI;CACvD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAGD,MAAM,yBAAsD,IAAI,IAAI;CAClE,CAAC,KAAM,EAAE;CACT,CAAC,MAAM,EAAE;CACT,CAAC,MAAM,EAAE;CACT,CAAC,MAAM,EAAE;CACT,CAAC,MAAM,EAAE;CACT,CAAC,MAAM,GAAG;CACV,CAAC,MAAM,GAAG;CACV,CAAC,MAAM,GAAG;CACV,CAAC,MAAM,EAAE;CACT,CAAC,MAAM,IAAI;AACb,CAAC;AAED,MAAM,0BAAkC,SAAA,GAAA,eAAA,OAAqB,GAAG;AAEhE,MAAa,iCAAsD,UAAgB;CACjF,MAAM,WAAW,MAAM;CACvB,MAAM,WAA0B,KAAK,SAAS,WAAW;EACvD,IAAI,WAAW,QAAQ;GACrB,IAAI,oBAAoB,IAAI,GAAG,GAAG,OAAO;GACzC,MAAM,aAAa,uBAAuB,IAAI,GAAG;GACjD,IAAI,eAAe,KAAA,GAAW;IAC5B,MAAM,gBAAgB,SAAS,YAAY,MAAM;IACjD,QAAQ,SAAA,GAAA,eAAA,OAAqB,KAAK,aAAa;GACjD;EACF;EACA,OAAO,SAAS,KAAK,MAAM;CAC7B;CACA,OAAO;EAAE,GAAG;EAAO,eAAe;CAAQ;AAC5C;;;AC7DA,IAAa,iBAAb,MAAa,uBAAuB,GAAA,QAAG,OAAO;CAC5C;CAEA,OAAgB,aAA4B,OAAO,uBAAuB;CAE1E,YAAY,QAA+B;EAEzC,MAAM,GAAG,eAAe,aAAa,QAAQ,GAAG,iBAD/B,UAAW,CAAC;EAE7B,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,wCAAwC;EAG1D,MAAM;GACJ,GAAG;GACH,MAAM;GACN,UAAU;GACV,cAAc,IAAI,aAAa,OAAO,QAAQ,MAAM;EACtD,CAAC;CACH;CAGA,MAAe,GAAG,MAAsB;EACtC,MAAM,QAAQ,KAAK;EACnB,MAAM,eAAe;GAEnB,OAAQ,MAAM,MAAc,MAAM,MAAM,IAAI;EAC9C;EAGA,IAAI,UAAU,QAAQ,UAAU,KAAA,GAAW,OAAO,OAAO;EAMzD,IAAI,OAAQ,MAA+B,WAAW,YACpD,OAAO,OAAO;EAGhB,MAAM,UAAU,KAAK,WAAW,QAAQ,OAAO,QAAQ,UAAU;EACjE,IAAI,YAAY,IAAI;GAClB,MAAM,SAAS,KAAK;GACpB,MAAM,cAAc,KAAK,MAAM;GAC/B,YAAY,OAAO,SAAS,CAAC;GAE7B,IAAI;IACF,KAAK,MAAM,GAAG,WAAW,EAAE,MACxB,QAAiB,OAAO,MAAM,GAAG,IACjC,QAAiB,OAAO,KAAK,KAAA,CAAS,CACzC;GACF,SAAS,KAAK;IACZ,OAAO,KAAK,KAAA,CAAS;GACvB;GACA;EACF;EAEA,IAAI,SAAS,KAAK,KAAK,YAAY,MAAM,KAAK,GAC5C,KAAK,KAAK;GACR,GAAG;GACH,OAAO,8BAA8B,MAAM,KAAK;EAClD;EAGF,MAAM,QAAQ,KAAK;EACnB,IAAI;EACJ,IAAI,UAAU,KAAA,GACZ,IAAI;GACF,IAAI,OAAO;EACb,SAAS,KAAK;GACZ,OAAO,QAAQ,OAAO,GAAG;EAC3B;OAEA,IAAI,MAAM,KAAK,MAAM;EAEvB,IAAI;EACJ,MAAM,mBAAmB;GACvB,IAAI,KAAK,yBAAyB,MAChC,KAAK,uBAAuB,KAAA;EAEhC;EACA,OAAO,EAAE,KAAK,YAAY,UAAU;EACpC,KAAK,uBAAuB;EAC5B,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACFA,IAAa,eAAb,cAAkC,GAAA,QAAG,KAAK;;;;;;CAMxC;;;;;CAMA;CAEA;CAEA,YAAY,EACV,WAAW,OAAO,QAAQ,GAC1B,MAAM,GACN,QACA,WACA,SACA,aACuD,CAAC,GAAG;EAC3D,MAAM,iBAAiB,UAAU,IAAIE,qBAAAA,OAAO;EAK5C,MAAM,aAAa;GACjB,QAAQ;GACR;IACC,eAAe,aAAa;IAC3B,QAAQ;IACR,aAAa,MAAM,IAAI,IAAI,YAAY,IAAI,KAAA;IAC3C;IACA;IACA,UAAU,gBAAgB,gBAAgB,QAAQ;IAClD;GACF;EACF;EAEA,MAAM,UAAU;EAEhB,KAAK,WAAW;EAChB,KAAK,SAAS;EACd,KAAKD,cAAc,CAAC;CACtB;CASA,IAAa,UAA6C;EACxD,MAAM,UAAU,YAA2B;GACzC,IAAI,KAAKA,eAAe,CAAC,KAAK,OAAO,QACnC,MAAM,KAAK,OAAO,MAAM;EAE5B;EACA,IAAI,UAAU;GACZ,MAAM,UAAU;IACd,QAAQ,EAAE,KAAK,UAAU,QAAQ;GACnC,CAAC;GACD;EACF;EACA,OAAO,MAAM,IAAI,EAAE,KAAK,OAAO;CACjC;AACF;AC9HA,MAAM,gCAAgC,MAA6B;AAiEnE,MAAM,2BAA2B;;;;;;;AAQjC,MAAM,gCAAoD;CACxD,IAAI;EACF,OAAO,QAAQ,cAAc,EAAE,SAAS;CAC1C,QAAQ;EACN;CACF;AACF;AAEA,MAAM,cAAc,QAA2B,MAAsB;CACnE,MAAM,IAAI,OAAO;CACjB,IAAI,MAAM,GAAG,OAAO;;CAGpB,OAAO,OAFO,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,KAAM,IAAI,MAAO,CAAC,IAAI,CAAC,CAEpD,MAAM;AAC1B;AAEA,IAAa,cAAb,MAAkD;CAChD;CACA;CAEA,iBAAmC,CAAC;CACpC,eAAuB;CACvB,aAAqB;CACrB,mBAA2B;CAC3B,eAAuB;CAEvB,yBAAiC;CACjC,uBAA+B;CAC/B,8BAAsC;CAEtC,SAAiB;CACjB;CACA;CACA,eAAuB;CAEvB,YAAY,OAAyB;EACnC,KAAK,QAAQ;EACb,KAAK,kBAAkB,QAAQ,OAAO,OAAO;CAC/C;CAEA,YAAY,YAAoB,WAA0B;EACxD,IAAI,KAAK,QAAQ;EACjB,KAAK,cAAc;EACnB,KAAK,gBAAgB;EACrB,KAAK,eAAe,KAAK,UAAU;EACnC,IAAI,KAAK,eAAe,SAAS,+BAC/B,KAAK,iBAAiB,KAAK,eAAe,MAAM,IAA2B;EAE7E,IAAI,CAAC,WAAW,KAAK,oBAAoB;CAC3C;CAEA,eAAe,YAA0B;EACvC,IAAI,KAAK,QAAQ;EACjB,IAAI,KAAK,UAAU,QAAQ;EAC3B,KAAK,0BAA0B;EAC/B,KAAK,+BAA+B;EACpC,IAAI,aAAa,KAAK,sBAAsB,KAAK,uBAAuB;CAC1E;CAEA,mBAAyB;EACvB,IAAI,KAAK,QAAQ;EACjB,KAAK,gBAAgB;CACvB;CAEA,MAAM,SAAS,QAAyC;EACtD,MAAM,aACJ,KAAK,oBAAoB,OAAO,QAAQ,OAAO,OAAO,IAAI,KAAK,eAAe;EAChF,MAAM,cAAc,KAAK,eAAe,KAAK,oBAAoB,MAAM,KAAK,YAAY,MAAM;EAE9F,MAAM,SAAS,CAAC,GAAG,KAAK,cAAc,EAAE,MAAM,GAAG,MAAM,IAAI,CAAC;EAC5D,MAAM,aAAa,KAAK,eAAe,IAAI,IAAI,KAAK,eAAe,KAAK;EAExE,MAAM,OAAkB;GACtB;GACA,YAAY,KAAK;GACjB,kBAAkB,KAAK;GACvB,cAAc,KAAK;GACnB;GACA,kBAAkB,WAAW,QAAQ,EAAE;GACvC,kBAAkB,WAAW,QAAQ,EAAE;GACvC,kBAAkB,WAAW,QAAQ,GAAG;GACxC,cAAc,KAAK;GACnB;EACF;EAEA,IAAI,KAAK,UAAU,SACjB,OAAO;GAAE,GAAG;GAAM,YAAY;EAAQ;EAGxC,MAAM,QAAQ,KAAK;EACnB,OAAO;GACL,GAAG;GACH,YAAY;GACZ,qBAAqB,wBAAwB;GAC7C,wBAAwB,KAAK;GAC7B,6BAA6B;GAC7B,sBAAsB,UAAU,IAAI,IAAI,KAAK,yBAAyB;GACtE,sBAAsB,KAAK;EAC7B;CACF;CAEA,MAAM,OAAO,QAAyB,kBAAyC;EAC7E,IAAI,KAAK,QAAQ;EACjB,KAAK,SAAS;EAEd,KAAK,mBAAmB,OAAO,mBAAmB,KAAK,eAAe;EACtE,IAAI;GACF,KAAK,oBAAoB,MAAM,KAAK,YAAY,MAAM;EACxD,UAAU;GACR,KAAK,eAAe;EACtB;CACF;CAEA,MAAc,YAAY,QAAsD;EAC9E,IAAI;EACJ,IAAI;GACF,MAAM,UAAU,IAAI,SAAgB,GAAG,WAAW;IAChD,QAAQ,iBACA,uBAAO,IAAI,MAAM,kCAAkC,CAAC,GAC1D,wBACF;IACA,MAAM,QAAQ;GAChB,CAAC;GACD,MAAM,EAAE,SAAS,MAAM,QAAQ,KAAK,CAClC,OAAO,MACL,2DACF,GACA,OACF,CAAC;GACD,MAAM,OAAO,KAAK,IAAI;GACtB,IAAI,QAAQ,MAAM,OAAO,KAAA;GACzB,MAAM,IAAI,OAAO,IAAI;GACrB,OAAO,OAAO,SAAS,CAAC,IAAI,IAAI,KAAA;EAClC,QAAQ;GACN;EACF,UAAU;GACR,aAAa,KAAK;EACpB;CACF;AACF;;;;ACjQA,MAAa,cAAc,eAA+B,IAAI,WAAW,QAAQ,MAAM,MAAI,EAAE;;;ACE7F,MAAM,kBAAkB;AAExB,MAAM,0BAA0B;4BACJ,gBAAgB;AAE5C,MAAM,oBAAoB,GAAG,wBAAwB;;AAGrD,MAAM,iBAAiB,MAAsB,IAAI,EAAE,QAAQ,MAAM,IAAI,EAAE;AAEvE,MAAM,wBAAwB,WAAW,eAAe;AACxD,MAAM,0BAA0B,cAAc,eAAe;;;;;;;;;AAU7D,IAAa,kBAAb,MAA6B;CAC3B;CACA,eAAe;CAEf,YAAY,QAAkC;EAC5C,KAAKE,UAAU;CACjB;;;;;CAMA,MAAM,aAA4B;EAChC,MAAM,KAAKA,QAAQ,KAAK,yBAAyB,sBAAsB,SAAS;EAEhF,IAAI;GACF,MAAM,KAAKA,QAAQ,KAAK,OAAO;GAC/B,MAAM,KAAKA,QAAQ,KAAK,iBAAiB,uBAAuB;GAEhE,MAAM,EAAE,MAAM,WAAW,MAAM,KAAKA,QAAQ,MAK1C;;;iBAGS,mBACX;GAEA,MAAM,KAAKA,QAAQ,KACjB,gBAAgB,sBAAsB,kEACxC;GAEA,KAAK,MAAM,CAAC,GAAG,EAAE,YAAY,WAAW,gBAAgB,OAAO,QAAQ,GAAG;IACxE,MAAM,WAAW,SAAS;IAC1B,MAAM,KAAKA,QAAQ,KACjB,gBAAgB,sBAAsB,GAAG,WAAW,QAAQ,EAAE,oBAAoB,WACpF;IACA,MAAM,KAAKA,QAAQ,KACjB,eAAe,sBAAsB,oBAAoB,cAAc,QAAQ,EAAE,IAAI,cAAc,UAAU,EAAE,IAAI,cAAc,SAAS,EAAE,EAC9I;GACF;GAEA,MAAM,EAAE,MAAM,SAAS,MAAM,KAAKA,QAAQ,MACxC;;iBAES,wBAAwB;oCAEnC;GAEA,MAAM,KAAKA,QAAQ,KACjB,gBAAgB,sBAAsB,uCACxC;GACA,KAAK,MAAM,EAAE,MAAM,WAAW,MAC5B,MAAM,KAAKA,QAAQ,KACjB,eAAe,sBAAsB,uBAAuB,KAAK,IAAI,MAAM,EAC7E;GAGF,MAAM,KAAKA,QAAQ,KAAK,QAAQ;EAClC,SAAS,KAAK;GACZ,MAAM,KAAKA,QAAQ,KAAK,UAAU;GAClC,MAAM,KAAKA,QAAQ,KAAK,yBAAyB,sBAAsB,SAAS;GAChF,MAAM;EACR;EAEA,KAAKC,eAAe;CACtB;;CAGA,MAAM,gBAA+B;EACnC,KAAKA,eAAe;EACpB,MAAM,KAAKD,QAAQ,KAAK,yBAAyB,sBAAsB,SAAS;CAClF;;;;;CAMA,MAAM,UAAyB;EAC7B,IAAI,KAAKC,cAAc,MAAM,KAAKC,sBAAsB;EAExD,MAAM,SAAS,MAAM,KAAKC,WAAW;EAErC,IAAI,QACF,MAAM,KAAKC,4BAA4B,YAAY;GACjD,MAAM,KAAKJ,QAAQ,KAAK,kBAAkB,OAAO,0BAA0B;GAE3E,IAAI,CAAC,KAAKC,cAAc;GAExB,MAAM,EAAE,MAAM,mBAAmB,MAAM,KAAKD,QAAQ,MAIlD;;kBAEQ,sBAAsB,UAChC;GAEA,KAAK,MAAM,EAAE,iBAAiB,eAAe,gBAC3C,MAAM,KAAKA,QAAQ,KACjB,eAAe,UAAU,iBAAiB,sBAAsB,GAAG,iBACrE;GAGF,MAAM,EAAE,MAAM,SAAS,MAAM,KAAKA,QAAQ,MACxC,iEAAiE,sBAAsB,aACzF;GAEA,KAAK,MAAM,EAAE,MAAM,WAAW,MAC5B,MAAM,KAAKA,QAAQ,KAAK,iBAAiB,KAAK,IAAI,MAAM,EAAE;EAE9D,CAAC;EAGH,MAAM,KAAKA,QAAQ,KAAK,aAAa;CACvC;CAEA,MAAMG,aAA8B;EAClC,MAAM,EAAE,SAAS,MAAM,KAAKH,QAAQ,MAClC;;eAES,mBACX;EACA,OAAO,KAAK,KAAK,QAAQ,IAAI,SAAS,EAAE,KAAK,IAAI;CACnD;;;;;;CAOA,MAAME,wBAA0C;EAC9C,MAAM,EAAE,SAAS,MAAM,KAAKF,QAAQ,MAClC,0BAA0B,wBAAwB,wBACpD;EACA,MAAM,SAAS,KAAK,IAAI;EACxB,IAAI,CAAC,QAAQ,KAAKC,eAAe;EACjC,OAAO,CAAC,CAAC;CACX;CAEA,MAAMG,4BAA4B,IAAwC;EACxE,IAAI;GACF,MAAM,KAAKJ,QAAQ,KAAK,wCAAwC;GAChE,MAAM,GAAG;EACX,UAAU;GACR,MAAM,KAAKA,QAAQ,KAAK,wCAAwC;EAClE;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1HA,MAAa,8BAAoC;CAC/C,QAAQ,YACN,+HAEA,EAAE,MAAM,0BAA0B,CACpC;AACF;AAEA,MAAM,eAAe,IAAI,qBAA2B,qBAAqB;AA6DzE,IAAa,eAAb,MAA0B;;CAExB;;;;;;;CAQA;;;;;;;;CASA;CAEA;CACA;CACA;CACA,aAA8B,CAAC;CAC/B;CACA;CAEA,YAAY,UAA+B,CAAC,GAAG;EAC7C,MAAM,aAAa,QAAQ,cAAc;EACzC,IAAI,eAAe,SAAS,eAAe,WAAW,eAAe,QACnE,MAAM,IAAI,MAAM,qDAAqD,OAAO,UAAU,GAAG;EAG3F,KAAKS,cAAc,CAAC,QAAQ;EAC5B,KAAK,SAAS,QAAQ,UAAU,IAAIC,qBAAAA,OAAO;EAC3C,KAAK,WAAW,QAAQ,YAAY,OAAO,QAAQ;EAEnD,KAAKJ,SAAS,eAAe,QAAQ,KAAA,IAAY,IAAI,YAAY,UAAU;EAC3E,KAAKD,QAAQ,IAAI,aAAa;GAC5B,GAAG;GACH,QAAQ,KAAK;GACb,UAAU,KAAK;GACf,WAAW,KAAKC;EAClB,CAAC;EACD,KAAKC,YAAY,IAAI,gBAAgB,KAAK,MAAM;EAEhD,KAAK,UAAU,IAAII,mBAAAA,SAAS,KAAKN,KAAK;EAEtC,aAAa,SAAS,KAAK,SAAS,KAAA,GAAW,KAAKG,UAAU;CAChE;;;;;;;;;;;;CAaA,UAAU,YAA2B;EACnC,KAAKI,gBAAgB,SAAS;EAC9B,KAAKN,QAAQ,iBAAiB;EAC9B,OAAO,KAAKC,UAAU,QAAQ;CAChC;;;;;;;;;;;CAYA,aAAa,YAA2B;EACtC,KAAKK,gBAAgB,YAAY;EACjC,OAAO,KAAKL,UAAU,WAAW;CACnC;;;;;;CAOA,gBAAgB,YAA2B;EACzC,KAAKK,gBAAgB,eAAe;EACpC,OAAO,KAAKL,UAAU,cAAc;CACtC;CAEA,gBAAgB,QAAsB;EACpC,MAAM,WAAW,KAAKF,MAAM,aAAa,KAAKA,MAAM;EACpD,IAAI,WAAW,GACb,MAAM,IAAI,MACR,GAAG,OAAO,6CAA6C,SAAS,sFAElE;CAEJ;;;;;;;;;;;;CAaA,QAAQ,YAA2B;EACjC,IAAI,CAAC,KAAKQ,UACR,KAAKA,YAAY,YAAY;GAC3B,MAAM,aAAa,KAAKP,SAAS,QAAQ,OAAO,OAAO,IAAI,KAAA;GAC3D,MAAM,KAAKD,MAAM,IAAI;GACrB,IAAI,eAAe,KAAA,GACjB,MAAM,KAAKC,QAAQ,OAAO,KAAK,QAAQ,UAAU;GAEnD,aAAa,WAAW,KAAKE,UAAU;GACvC,IAAI,KAAKC,eAAe,CAAC,KAAK,OAAO,QACnC,MAAM,KAAK,OAAO,MAAM;EAE5B,GAAG;EAEL,OAAO,KAAKI;CACd;;;;;;CAOA,QAAQ,YAAwC;EAC9C,OAAO,KAAKP,SAAS,KAAKA,OAAO,SAAS,KAAK,MAAM,IAAI,KAAA;CAC3D;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1NA,MAAM,mBAAmB;AACzB,MAAM,sBAAsB;AAC5B,MAAM,sBAAsB;AAC5B,MAAM,uBAAuB;AAC7B,MAAM,sBAAsB;AAoD5B,IAAa,eAAb,MAA0B;;;;;;;;CAQxB;CAEA;CACA;CACA,eAAwB,IAAI,YAAY;CACxC,2BAAoB,IAAI,IAAmB;CAC3C;CAEA;CAEA,YAAY,UAA+B,CAAC,GAAG;EAC7C,MAAM,EAAE,QAAQ,GAAG,SAAS;EAE5B,KAAKY,cAAc,CAAC;EACpB,KAAK,SAAS,UAAU,IAAIC,qBAAAA,OAAO;EACnC,KAAKL,WAAW;GACd,GAAG;GACH,MAAM,KAAK,QAAQ;GACnB,MAAM,KAAK,SAAS,CAAC,KAAK,UAAU,IAAI;GACxC,MAAM,KAAK,OAAO,mBAAmB,KAAK,IAAI,IAAI;EACpD;EACA,KAAKC,UAAUK,SAAAA,QAAI,cAAc,WAAW,KAAKC,cAAc,MAAM,CAAC;CACxE;CAEA,SAAS,YAA6B;EACpC,IAAI,KAAKC,mBAAmB,OAAO,KAAKA;EACxC,IAAI,KAAK,OAAO,QACd,MAAM,IAAI,MAAM,kEAAkE;EAGpF,OAAO,IAAI,SAAiB,SAAS,WAAW;GAC9C,MAAM,WAAW,QAAqB,OAAO,GAAG;GAChD,KAAKP,QAAQ,KAAK,SAAS,OAAO;GAGlC,IAAI,KAAKD,SAAS,SAAS;IACzB,MAAM,EAAE,SAAS,MAAM,SAAS,KAAKA;IAErC,KAAKC,QAAQ,OAAOQ,UAAAA,QAAS,KAAK,SAAS,YAAY,MAAM,SAAS;KACpE,KAAKR,QAAQ,eAAe,SAAS,OAAO;KAC5C,KAAKO,oBAAoB,cAAc,KAAK,kBAAkB,mBAAmB,OAAO,EAAE,QAAQ;KAClG,OAAO,QAAQ,KAAKA,iBAAiB;IACvC,CAAC;IAED;GACF;GAGA,KAAKP,QAAQ,OAAO,KAAKD,SAAS,MAAM,KAAKA,SAAS,YAAY;IAChE,KAAKC,QAAQ,eAAe,SAAS,OAAO;IAE5C,MAAM,EAAE,SAAS,KAAKD;IAItB,MAAM,EAAE,SAAS,QAAQ,SAAS,KAAKC,QAAQ,QAAQ;IAEvD,KAAKO,oBACH,WAAW,SACP,cAAc,KAAK,kBAAkB,mBAAmB,OAAO,EAAE,QAAQ,SACzE,cAAc,KAAK,GAAG,QAAQ,GAAG,KAAK;IAC5C,KAAKR,SAAS,OAAO;IAErB,OAAO,QAAQ,KAAKQ,iBAAiB;GACvC,CAAC;EACH,CAAC;CACH;;;;;;;CAQA,QAAQ,YAA2B;EACjC,MAAM,UAAU,CAAC,GAAG,KAAKL,QAAQ;EACjC,KAAK,MAAM,UAAU,SAAS,OAAO,QAAQ;EAC7C,MAAM,IAAI,SAAe,SAAS,WAAW;GAC3C,KAAKF,QAAQ,OAAO,QAAS,MAAM,OAAO,GAAG,IAAI,QAAQ,CAAE;EAC7D,CAAC;EACD,MAAM,QAAQ,IAAI,QAAQ,KAAK,EAAE,aAAa,QAAQ,OAAO,CAAC;EAC9D,IAAI,KAAKG,eAAe,CAAC,KAAK,OAAO,QACnC,MAAM,KAAK,OAAO,MAAM;CAE5B;CAEA,YAAY,QAAqC;EAC/C,OAAO,SAAS,IAAI,aAAa,KAAK,QAAQ;GAC5C,aAAa,KAAKF;GAClB,SAAS,KAAKF,SAAS;GACvB,UAAU,gBAAgB,KAAK,QAAQ,KAAKA,SAAS,QAAQ;GAM7D,8BAA8B;EAChC,CAAC;EACD,OAAO,OAAO,GAAG,eAAe,OAAO,QAAQ,CAAC;EAChD,OAAO,KAAK,eAAe;GACzB,MAAM,EAAE,WAAW;GACnB,IAAI,UAAU,CAAC,OAAO,WAAW,OAAO,QAAQ;EAClD,CAAC;EACD,OAAO,KAAK,OAAO,MAAM,EAAE,KAAK,MAAM;EACtC,OAAO,OAAO;CAChB;CAEA,cAAc,QAA6B;EACzC,OAAO,WAAW,IAAI;EACtB,KAAKG,SAAS,IAAI,MAAM;EACxB,OAAO,GAAG,eAAe,KAAKA,SAAS,OAAO,MAAM,CAAC;EACrD,OAAO,GAAG,eAAe,OAAO,QAAQ,CAAC;EAEzC,IAAI,SAAiB,OAAO,MAAM,CAAC;EAEnC,MAAM,UAAU,UAAwB;GACtC,SAAS,OAAO,WAAW,IAAI,QAAQ,OAAO,OAAO,CAAC,QAAQ,KAAK,CAAC;GAGpE,OAAO,OAAO,UAAU,sBAAsB;IAC5C,MAAM,MAAM,OAAO,YAAY,CAAC;IAChC,MAAM,OAAO,OAAO,YAAY,CAAC;IACjC,IAAI,QAAQ,MAAM,SAAS,oBAAoB,SAAS,sBAAsB;KAC5E,OAAO,MAAM,GAAG;KAChB,SAAS,OAAO,SAAS,CAAC;KAC1B;IACF;IACA;GACF;GACA,IAAI,OAAO,SAAS,sBAAsB;GAE1C,MAAM,MAAM,OAAO,YAAY,CAAC;GAChC,MAAM,OAAO,OAAO,YAAY,CAAC;GAEjC,IAAI,QAAQ,MAAM,SAAS,qBAAqB;IAC9C,IAAI,OAAO,SAAS,IAAI;IACxB,OAAO,eAAe,QAAQ,MAAM;IACpC,OAAO,IAAI;IACX;GACF;GAEA,OAAO,eAAe,QAAQ,MAAM;GAGpC,OAAO,MAAM;GACb,KAAKO,YAAY,MAAM,EAAE,MAAM,MAAM;GACrC,OAAO,OAAO;EAChB;EAEA,OAAO,GAAG,QAAQ,MAAM;CAC1B;AACF;;;AC1OA,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAEzB,MAAa,6BAA6B,QAAwB;CAChE,IAAI,CAAC,IAAI,SAAS,eAAe,KAAK,CAAC,IAAI,SAAS,eAAe,GACjE,OAAO;CAET,OAAO,IAAI,QAAQ,iBAAiB,gBAAgB;AACtD;;;;;;;AAQA,MAAM,eAAe,YACnB,IAAI,MAAM,SAAS,EACjB,IAAI,QAAQ,MAAM;CAChB,IAAI,SAAS,YACX,QAAQ,UACN,OAAO,SAAS;EAAE,GAAG;EAAO,KAAK,0BAA0B,MAAM,GAAG;CAAE,CAAC;CAE3E,MAAM,QAAQ,QAAQ,IAAI,QAAQ,IAAI;CACtC,OAAO,OAAO,UAAU,aAAa,MAAM,KAAK,MAAM,IAAI;AAC5D,EACF,CAAC;AAEH,MAAa,sBACX,YAEA,IAAI,MAAM,SAAS,EACjB,IAAI,QAAQ,MAAM;CAChB,IAAI,SAAS,WACX,OAAO,YAAY,YAAY,MAAM,OAAO,QAAQ,CAAC;CAEvD,IAAI,SAAS,qBACX,OAAO,YAAY,YAAY,MAAM,OAAO,kBAAkB,CAAC;CAEjE,MAAM,QAAQ,QAAQ,IAAI,QAAQ,IAAI;CACtC,OAAO,OAAO,UAAU,aAAa,MAAM,KAAK,MAAM,IAAI;AAC5D,EACF,CAAC;;;ACHH,MAAM,cAAc,OAAO,YAAuC;CAChE,MAAM,EAAE,wCAAwC,MAAM,OAAO;CAG7D,OAAO,oCAAoC,mBAAmB,OAAO,CAAC;AACxE;AAEA,MAAM,qBAA4E;CAChF,gBAAgB,CAAC;CACjB,eAAe,CAAC;AAClB;;;;;;;;;;;AAYA,MAAM,qBAAqB,OAAO,YAAqC;CACrE,MAAM,OAAO,MAAM,QAAQ,QAAQ;CACnC,IAAI;EACF,MAAM,SAAS,MAAM,KAAK,SAAS;GACjC,KAAK;;GAEL,MAAM,CAAC;GACP,UAAU,CAAC;EACb,CAAC;EACD,MAAM,MAAM,OAAO,YAAY,QAAQ,SAAS;EAChD,MAAM,QAAQ,OAAO,KAAK,KAAK,QAAQ,OAAO,IAAI,IAAI,CAAC;EACvD,KAAK,MAAM,QAAQ,OACjB,MAAM,KAAK,WAAW;GACpB,KAAK,yBAAyB,WAAW,IAAI,EAAE;GAC/C,MAAM,CAAC;GACP,UAAU,CAAC;EACb,CAAC;EAEH,MAAM,KAAK,WAAW;GACpB,KAAK;GACL,MAAM,CAAC;GACP,UAAU,CAAC;EACb,CAAC;CACH,UAAU;EACR,MAAM,KAAK,QAAQ;CACrB;AACF;AAEA,MAAa,aAAa,OACxB,SACA,YAC8B;CAC9B,MAAM,WAAW,QAAQ,YAAY;CACrC,IAAI,QAAQ,YACV,MAAM,mBAAmB,OAAO;CAElC,MAAM,EAAE,iBAAiB,MAAM,OAAO;CACtC,MAAM,QAAQ,MAAM,YAAY,OAAO;CAEvC,MAAM,SAAS,MAAM,aAAa,IAChC,EAAE,YAAY,CAAC,CAAC,UAAU,QAAQ,MAAM,CAAC,EAAE,SACrC,CAAC,GACP,KACF;CACA,IAAI;EACF,MAAM,SAAS,MAAM,OAAO,WAAW;GACrC,OAAO,QAAQ,kBAAkB;GACjC,QAAQ,EAAE,OAAO,CAAC;IAAE,MAAM;IAAU,SAAS,QAAQ;GAAO,CAAC,EAAE;GAC/D,SAAS,YAAY;EACvB,CAAC;EACD,OAAO;GACL,eAAe,OAAO;GACtB,UAAU,OAAO;GACjB,cAAc,OAAO;EACvB;CACF,UAAU;EACR,OAAO,KAAK;CACd;AACF;AAEA,MAAa,cAAc,OAAO,YAAqC;CACrE,MAAM,mBAAmB,OAAO;AAClC;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzGA,MAAa,oBAAoB,OAAO,eAAqD;CAC3F,IAAI;EACF,MAAM,EAAE,uBAAuB,MAAM,OAAO;EAC5C,MAAM,EAAE,QAAQ,UAAU,MAAM,mBAAmB,EAAE,YAAY,cAAc,QAAQ,IAAI,EAAE,CAAC;EAC9F,IAAI,OAAO,OAAO,KAAA;EAElB,IAAI,OAAO,YAAY,MAAM,OAAO,OAAO,WAAW;EAEtD,MAAM,aAAa,OAAO;EAC1B,IAAI,YAAY,QAAA,GAAA,UAAA,OAAA,GAAA,UAAA,SAAoB,UAAU,GAAG,YAAY;EAE7D;CACF,QAAQ;EACN;CACF;AACF;;;;;;AAOA,MAAa,sBAAsB,mBAA+C;CAChF,IAAI,EAAA,GAAA,QAAA,YAAY,cAAc,GAAG,OAAO,KAAA;CAExC,MAAM,QAAA,GAAA,QAAA,aAAmB,cAAc,EACpC,QAAQ,eAAA,GAAA,QAAA,WAAA,GAAA,UAAA,MAA4B,gBAAgB,SAAS,CAAC,EAAE,YAAY,CAAC,EAC7E,KAAK;CAER,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,aAAa,MAAM;EAC5B,MAAM,WAAA,GAAA,UAAA,MAAe,gBAAgB,WAAW,eAAe;EAC/D,KAAA,GAAA,QAAA,YAAe,OAAO,GACpB,SAAS,MAAA,GAAA,QAAA,cAAkB,SAAS,MAAM,CAAC;CAE/C;CAEA,OAAO,SAAS,SAAS,IAAI,SAAS,KAAK,IAAI,IAAI,KAAA;AACrD;;;;;;;;AASA,MAAa,kBAAkB,OAAO,YAAoD;CACxF,IAAI,QAAQ,KAAK,OAAO,QAAQ;CAEhC,IAAI,QAAQ,gBAAgB;EAC1B,MAAM,MAAM,mBAAmB,QAAQ,cAAc;EACrD,IAAI,KAAK,OAAO;EAChB,MAAM,IAAI,MACR,mCAAmC,QAAQ,eAAe,0DAC5D;CACF;CAEA,MAAM,iBAAiB,MAAM,kBAAkB,QAAQ,UAAU;CACjE,IAAI,gBAAgB;EAClB,MAAM,MAAM,mBAAmB,cAAc;EAC7C,IAAI,KAAK,OAAO;EAEhB,MAAM,IAAI,MACR,wDAAwD,eAAe,4GAGzE;CACF;CAEA,IAAI,QAAQ,YACV,MAAM,IAAI,MACR,4CAA4C,QAAQ,WAAW,oJAGjE;CAGF,MAAM,IAAI,MACR,8JAGF;AACF;;;;;;;;;;;AAYA,MAAa,iBAAiB,OAC5B,QACA,UAAiC,CAAC,MACA;CAClC,MAAM,MAAM,MAAM,gBAAgB,OAAO;CACzC,MAAM,QAAQ,QAAQ,OAAO,OAAO;CACpC,IAAI;EACF,MAAM,OAAO,KAAK,GAAG;CACvB,SAAS,KAAK;EACZ,MAAM,QAAQ,OAAO,UAAU,kBAAkB,OAAO,QAAQ,KAAK;EACrE,MAAM,IAAI,MACR,iCAAiC,MAAM,0CACvC,EAAE,OAAO,IAAI,CACf;CACF;CACA,OAAO,EAAE,YAAY,OAAO,QAAQ,OAAO,OAAO,IAAI,KAAK,IAAI,IAAI;AACrE;;;;;;;;;;;;;;;;AAiBA,MAAa,gBAAgB,OAAO,WAAuD;CACzF,MAAM,EAAE,SAAS,MAAM,OAAO,MAC5B,uEACF;CACA,IAAI,CAAC,KAAK,IAAI,QAAQ,OAAO;CAE7B,MAAM,EAAE,MAAM,YAAY,MAAM,OAAO,MACrC,qFACF;CACA,QAAQ,QAAQ,IAAI,SAAS,KAAK;AACpC;;;;;;;;;;;;;;;;;;AAmBA,MAAa,YAAY,OAAO,WAAuD;CACrF,MAAM,EAAE,SAAS,MAAM,OAAO,MAC5B;;;iBAIF;CACA,OAAO,KAAK,IAAI,WAAW;AAC7B"}
|