@crashlab/pg-mock 0.1.15 → 1.0.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 +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,8 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
CrashlabUnsupportedPGFeature: () => CrashlabUnsupportedPGFeature,
|
|
33
34
|
PgMock: () => PgMock,
|
|
34
|
-
SimNodeUnsupportedPGFeature: () => SimNodeUnsupportedPGFeature,
|
|
35
35
|
proto: () => protocol_exports
|
|
36
36
|
});
|
|
37
37
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -144,10 +144,10 @@ function parseQueryMsg(data) {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// src/index.ts
|
|
147
|
-
var
|
|
147
|
+
var CrashlabUnsupportedPGFeature = class extends Error {
|
|
148
148
|
constructor(detail) {
|
|
149
|
-
super(`
|
|
150
|
-
this.name = "
|
|
149
|
+
super(`Crashlab: Unsupported PostgreSQL feature: ${detail}`);
|
|
150
|
+
this.name = "CrashlabUnsupportedPGFeature";
|
|
151
151
|
}
|
|
152
152
|
};
|
|
153
153
|
function createPGliteInstance() {
|
|
@@ -425,8 +425,8 @@ var PgMock = class {
|
|
|
425
425
|
};
|
|
426
426
|
// Annotate the CommonJS export names for ESM import in node:
|
|
427
427
|
0 && (module.exports = {
|
|
428
|
+
CrashlabUnsupportedPGFeature,
|
|
428
429
|
PgMock,
|
|
429
|
-
SimNodeUnsupportedPGFeature,
|
|
430
430
|
proto
|
|
431
431
|
});
|
|
432
432
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/protocol.ts"],"sourcesContent":["import type { TcpMockHandler, TcpMockContext, TcpHandlerResult } from '@crashlab/tcp';\nimport * as proto from './protocol.js';\n\nexport class SimNodeUnsupportedPGFeature extends Error {\n constructor(detail: string) {\n super(`SimNode: Unsupported PostgreSQL feature: ${detail}`);\n this.name = 'SimNodeUnsupportedPGFeature';\n }\n}\n\n// ── PGlite helpers ────────────────────────────────────────────────────────────\n\n// Dynamically imported so the package remains optional at load time.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype PGliteInstance = any;\n\nfunction createPGliteInstance(): Promise<PGliteInstance> {\n return import('@electric-sql/pglite').then(({ PGlite }) => new PGlite());\n}\n\n/** Infer a PostgreSQL command tag from the SQL statement and affected-row count. */\nfunction inferTag(sql: string, rowCount: number, affected?: number): string {\n const verb = sql.trim().split(/\\s+/)[0]?.toUpperCase() ?? '';\n switch (verb) {\n case 'SELECT': return `SELECT ${rowCount}`;\n case 'INSERT': return `INSERT 0 ${affected ?? rowCount}`;\n case 'UPDATE': return `UPDATE ${affected ?? rowCount}`;\n case 'DELETE': return `DELETE ${affected ?? rowCount}`;\n case 'CREATE': return 'CREATE TABLE';\n case 'DROP': return 'DROP TABLE';\n case 'BEGIN': return 'BEGIN';\n case 'COMMIT': return 'COMMIT';\n case 'ROLLBACK': return 'ROLLBACK';\n default: return verb;\n }\n}\n\n// ── PgConnection ──────────────────────────────────────────────────────────────\n\nclass PgConnection {\n private _phase: 'startup' | 'ready' = 'startup';\n private _txState: 'I' | 'T' | 'E' = 'I';\n private _buf: Buffer = Buffer.alloc(0);\n\n /** Prepared statements: statement name → SQL. '' = unnamed statement. */\n private _statements = new Map<string, string>();\n /** Bound portals: portal name → { sql, params }. '' = unnamed portal. */\n private _portals = new Map<string, { sql: string; params: string[] }>();\n\n constructor(private _pglite: Promise<PGliteInstance>) {}\n\n async processData(data: Buffer): Promise<Buffer> {\n this._buf = this._buf.length > 0 ? Buffer.concat([this._buf, data]) : data;\n\n // ── Startup / SSL handshake ───────────────────────────────────────────────\n if (this._phase === 'startup') {\n // SSL probe is exactly 8 bytes; startup message has a 4-byte length prefix\n if (this._buf.length < 4) return Buffer.alloc(0);\n const msgLen = this._buf.readInt32BE(0);\n if (this._buf.length < msgLen) return Buffer.alloc(0);\n\n const msg = this._buf.subarray(0, msgLen);\n this._buf = this._buf.subarray(msgLen);\n\n const parsed = proto.parseStartupMsg(msg);\n if ('isSSL' in parsed) return Buffer.from('N');\n this._phase = 'ready';\n return proto.startupResponse();\n }\n\n // ── Ready phase: consume complete framed messages ──────────────────────────\n const responses: Buffer[] = [];\n\n while (this._buf.length >= 5) {\n const msgType = String.fromCharCode(this._buf[0]);\n const msgLen = this._buf.readInt32BE(1); // includes self (4 bytes) but not type byte\n const totalLen = 1 + msgLen;\n if (this._buf.length < totalLen) break; // incomplete — wait for more data\n\n const payload = this._buf.subarray(5, totalLen);\n this._buf = this._buf.subarray(totalLen);\n\n // Simple Query ('Q')\n if (msgType === 'Q') {\n const nul = payload.indexOf(0);\n const sql = payload.toString('utf8', 0, nul >= 0 ? nul : payload.length);\n responses.push(await this._execQuery(sql));\n continue;\n }\n\n switch (msgType) {\n case 'P': { // Parse: statement_name\\0 + query\\0 + Int16(numParams) + ...\n const nameEnd = payload.indexOf(0);\n const stmtName = nameEnd > 0 ? payload.toString('utf8', 0, nameEnd) : '';\n const queryStart = nameEnd + 1;\n const queryEnd = payload.indexOf(0, queryStart);\n const sql = payload.toString('utf8', queryStart, queryEnd >= 0 ? queryEnd : payload.length);\n this._statements.set(stmtName, sql);\n responses.push(proto.parseComplete());\n break;\n }\n case 'B': { // Bind: portal\\0 + statement\\0 + formats + params + result_formats\n const { portal, statement, params } = this._parseBind(payload);\n const boundSql = this._statements.get(statement) ?? '';\n this._portals.set(portal, { sql: boundSql, params });\n responses.push(proto.bindComplete());\n break;\n }\n case 'D': { // Describe\n responses.push(proto.noData());\n break;\n }\n case 'E': { // Execute: portal\\0 + maxRows(Int32)\n const portalEnd = payload.indexOf(0);\n const portalName = portalEnd > 0 ? payload.toString('utf8', 0, portalEnd) : '';\n const bound = this._portals.get(portalName);\n if (bound && bound.sql) {\n const r = await this._execQueryWithParams(bound.sql, bound.params);\n responses.push(r);\n }\n break;\n }\n case 'S': { // Sync\n responses.push(proto.readyForQuery(this._txState));\n break;\n }\n case 'X': { // Terminate\n break;\n }\n default:\n // Silently ignore unknown message types\n break;\n }\n }\n\n return responses.length > 0 ? Buffer.concat(responses) : Buffer.alloc(0);\n }\n\n /** Parse a Bind message payload into portal, statement, and parameter values. */\n private _parseBind(payload: Buffer): { portal: string; statement: string; params: string[] } {\n let off = 0;\n // portal name \\0\n const portalEnd = payload.indexOf(0, off);\n const portal = portalEnd > off ? payload.toString('utf8', off, portalEnd) : '';\n off = portalEnd + 1;\n // statement name \\0\n const stmtEnd = payload.indexOf(0, off);\n const statement = stmtEnd > off ? payload.toString('utf8', off, stmtEnd) : '';\n off = stmtEnd + 1;\n // Int16 num format codes + format codes (skip)\n const numFormats = payload.readInt16BE(off); off += 2;\n off += numFormats * 2; // skip format codes\n // Int16 num params\n const numParams = payload.readInt16BE(off); off += 2;\n const params: string[] = [];\n for (let i = 0; i < numParams; i++) {\n const len = payload.readInt32BE(off); off += 4;\n if (len === -1) {\n params.push('NULL');\n } else {\n params.push(payload.toString('utf8', off, off + len));\n off += len;\n }\n }\n return { portal, statement, params };\n }\n\n /** Execute a parameterized query — substitutes $1, $2, … and runs via PGlite. */\n private async _execQueryWithParams(sql: string, params: string[]): Promise<Buffer> {\n const trimmed = sql.trim();\n const upper = trimmed.toUpperCase();\n\n if (upper === 'BEGIN') { this._txState = 'T'; return proto.commandComplete('BEGIN'); }\n if (upper === 'COMMIT') { this._txState = 'I'; return proto.commandComplete('COMMIT'); }\n if (upper === 'ROLLBACK') { this._txState = 'I'; return proto.commandComplete('ROLLBACK'); }\n\n const db = await this._pglite;\n try {\n // PGlite supports parameterized queries directly\n const result = await db.query(trimmed, params);\n const fields: Array<{ name: string }> = result.fields ?? [];\n const rows: Array<Record<string, unknown>> = result.rows ?? [];\n\n const bufs: Buffer[] = [];\n if (fields.length > 0) {\n bufs.push(proto.rowDescription(fields.map((f: { name: string }) => f.name)));\n for (const row of rows) {\n bufs.push(proto.dataRow(fields.map((f: { name: string }) => {\n const v = row[f.name];\n return v === null || v === undefined ? null : String(v);\n })));\n }\n }\n\n const tag = inferTag(trimmed, rows.length, result.affectedRows as number | undefined);\n bufs.push(proto.commandComplete(tag));\n return Buffer.concat(bufs);\n } catch (err) {\n return proto.errorResponse(err instanceof Error ? err.message : String(err));\n }\n }\n\n private async _execQuery(sql: string): Promise<Buffer> {\n const trimmed = sql.trim();\n const upper = trimmed.toUpperCase();\n\n if (upper === 'BEGIN') { this._txState = 'T'; return Buffer.concat([proto.commandComplete('BEGIN'), proto.readyForQuery('T')]); }\n if (upper === 'COMMIT') { this._txState = 'I'; return Buffer.concat([proto.commandComplete('COMMIT'), proto.readyForQuery('I')]); }\n if (upper === 'ROLLBACK') { this._txState = 'I'; return Buffer.concat([proto.commandComplete('ROLLBACK'), proto.readyForQuery('I')]); }\n\n const db = await this._pglite;\n try {\n const result = await db.query(trimmed);\n const fields: Array<{ name: string }> = result.fields ?? [];\n const rows: Array<Record<string, unknown>> = result.rows ?? [];\n\n const bufs: Buffer[] = [];\n\n if (fields.length > 0) {\n bufs.push(proto.rowDescription(fields.map((f: { name: string }) => f.name)));\n for (const row of rows) {\n bufs.push(proto.dataRow(fields.map((f: { name: string }) => {\n const v = row[f.name];\n return v === null || v === undefined ? null : String(v);\n })));\n }\n }\n\n const tag = inferTag(trimmed, rows.length, result.affectedRows as number | undefined);\n if (tag === 'BEGIN') this._txState = 'T';\n else if (tag === 'COMMIT' || tag === 'ROLLBACK') this._txState = 'I';\n\n bufs.push(proto.commandComplete(tag));\n bufs.push(proto.readyForQuery(this._txState));\n return Buffer.concat(bufs);\n } catch (err) {\n return Buffer.concat([\n proto.errorResponse(err instanceof Error ? err.message : String(err)),\n proto.readyForQuery(this._txState === 'T' ? 'E' : 'I'),\n ]);\n }\n }\n}\n\n// ── PgMock ────────────────────────────────────────────────────────────────────\n\nexport class PgMock {\n /** Shared PGlite instance (one per PgMock, lazy-initialised). */\n private _pglite: Promise<PGliteInstance>;\n /** Tracks all in-flight seed operations so ready() can await them. */\n private _seedPromise: Promise<void> = Promise.resolve();\n private _connections = new Map<number, PgConnection>();\n\n constructor() {\n this._pglite = createPGliteInstance();\n }\n\n /**\n * Resolves once PGlite is initialised AND all pending seedData() calls have\n * been mirrored into PGlite. Await this before making wire-protocol queries\n * in tests that call seedData().\n */\n async ready(): Promise<void> {\n await this._pglite;\n await this._seedPromise;\n }\n\n /**\n * Seed data directly into PGlite.\n * Creates a simple text-column table with the supplied rows.\n */\n seedData(table: string, rows: Array<Record<string, string | null>>): void {\n // Write directly to PGlite (no legacy sync store).\n this._seedPromise = this._seedPromise.then(() => this._seedPGlite(table, rows));\n }\n\n private async _seedPGlite(table: string, rows: Array<Record<string, string | null>>): Promise<void> {\n if (rows.length === 0) return;\n const db = await this._pglite;\n const cols = Object.keys(rows[0]);\n const colDefs = cols.map(c => `\"${c}\" TEXT`).join(', ');\n await db.exec(`CREATE TABLE IF NOT EXISTS \"${table}\" (${colDefs})`);\n for (const row of rows) {\n const vals = cols.map(c => row[c] === null ? 'NULL' : `'${String(row[c]).replace(/'/g, \"''\")}'`).join(', ');\n await db.exec(`INSERT INTO \"${table}\" (${cols.map(c => `\"${c}\"`).join(', ')}) VALUES (${vals})`);\n }\n }\n\n /**\n * Execute a raw SQL query against the embedded PGlite instance.\n * Returns rows as plain objects keyed by column name.\n */\n async query<T = Record<string, unknown>>(sql: string): Promise<{ rows: T[]; fields: Array<{ name: string }> }> {\n const db = await this._pglite;\n return db.query(sql) as Promise<{ rows: T[]; fields: Array<{ name: string }> }>;\n }\n\n createHandler(): TcpMockHandler {\n return async (data: Buffer, ctx: TcpMockContext): Promise<TcpHandlerResult> => {\n if (!this._connections.has(ctx.socketId)) {\n this._connections.set(ctx.socketId, new PgConnection(this._pglite));\n }\n return this._connections.get(ctx.socketId)!.processData(data);\n };\n }\n}\n\nexport { proto };\n","// PG wire protocol v3 encoding helpers\nconst int32 = (n: number): Buffer => { const b = Buffer.alloc(4); b.writeInt32BE(n); return b; };\nconst int16 = (n: number): Buffer => { const b = Buffer.alloc(2); b.writeInt16BE(n); return b; };\nconst cstr = (s: string): Buffer => Buffer.from(s + '\\0', 'utf8');\n\nfunction pgMsg(type: string, payload: Buffer): Buffer {\n return Buffer.concat([Buffer.from(type), int32(payload.length + 4), payload]);\n}\n\nexport const authOk = (): Buffer => pgMsg('R', int32(0));\nexport const paramStatus = (k: string, v: string): Buffer => pgMsg('S', Buffer.concat([cstr(k), cstr(v)]));\nexport const backendKeyData = (): Buffer => pgMsg('K', Buffer.concat([int32(1), int32(1)]));\nexport const readyForQuery = (s: 'I' | 'T' | 'E'): Buffer => pgMsg('Z', Buffer.from(s));\n\nexport function rowDescription(cols: string[]): Buffer {\n const parts: Buffer[] = [int16(cols.length)];\n for (const c of cols) {\n // name, tableOID, colAttrNum, typeOID(text=25), typeSize(-1), typeMod(-1), formatCode(0=text)\n const typeSizeBuf = Buffer.alloc(2);\n typeSizeBuf.writeInt16BE(-1);\n parts.push(cstr(c), int32(0), int16(0), int32(25), typeSizeBuf, int32(-1), int16(0));\n }\n return pgMsg('T', Buffer.concat(parts));\n}\n\nexport function dataRow(vals: (string | null)[]): Buffer {\n const parts: Buffer[] = [int16(vals.length)];\n for (const v of vals) {\n if (v === null) { parts.push(int32(-1)); }\n else { const b = Buffer.from(v, 'utf8'); parts.push(int32(b.length), b); }\n }\n return pgMsg('D', Buffer.concat(parts));\n}\n\nexport const commandComplete = (tag: string): Buffer => pgMsg('C', cstr(tag));\n\nexport function errorResponse(msg: string, code = '42000'): Buffer {\n return pgMsg('E', Buffer.concat([\n Buffer.from('S'), cstr('ERROR'),\n Buffer.from('C'), cstr(code),\n Buffer.from('M'), cstr(msg),\n Buffer.from([0]),\n ]));\n}\n\nexport function startupResponse(): Buffer {\n return Buffer.concat([\n authOk(),\n paramStatus('server_version', '15.0'),\n paramStatus('client_encoding', 'UTF8'),\n backendKeyData(),\n readyForQuery('I'),\n ]);\n}\n\n// Extended protocol response messages\nexport const parseComplete = (): Buffer => pgMsg('1', Buffer.alloc(0));\nexport const bindComplete = (): Buffer => pgMsg('2', Buffer.alloc(0));\nexport const noData = (): Buffer => pgMsg('n', Buffer.alloc(0));\n\n/**\n * Extract the portal name from an Execute message payload so that\n * PgConnection can look up the prepared SQL. Returns empty string\n * (unnamed portal) when no explicit portal name was given.\n */\nexport function parseExecuteMsg(payload: Buffer): string {\n // Execute: portal-name\\0 + maxRows(Int32)\n const nul = payload.indexOf(0);\n return nul > 0 ? payload.toString('utf8', 0, nul) : '';\n}\n\nexport function parseStartupMsg(data: Buffer): { isSSL: boolean } | { user: string; database: string } {\n if (data.length >= 8 && data.readInt32BE(4) === 80877103) return { isSSL: true };\n let off = 8;\n const params: Record<string, string> = {};\n while (off < data.length - 1) {\n const kEnd = data.indexOf(0, off); if (kEnd < 0) break;\n const key = data.toString('utf8', off, kEnd); off = kEnd + 1;\n const vEnd = data.indexOf(0, off); if (vEnd < 0) break;\n params[key] = data.toString('utf8', off, vEnd); off = vEnd + 1;\n }\n return { user: params.user ?? 'unknown', database: params.database ?? 'unknown' };\n}\n\nexport function parseQueryMsg(data: Buffer): string {\n // 'Q' + Int32 length + query\\0\n const nul = data.indexOf(0, 5);\n return data.toString('utf8', 5, nul >= 0 ? nul : data.length);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/protocol.ts"],"sourcesContent":["import type { TcpMockHandler, TcpMockContext, TcpHandlerResult } from '@crashlab/tcp';\nimport * as proto from './protocol.js';\n\nexport class CrashlabUnsupportedPGFeature extends Error {\n constructor(detail: string) {\n super(`Crashlab: Unsupported PostgreSQL feature: ${detail}`);\n this.name = 'CrashlabUnsupportedPGFeature';\n }\n}\n\n// ── PGlite helpers ────────────────────────────────────────────────────────────\n\n// Dynamically imported so the package remains optional at load time.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype PGliteInstance = any;\n\nfunction createPGliteInstance(): Promise<PGliteInstance> {\n return import('@electric-sql/pglite').then(({ PGlite }) => new PGlite());\n}\n\n/** Infer a PostgreSQL command tag from the SQL statement and affected-row count. */\nfunction inferTag(sql: string, rowCount: number, affected?: number): string {\n const verb = sql.trim().split(/\\s+/)[0]?.toUpperCase() ?? '';\n switch (verb) {\n case 'SELECT': return `SELECT ${rowCount}`;\n case 'INSERT': return `INSERT 0 ${affected ?? rowCount}`;\n case 'UPDATE': return `UPDATE ${affected ?? rowCount}`;\n case 'DELETE': return `DELETE ${affected ?? rowCount}`;\n case 'CREATE': return 'CREATE TABLE';\n case 'DROP': return 'DROP TABLE';\n case 'BEGIN': return 'BEGIN';\n case 'COMMIT': return 'COMMIT';\n case 'ROLLBACK': return 'ROLLBACK';\n default: return verb;\n }\n}\n\n// ── PgConnection ──────────────────────────────────────────────────────────────\n\nclass PgConnection {\n private _phase: 'startup' | 'ready' = 'startup';\n private _txState: 'I' | 'T' | 'E' = 'I';\n private _buf: Buffer = Buffer.alloc(0);\n\n /** Prepared statements: statement name → SQL. '' = unnamed statement. */\n private _statements = new Map<string, string>();\n /** Bound portals: portal name → { sql, params }. '' = unnamed portal. */\n private _portals = new Map<string, { sql: string; params: string[] }>();\n\n constructor(private _pglite: Promise<PGliteInstance>) {}\n\n async processData(data: Buffer): Promise<Buffer> {\n this._buf = this._buf.length > 0 ? Buffer.concat([this._buf, data]) : data;\n\n // ── Startup / SSL handshake ───────────────────────────────────────────────\n if (this._phase === 'startup') {\n // SSL probe is exactly 8 bytes; startup message has a 4-byte length prefix\n if (this._buf.length < 4) return Buffer.alloc(0);\n const msgLen = this._buf.readInt32BE(0);\n if (this._buf.length < msgLen) return Buffer.alloc(0);\n\n const msg = this._buf.subarray(0, msgLen);\n this._buf = this._buf.subarray(msgLen);\n\n const parsed = proto.parseStartupMsg(msg);\n if ('isSSL' in parsed) return Buffer.from('N');\n this._phase = 'ready';\n return proto.startupResponse();\n }\n\n // ── Ready phase: consume complete framed messages ──────────────────────────\n const responses: Buffer[] = [];\n\n while (this._buf.length >= 5) {\n const msgType = String.fromCharCode(this._buf[0]);\n const msgLen = this._buf.readInt32BE(1); // includes self (4 bytes) but not type byte\n const totalLen = 1 + msgLen;\n if (this._buf.length < totalLen) break; // incomplete — wait for more data\n\n const payload = this._buf.subarray(5, totalLen);\n this._buf = this._buf.subarray(totalLen);\n\n // Simple Query ('Q')\n if (msgType === 'Q') {\n const nul = payload.indexOf(0);\n const sql = payload.toString('utf8', 0, nul >= 0 ? nul : payload.length);\n responses.push(await this._execQuery(sql));\n continue;\n }\n\n switch (msgType) {\n case 'P': { // Parse: statement_name\\0 + query\\0 + Int16(numParams) + ...\n const nameEnd = payload.indexOf(0);\n const stmtName = nameEnd > 0 ? payload.toString('utf8', 0, nameEnd) : '';\n const queryStart = nameEnd + 1;\n const queryEnd = payload.indexOf(0, queryStart);\n const sql = payload.toString('utf8', queryStart, queryEnd >= 0 ? queryEnd : payload.length);\n this._statements.set(stmtName, sql);\n responses.push(proto.parseComplete());\n break;\n }\n case 'B': { // Bind: portal\\0 + statement\\0 + formats + params + result_formats\n const { portal, statement, params } = this._parseBind(payload);\n const boundSql = this._statements.get(statement) ?? '';\n this._portals.set(portal, { sql: boundSql, params });\n responses.push(proto.bindComplete());\n break;\n }\n case 'D': { // Describe\n responses.push(proto.noData());\n break;\n }\n case 'E': { // Execute: portal\\0 + maxRows(Int32)\n const portalEnd = payload.indexOf(0);\n const portalName = portalEnd > 0 ? payload.toString('utf8', 0, portalEnd) : '';\n const bound = this._portals.get(portalName);\n if (bound && bound.sql) {\n const r = await this._execQueryWithParams(bound.sql, bound.params);\n responses.push(r);\n }\n break;\n }\n case 'S': { // Sync\n responses.push(proto.readyForQuery(this._txState));\n break;\n }\n case 'X': { // Terminate\n break;\n }\n default:\n // Silently ignore unknown message types\n break;\n }\n }\n\n return responses.length > 0 ? Buffer.concat(responses) : Buffer.alloc(0);\n }\n\n /** Parse a Bind message payload into portal, statement, and parameter values. */\n private _parseBind(payload: Buffer): { portal: string; statement: string; params: string[] } {\n let off = 0;\n // portal name \\0\n const portalEnd = payload.indexOf(0, off);\n const portal = portalEnd > off ? payload.toString('utf8', off, portalEnd) : '';\n off = portalEnd + 1;\n // statement name \\0\n const stmtEnd = payload.indexOf(0, off);\n const statement = stmtEnd > off ? payload.toString('utf8', off, stmtEnd) : '';\n off = stmtEnd + 1;\n // Int16 num format codes + format codes (skip)\n const numFormats = payload.readInt16BE(off); off += 2;\n off += numFormats * 2; // skip format codes\n // Int16 num params\n const numParams = payload.readInt16BE(off); off += 2;\n const params: string[] = [];\n for (let i = 0; i < numParams; i++) {\n const len = payload.readInt32BE(off); off += 4;\n if (len === -1) {\n params.push('NULL');\n } else {\n params.push(payload.toString('utf8', off, off + len));\n off += len;\n }\n }\n return { portal, statement, params };\n }\n\n /** Execute a parameterized query — substitutes $1, $2, … and runs via PGlite. */\n private async _execQueryWithParams(sql: string, params: string[]): Promise<Buffer> {\n const trimmed = sql.trim();\n const upper = trimmed.toUpperCase();\n\n if (upper === 'BEGIN') { this._txState = 'T'; return proto.commandComplete('BEGIN'); }\n if (upper === 'COMMIT') { this._txState = 'I'; return proto.commandComplete('COMMIT'); }\n if (upper === 'ROLLBACK') { this._txState = 'I'; return proto.commandComplete('ROLLBACK'); }\n\n const db = await this._pglite;\n try {\n // PGlite supports parameterized queries directly\n const result = await db.query(trimmed, params);\n const fields: Array<{ name: string }> = result.fields ?? [];\n const rows: Array<Record<string, unknown>> = result.rows ?? [];\n\n const bufs: Buffer[] = [];\n if (fields.length > 0) {\n bufs.push(proto.rowDescription(fields.map((f: { name: string }) => f.name)));\n for (const row of rows) {\n bufs.push(proto.dataRow(fields.map((f: { name: string }) => {\n const v = row[f.name];\n return v === null || v === undefined ? null : String(v);\n })));\n }\n }\n\n const tag = inferTag(trimmed, rows.length, result.affectedRows as number | undefined);\n bufs.push(proto.commandComplete(tag));\n return Buffer.concat(bufs);\n } catch (err) {\n return proto.errorResponse(err instanceof Error ? err.message : String(err));\n }\n }\n\n private async _execQuery(sql: string): Promise<Buffer> {\n const trimmed = sql.trim();\n const upper = trimmed.toUpperCase();\n\n if (upper === 'BEGIN') { this._txState = 'T'; return Buffer.concat([proto.commandComplete('BEGIN'), proto.readyForQuery('T')]); }\n if (upper === 'COMMIT') { this._txState = 'I'; return Buffer.concat([proto.commandComplete('COMMIT'), proto.readyForQuery('I')]); }\n if (upper === 'ROLLBACK') { this._txState = 'I'; return Buffer.concat([proto.commandComplete('ROLLBACK'), proto.readyForQuery('I')]); }\n\n const db = await this._pglite;\n try {\n const result = await db.query(trimmed);\n const fields: Array<{ name: string }> = result.fields ?? [];\n const rows: Array<Record<string, unknown>> = result.rows ?? [];\n\n const bufs: Buffer[] = [];\n\n if (fields.length > 0) {\n bufs.push(proto.rowDescription(fields.map((f: { name: string }) => f.name)));\n for (const row of rows) {\n bufs.push(proto.dataRow(fields.map((f: { name: string }) => {\n const v = row[f.name];\n return v === null || v === undefined ? null : String(v);\n })));\n }\n }\n\n const tag = inferTag(trimmed, rows.length, result.affectedRows as number | undefined);\n if (tag === 'BEGIN') this._txState = 'T';\n else if (tag === 'COMMIT' || tag === 'ROLLBACK') this._txState = 'I';\n\n bufs.push(proto.commandComplete(tag));\n bufs.push(proto.readyForQuery(this._txState));\n return Buffer.concat(bufs);\n } catch (err) {\n return Buffer.concat([\n proto.errorResponse(err instanceof Error ? err.message : String(err)),\n proto.readyForQuery(this._txState === 'T' ? 'E' : 'I'),\n ]);\n }\n }\n}\n\n// ── PgMock ────────────────────────────────────────────────────────────────────\n\nexport class PgMock {\n /** Shared PGlite instance (one per PgMock, lazy-initialised). */\n private _pglite: Promise<PGliteInstance>;\n /** Tracks all in-flight seed operations so ready() can await them. */\n private _seedPromise: Promise<void> = Promise.resolve();\n private _connections = new Map<number, PgConnection>();\n\n constructor() {\n this._pglite = createPGliteInstance();\n }\n\n /**\n * Resolves once PGlite is initialised AND all pending seedData() calls have\n * been mirrored into PGlite. Await this before making wire-protocol queries\n * in tests that call seedData().\n */\n async ready(): Promise<void> {\n await this._pglite;\n await this._seedPromise;\n }\n\n /**\n * Seed data directly into PGlite.\n * Creates a simple text-column table with the supplied rows.\n */\n seedData(table: string, rows: Array<Record<string, string | null>>): void {\n // Write directly to PGlite (no legacy sync store).\n this._seedPromise = this._seedPromise.then(() => this._seedPGlite(table, rows));\n }\n\n private async _seedPGlite(table: string, rows: Array<Record<string, string | null>>): Promise<void> {\n if (rows.length === 0) return;\n const db = await this._pglite;\n const cols = Object.keys(rows[0]);\n const colDefs = cols.map(c => `\"${c}\" TEXT`).join(', ');\n await db.exec(`CREATE TABLE IF NOT EXISTS \"${table}\" (${colDefs})`);\n for (const row of rows) {\n const vals = cols.map(c => row[c] === null ? 'NULL' : `'${String(row[c]).replace(/'/g, \"''\")}'`).join(', ');\n await db.exec(`INSERT INTO \"${table}\" (${cols.map(c => `\"${c}\"`).join(', ')}) VALUES (${vals})`);\n }\n }\n\n /**\n * Execute a raw SQL query against the embedded PGlite instance.\n * Returns rows as plain objects keyed by column name.\n */\n async query<T = Record<string, unknown>>(sql: string): Promise<{ rows: T[]; fields: Array<{ name: string }> }> {\n const db = await this._pglite;\n return db.query(sql) as Promise<{ rows: T[]; fields: Array<{ name: string }> }>;\n }\n\n createHandler(): TcpMockHandler {\n return async (data: Buffer, ctx: TcpMockContext): Promise<TcpHandlerResult> => {\n if (!this._connections.has(ctx.socketId)) {\n this._connections.set(ctx.socketId, new PgConnection(this._pglite));\n }\n return this._connections.get(ctx.socketId)!.processData(data);\n };\n }\n}\n\nexport { proto };\n","// PG wire protocol v3 encoding helpers\nconst int32 = (n: number): Buffer => { const b = Buffer.alloc(4); b.writeInt32BE(n); return b; };\nconst int16 = (n: number): Buffer => { const b = Buffer.alloc(2); b.writeInt16BE(n); return b; };\nconst cstr = (s: string): Buffer => Buffer.from(s + '\\0', 'utf8');\n\nfunction pgMsg(type: string, payload: Buffer): Buffer {\n return Buffer.concat([Buffer.from(type), int32(payload.length + 4), payload]);\n}\n\nexport const authOk = (): Buffer => pgMsg('R', int32(0));\nexport const paramStatus = (k: string, v: string): Buffer => pgMsg('S', Buffer.concat([cstr(k), cstr(v)]));\nexport const backendKeyData = (): Buffer => pgMsg('K', Buffer.concat([int32(1), int32(1)]));\nexport const readyForQuery = (s: 'I' | 'T' | 'E'): Buffer => pgMsg('Z', Buffer.from(s));\n\nexport function rowDescription(cols: string[]): Buffer {\n const parts: Buffer[] = [int16(cols.length)];\n for (const c of cols) {\n // name, tableOID, colAttrNum, typeOID(text=25), typeSize(-1), typeMod(-1), formatCode(0=text)\n const typeSizeBuf = Buffer.alloc(2);\n typeSizeBuf.writeInt16BE(-1);\n parts.push(cstr(c), int32(0), int16(0), int32(25), typeSizeBuf, int32(-1), int16(0));\n }\n return pgMsg('T', Buffer.concat(parts));\n}\n\nexport function dataRow(vals: (string | null)[]): Buffer {\n const parts: Buffer[] = [int16(vals.length)];\n for (const v of vals) {\n if (v === null) { parts.push(int32(-1)); }\n else { const b = Buffer.from(v, 'utf8'); parts.push(int32(b.length), b); }\n }\n return pgMsg('D', Buffer.concat(parts));\n}\n\nexport const commandComplete = (tag: string): Buffer => pgMsg('C', cstr(tag));\n\nexport function errorResponse(msg: string, code = '42000'): Buffer {\n return pgMsg('E', Buffer.concat([\n Buffer.from('S'), cstr('ERROR'),\n Buffer.from('C'), cstr(code),\n Buffer.from('M'), cstr(msg),\n Buffer.from([0]),\n ]));\n}\n\nexport function startupResponse(): Buffer {\n return Buffer.concat([\n authOk(),\n paramStatus('server_version', '15.0'),\n paramStatus('client_encoding', 'UTF8'),\n backendKeyData(),\n readyForQuery('I'),\n ]);\n}\n\n// Extended protocol response messages\nexport const parseComplete = (): Buffer => pgMsg('1', Buffer.alloc(0));\nexport const bindComplete = (): Buffer => pgMsg('2', Buffer.alloc(0));\nexport const noData = (): Buffer => pgMsg('n', Buffer.alloc(0));\n\n/**\n * Extract the portal name from an Execute message payload so that\n * PgConnection can look up the prepared SQL. Returns empty string\n * (unnamed portal) when no explicit portal name was given.\n */\nexport function parseExecuteMsg(payload: Buffer): string {\n // Execute: portal-name\\0 + maxRows(Int32)\n const nul = payload.indexOf(0);\n return nul > 0 ? payload.toString('utf8', 0, nul) : '';\n}\n\nexport function parseStartupMsg(data: Buffer): { isSSL: boolean } | { user: string; database: string } {\n if (data.length >= 8 && data.readInt32BE(4) === 80877103) return { isSSL: true };\n let off = 8;\n const params: Record<string, string> = {};\n while (off < data.length - 1) {\n const kEnd = data.indexOf(0, off); if (kEnd < 0) break;\n const key = data.toString('utf8', off, kEnd); off = kEnd + 1;\n const vEnd = data.indexOf(0, off); if (vEnd < 0) break;\n params[key] = data.toString('utf8', off, vEnd); off = vEnd + 1;\n }\n return { user: params.user ?? 'unknown', database: params.database ?? 'unknown' };\n}\n\nexport function parseQueryMsg(data: Buffer): string {\n // 'Q' + Int32 length + query\\0\n const nul = data.indexOf(0, 5);\n return data.toString('utf8', 5, nul >= 0 ? nul : data.length);\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TcpMockHandler } from '@crashlab/tcp';
|
|
2
2
|
import * as proto from './protocol.js';
|
|
3
|
-
export declare class
|
|
3
|
+
export declare class CrashlabUnsupportedPGFeature extends Error {
|
|
4
4
|
constructor(detail: string);
|
|
5
5
|
}
|
|
6
6
|
export declare class PgMock {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAoC,MAAM,eAAe,CAAC;AACtF,OAAO,KAAK,KAAK,MAAM,eAAe,CAAC;AAEvC,qBAAa,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAoC,MAAM,eAAe,CAAC;AACtF,OAAO,KAAK,KAAK,MAAM,eAAe,CAAC;AAEvC,qBAAa,4BAA6B,SAAQ,KAAK;gBACzC,MAAM,EAAE,MAAM;CAI3B;AA8OD,qBAAa,MAAM;IACjB,iEAAiE;IACjE,OAAO,CAAC,OAAO,CAA0B;IACzC,sEAAsE;IACtE,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,YAAY,CAAmC;;IAMvD;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI;YAK3D,WAAW;IAYzB;;;OAGG;IACG,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAAC,MAAM,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;IAK9G,aAAa,IAAI,cAAc;CAQhC;AAED,OAAO,EAAE,KAAK,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -112,10 +112,10 @@ function parseQueryMsg(data) {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
// src/index.ts
|
|
115
|
-
var
|
|
115
|
+
var CrashlabUnsupportedPGFeature = class extends Error {
|
|
116
116
|
constructor(detail) {
|
|
117
|
-
super(`
|
|
118
|
-
this.name = "
|
|
117
|
+
super(`Crashlab: Unsupported PostgreSQL feature: ${detail}`);
|
|
118
|
+
this.name = "CrashlabUnsupportedPGFeature";
|
|
119
119
|
}
|
|
120
120
|
};
|
|
121
121
|
function createPGliteInstance() {
|
|
@@ -392,8 +392,8 @@ var PgMock = class {
|
|
|
392
392
|
}
|
|
393
393
|
};
|
|
394
394
|
export {
|
|
395
|
+
CrashlabUnsupportedPGFeature,
|
|
395
396
|
PgMock,
|
|
396
|
-
SimNodeUnsupportedPGFeature,
|
|
397
397
|
protocol_exports as proto
|
|
398
398
|
};
|
|
399
399
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/protocol.ts","../src/index.ts"],"sourcesContent":["// PG wire protocol v3 encoding helpers\nconst int32 = (n: number): Buffer => { const b = Buffer.alloc(4); b.writeInt32BE(n); return b; };\nconst int16 = (n: number): Buffer => { const b = Buffer.alloc(2); b.writeInt16BE(n); return b; };\nconst cstr = (s: string): Buffer => Buffer.from(s + '\\0', 'utf8');\n\nfunction pgMsg(type: string, payload: Buffer): Buffer {\n return Buffer.concat([Buffer.from(type), int32(payload.length + 4), payload]);\n}\n\nexport const authOk = (): Buffer => pgMsg('R', int32(0));\nexport const paramStatus = (k: string, v: string): Buffer => pgMsg('S', Buffer.concat([cstr(k), cstr(v)]));\nexport const backendKeyData = (): Buffer => pgMsg('K', Buffer.concat([int32(1), int32(1)]));\nexport const readyForQuery = (s: 'I' | 'T' | 'E'): Buffer => pgMsg('Z', Buffer.from(s));\n\nexport function rowDescription(cols: string[]): Buffer {\n const parts: Buffer[] = [int16(cols.length)];\n for (const c of cols) {\n // name, tableOID, colAttrNum, typeOID(text=25), typeSize(-1), typeMod(-1), formatCode(0=text)\n const typeSizeBuf = Buffer.alloc(2);\n typeSizeBuf.writeInt16BE(-1);\n parts.push(cstr(c), int32(0), int16(0), int32(25), typeSizeBuf, int32(-1), int16(0));\n }\n return pgMsg('T', Buffer.concat(parts));\n}\n\nexport function dataRow(vals: (string | null)[]): Buffer {\n const parts: Buffer[] = [int16(vals.length)];\n for (const v of vals) {\n if (v === null) { parts.push(int32(-1)); }\n else { const b = Buffer.from(v, 'utf8'); parts.push(int32(b.length), b); }\n }\n return pgMsg('D', Buffer.concat(parts));\n}\n\nexport const commandComplete = (tag: string): Buffer => pgMsg('C', cstr(tag));\n\nexport function errorResponse(msg: string, code = '42000'): Buffer {\n return pgMsg('E', Buffer.concat([\n Buffer.from('S'), cstr('ERROR'),\n Buffer.from('C'), cstr(code),\n Buffer.from('M'), cstr(msg),\n Buffer.from([0]),\n ]));\n}\n\nexport function startupResponse(): Buffer {\n return Buffer.concat([\n authOk(),\n paramStatus('server_version', '15.0'),\n paramStatus('client_encoding', 'UTF8'),\n backendKeyData(),\n readyForQuery('I'),\n ]);\n}\n\n// Extended protocol response messages\nexport const parseComplete = (): Buffer => pgMsg('1', Buffer.alloc(0));\nexport const bindComplete = (): Buffer => pgMsg('2', Buffer.alloc(0));\nexport const noData = (): Buffer => pgMsg('n', Buffer.alloc(0));\n\n/**\n * Extract the portal name from an Execute message payload so that\n * PgConnection can look up the prepared SQL. Returns empty string\n * (unnamed portal) when no explicit portal name was given.\n */\nexport function parseExecuteMsg(payload: Buffer): string {\n // Execute: portal-name\\0 + maxRows(Int32)\n const nul = payload.indexOf(0);\n return nul > 0 ? payload.toString('utf8', 0, nul) : '';\n}\n\nexport function parseStartupMsg(data: Buffer): { isSSL: boolean } | { user: string; database: string } {\n if (data.length >= 8 && data.readInt32BE(4) === 80877103) return { isSSL: true };\n let off = 8;\n const params: Record<string, string> = {};\n while (off < data.length - 1) {\n const kEnd = data.indexOf(0, off); if (kEnd < 0) break;\n const key = data.toString('utf8', off, kEnd); off = kEnd + 1;\n const vEnd = data.indexOf(0, off); if (vEnd < 0) break;\n params[key] = data.toString('utf8', off, vEnd); off = vEnd + 1;\n }\n return { user: params.user ?? 'unknown', database: params.database ?? 'unknown' };\n}\n\nexport function parseQueryMsg(data: Buffer): string {\n // 'Q' + Int32 length + query\\0\n const nul = data.indexOf(0, 5);\n return data.toString('utf8', 5, nul >= 0 ? nul : data.length);\n}\n","import type { TcpMockHandler, TcpMockContext, TcpHandlerResult } from '@crashlab/tcp';\nimport * as proto from './protocol.js';\n\nexport class SimNodeUnsupportedPGFeature extends Error {\n constructor(detail: string) {\n super(`SimNode: Unsupported PostgreSQL feature: ${detail}`);\n this.name = 'SimNodeUnsupportedPGFeature';\n }\n}\n\n// ── PGlite helpers ────────────────────────────────────────────────────────────\n\n// Dynamically imported so the package remains optional at load time.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype PGliteInstance = any;\n\nfunction createPGliteInstance(): Promise<PGliteInstance> {\n return import('@electric-sql/pglite').then(({ PGlite }) => new PGlite());\n}\n\n/** Infer a PostgreSQL command tag from the SQL statement and affected-row count. */\nfunction inferTag(sql: string, rowCount: number, affected?: number): string {\n const verb = sql.trim().split(/\\s+/)[0]?.toUpperCase() ?? '';\n switch (verb) {\n case 'SELECT': return `SELECT ${rowCount}`;\n case 'INSERT': return `INSERT 0 ${affected ?? rowCount}`;\n case 'UPDATE': return `UPDATE ${affected ?? rowCount}`;\n case 'DELETE': return `DELETE ${affected ?? rowCount}`;\n case 'CREATE': return 'CREATE TABLE';\n case 'DROP': return 'DROP TABLE';\n case 'BEGIN': return 'BEGIN';\n case 'COMMIT': return 'COMMIT';\n case 'ROLLBACK': return 'ROLLBACK';\n default: return verb;\n }\n}\n\n// ── PgConnection ──────────────────────────────────────────────────────────────\n\nclass PgConnection {\n private _phase: 'startup' | 'ready' = 'startup';\n private _txState: 'I' | 'T' | 'E' = 'I';\n private _buf: Buffer = Buffer.alloc(0);\n\n /** Prepared statements: statement name → SQL. '' = unnamed statement. */\n private _statements = new Map<string, string>();\n /** Bound portals: portal name → { sql, params }. '' = unnamed portal. */\n private _portals = new Map<string, { sql: string; params: string[] }>();\n\n constructor(private _pglite: Promise<PGliteInstance>) {}\n\n async processData(data: Buffer): Promise<Buffer> {\n this._buf = this._buf.length > 0 ? Buffer.concat([this._buf, data]) : data;\n\n // ── Startup / SSL handshake ───────────────────────────────────────────────\n if (this._phase === 'startup') {\n // SSL probe is exactly 8 bytes; startup message has a 4-byte length prefix\n if (this._buf.length < 4) return Buffer.alloc(0);\n const msgLen = this._buf.readInt32BE(0);\n if (this._buf.length < msgLen) return Buffer.alloc(0);\n\n const msg = this._buf.subarray(0, msgLen);\n this._buf = this._buf.subarray(msgLen);\n\n const parsed = proto.parseStartupMsg(msg);\n if ('isSSL' in parsed) return Buffer.from('N');\n this._phase = 'ready';\n return proto.startupResponse();\n }\n\n // ── Ready phase: consume complete framed messages ──────────────────────────\n const responses: Buffer[] = [];\n\n while (this._buf.length >= 5) {\n const msgType = String.fromCharCode(this._buf[0]);\n const msgLen = this._buf.readInt32BE(1); // includes self (4 bytes) but not type byte\n const totalLen = 1 + msgLen;\n if (this._buf.length < totalLen) break; // incomplete — wait for more data\n\n const payload = this._buf.subarray(5, totalLen);\n this._buf = this._buf.subarray(totalLen);\n\n // Simple Query ('Q')\n if (msgType === 'Q') {\n const nul = payload.indexOf(0);\n const sql = payload.toString('utf8', 0, nul >= 0 ? nul : payload.length);\n responses.push(await this._execQuery(sql));\n continue;\n }\n\n switch (msgType) {\n case 'P': { // Parse: statement_name\\0 + query\\0 + Int16(numParams) + ...\n const nameEnd = payload.indexOf(0);\n const stmtName = nameEnd > 0 ? payload.toString('utf8', 0, nameEnd) : '';\n const queryStart = nameEnd + 1;\n const queryEnd = payload.indexOf(0, queryStart);\n const sql = payload.toString('utf8', queryStart, queryEnd >= 0 ? queryEnd : payload.length);\n this._statements.set(stmtName, sql);\n responses.push(proto.parseComplete());\n break;\n }\n case 'B': { // Bind: portal\\0 + statement\\0 + formats + params + result_formats\n const { portal, statement, params } = this._parseBind(payload);\n const boundSql = this._statements.get(statement) ?? '';\n this._portals.set(portal, { sql: boundSql, params });\n responses.push(proto.bindComplete());\n break;\n }\n case 'D': { // Describe\n responses.push(proto.noData());\n break;\n }\n case 'E': { // Execute: portal\\0 + maxRows(Int32)\n const portalEnd = payload.indexOf(0);\n const portalName = portalEnd > 0 ? payload.toString('utf8', 0, portalEnd) : '';\n const bound = this._portals.get(portalName);\n if (bound && bound.sql) {\n const r = await this._execQueryWithParams(bound.sql, bound.params);\n responses.push(r);\n }\n break;\n }\n case 'S': { // Sync\n responses.push(proto.readyForQuery(this._txState));\n break;\n }\n case 'X': { // Terminate\n break;\n }\n default:\n // Silently ignore unknown message types\n break;\n }\n }\n\n return responses.length > 0 ? Buffer.concat(responses) : Buffer.alloc(0);\n }\n\n /** Parse a Bind message payload into portal, statement, and parameter values. */\n private _parseBind(payload: Buffer): { portal: string; statement: string; params: string[] } {\n let off = 0;\n // portal name \\0\n const portalEnd = payload.indexOf(0, off);\n const portal = portalEnd > off ? payload.toString('utf8', off, portalEnd) : '';\n off = portalEnd + 1;\n // statement name \\0\n const stmtEnd = payload.indexOf(0, off);\n const statement = stmtEnd > off ? payload.toString('utf8', off, stmtEnd) : '';\n off = stmtEnd + 1;\n // Int16 num format codes + format codes (skip)\n const numFormats = payload.readInt16BE(off); off += 2;\n off += numFormats * 2; // skip format codes\n // Int16 num params\n const numParams = payload.readInt16BE(off); off += 2;\n const params: string[] = [];\n for (let i = 0; i < numParams; i++) {\n const len = payload.readInt32BE(off); off += 4;\n if (len === -1) {\n params.push('NULL');\n } else {\n params.push(payload.toString('utf8', off, off + len));\n off += len;\n }\n }\n return { portal, statement, params };\n }\n\n /** Execute a parameterized query — substitutes $1, $2, … and runs via PGlite. */\n private async _execQueryWithParams(sql: string, params: string[]): Promise<Buffer> {\n const trimmed = sql.trim();\n const upper = trimmed.toUpperCase();\n\n if (upper === 'BEGIN') { this._txState = 'T'; return proto.commandComplete('BEGIN'); }\n if (upper === 'COMMIT') { this._txState = 'I'; return proto.commandComplete('COMMIT'); }\n if (upper === 'ROLLBACK') { this._txState = 'I'; return proto.commandComplete('ROLLBACK'); }\n\n const db = await this._pglite;\n try {\n // PGlite supports parameterized queries directly\n const result = await db.query(trimmed, params);\n const fields: Array<{ name: string }> = result.fields ?? [];\n const rows: Array<Record<string, unknown>> = result.rows ?? [];\n\n const bufs: Buffer[] = [];\n if (fields.length > 0) {\n bufs.push(proto.rowDescription(fields.map((f: { name: string }) => f.name)));\n for (const row of rows) {\n bufs.push(proto.dataRow(fields.map((f: { name: string }) => {\n const v = row[f.name];\n return v === null || v === undefined ? null : String(v);\n })));\n }\n }\n\n const tag = inferTag(trimmed, rows.length, result.affectedRows as number | undefined);\n bufs.push(proto.commandComplete(tag));\n return Buffer.concat(bufs);\n } catch (err) {\n return proto.errorResponse(err instanceof Error ? err.message : String(err));\n }\n }\n\n private async _execQuery(sql: string): Promise<Buffer> {\n const trimmed = sql.trim();\n const upper = trimmed.toUpperCase();\n\n if (upper === 'BEGIN') { this._txState = 'T'; return Buffer.concat([proto.commandComplete('BEGIN'), proto.readyForQuery('T')]); }\n if (upper === 'COMMIT') { this._txState = 'I'; return Buffer.concat([proto.commandComplete('COMMIT'), proto.readyForQuery('I')]); }\n if (upper === 'ROLLBACK') { this._txState = 'I'; return Buffer.concat([proto.commandComplete('ROLLBACK'), proto.readyForQuery('I')]); }\n\n const db = await this._pglite;\n try {\n const result = await db.query(trimmed);\n const fields: Array<{ name: string }> = result.fields ?? [];\n const rows: Array<Record<string, unknown>> = result.rows ?? [];\n\n const bufs: Buffer[] = [];\n\n if (fields.length > 0) {\n bufs.push(proto.rowDescription(fields.map((f: { name: string }) => f.name)));\n for (const row of rows) {\n bufs.push(proto.dataRow(fields.map((f: { name: string }) => {\n const v = row[f.name];\n return v === null || v === undefined ? null : String(v);\n })));\n }\n }\n\n const tag = inferTag(trimmed, rows.length, result.affectedRows as number | undefined);\n if (tag === 'BEGIN') this._txState = 'T';\n else if (tag === 'COMMIT' || tag === 'ROLLBACK') this._txState = 'I';\n\n bufs.push(proto.commandComplete(tag));\n bufs.push(proto.readyForQuery(this._txState));\n return Buffer.concat(bufs);\n } catch (err) {\n return Buffer.concat([\n proto.errorResponse(err instanceof Error ? err.message : String(err)),\n proto.readyForQuery(this._txState === 'T' ? 'E' : 'I'),\n ]);\n }\n }\n}\n\n// ── PgMock ────────────────────────────────────────────────────────────────────\n\nexport class PgMock {\n /** Shared PGlite instance (one per PgMock, lazy-initialised). */\n private _pglite: Promise<PGliteInstance>;\n /** Tracks all in-flight seed operations so ready() can await them. */\n private _seedPromise: Promise<void> = Promise.resolve();\n private _connections = new Map<number, PgConnection>();\n\n constructor() {\n this._pglite = createPGliteInstance();\n }\n\n /**\n * Resolves once PGlite is initialised AND all pending seedData() calls have\n * been mirrored into PGlite. Await this before making wire-protocol queries\n * in tests that call seedData().\n */\n async ready(): Promise<void> {\n await this._pglite;\n await this._seedPromise;\n }\n\n /**\n * Seed data directly into PGlite.\n * Creates a simple text-column table with the supplied rows.\n */\n seedData(table: string, rows: Array<Record<string, string | null>>): void {\n // Write directly to PGlite (no legacy sync store).\n this._seedPromise = this._seedPromise.then(() => this._seedPGlite(table, rows));\n }\n\n private async _seedPGlite(table: string, rows: Array<Record<string, string | null>>): Promise<void> {\n if (rows.length === 0) return;\n const db = await this._pglite;\n const cols = Object.keys(rows[0]);\n const colDefs = cols.map(c => `\"${c}\" TEXT`).join(', ');\n await db.exec(`CREATE TABLE IF NOT EXISTS \"${table}\" (${colDefs})`);\n for (const row of rows) {\n const vals = cols.map(c => row[c] === null ? 'NULL' : `'${String(row[c]).replace(/'/g, \"''\")}'`).join(', ');\n await db.exec(`INSERT INTO \"${table}\" (${cols.map(c => `\"${c}\"`).join(', ')}) VALUES (${vals})`);\n }\n }\n\n /**\n * Execute a raw SQL query against the embedded PGlite instance.\n * Returns rows as plain objects keyed by column name.\n */\n async query<T = Record<string, unknown>>(sql: string): Promise<{ rows: T[]; fields: Array<{ name: string }> }> {\n const db = await this._pglite;\n return db.query(sql) as Promise<{ rows: T[]; fields: Array<{ name: string }> }>;\n }\n\n createHandler(): TcpMockHandler {\n return async (data: Buffer, ctx: TcpMockContext): Promise<TcpHandlerResult> => {\n if (!this._connections.has(ctx.socketId)) {\n this._connections.set(ctx.socketId, new PgConnection(this._pglite));\n }\n return this._connections.get(ctx.socketId)!.processData(data);\n };\n }\n}\n\nexport { proto };\n"],"mappings":";;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,IAAM,QAAQ,CAAC,MAAsB;AAAE,QAAM,IAAI,OAAO,MAAM,CAAC;AAAG,IAAE,aAAa,CAAC;AAAG,SAAO;AAAG;AAC/F,IAAM,QAAQ,CAAC,MAAsB;AAAE,QAAM,IAAI,OAAO,MAAM,CAAC;AAAG,IAAE,aAAa,CAAC;AAAG,SAAO;AAAG;AAC/F,IAAM,OAAO,CAAC,MAAsB,OAAO,KAAK,IAAI,MAAM,MAAM;AAEhE,SAAS,MAAM,MAAc,SAAyB;AACpD,SAAO,OAAO,OAAO,CAAC,OAAO,KAAK,IAAI,GAAG,MAAM,QAAQ,SAAS,CAAC,GAAG,OAAO,CAAC;AAC9E;AAEO,IAAM,SAAS,MAAc,MAAM,KAAK,MAAM,CAAC,CAAC;AAChD,IAAM,cAAc,CAAC,GAAW,MAAsB,MAAM,KAAK,OAAO,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AAClG,IAAM,iBAAiB,MAAc,MAAM,KAAK,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;AACnF,IAAM,gBAAgB,CAAC,MAA+B,MAAM,KAAK,OAAO,KAAK,CAAC,CAAC;AAE/E,SAAS,eAAe,MAAwB;AACrD,QAAM,QAAkB,CAAC,MAAM,KAAK,MAAM,CAAC;AAC3C,aAAW,KAAK,MAAM;AAEpB,UAAM,cAAc,OAAO,MAAM,CAAC;AAClC,gBAAY,aAAa,EAAE;AAC3B,UAAM,KAAK,KAAK,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,EAAE,GAAG,aAAa,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC;AAAA,EACrF;AACA,SAAO,MAAM,KAAK,OAAO,OAAO,KAAK,CAAC;AACxC;AAEO,SAAS,QAAQ,MAAiC;AACvD,QAAM,QAAkB,CAAC,MAAM,KAAK,MAAM,CAAC;AAC3C,aAAW,KAAK,MAAM;AACpB,QAAI,MAAM,MAAM;AAAE,YAAM,KAAK,MAAM,EAAE,CAAC;AAAA,IAAG,OACpC;AAAE,YAAM,IAAI,OAAO,KAAK,GAAG,MAAM;AAAG,YAAM,KAAK,MAAM,EAAE,MAAM,GAAG,CAAC;AAAA,IAAG;AAAA,EAC3E;AACA,SAAO,MAAM,KAAK,OAAO,OAAO,KAAK,CAAC;AACxC;AAEO,IAAM,kBAAkB,CAAC,QAAwB,MAAM,KAAK,KAAK,GAAG,CAAC;AAErE,SAAS,cAAc,KAAa,OAAO,SAAiB;AACjE,SAAO,MAAM,KAAK,OAAO,OAAO;AAAA,IAC9B,OAAO,KAAK,GAAG;AAAA,IAAG,KAAK,OAAO;AAAA,IAC9B,OAAO,KAAK,GAAG;AAAA,IAAG,KAAK,IAAI;AAAA,IAC3B,OAAO,KAAK,GAAG;AAAA,IAAG,KAAK,GAAG;AAAA,IAC1B,OAAO,KAAK,CAAC,CAAC,CAAC;AAAA,EACjB,CAAC,CAAC;AACJ;AAEO,SAAS,kBAA0B;AACxC,SAAO,OAAO,OAAO;AAAA,IACnB,OAAO;AAAA,IACP,YAAY,kBAAkB,MAAM;AAAA,IACpC,YAAY,mBAAmB,MAAM;AAAA,IACrC,eAAe;AAAA,IACf,cAAc,GAAG;AAAA,EACnB,CAAC;AACH;AAGO,IAAM,gBAAiB,MAAc,MAAM,KAAK,OAAO,MAAM,CAAC,CAAC;AAC/D,IAAM,eAAiB,MAAc,MAAM,KAAK,OAAO,MAAM,CAAC,CAAC;AAC/D,IAAM,SAAiB,MAAc,MAAM,KAAK,OAAO,MAAM,CAAC,CAAC;AAO/D,SAAS,gBAAgB,SAAyB;AAEvD,QAAM,MAAM,QAAQ,QAAQ,CAAC;AAC7B,SAAO,MAAM,IAAI,QAAQ,SAAS,QAAQ,GAAG,GAAG,IAAI;AACtD;AAEO,SAAS,gBAAgB,MAAuE;AACrG,MAAI,KAAK,UAAU,KAAK,KAAK,YAAY,CAAC,MAAM,SAAU,QAAO,EAAE,OAAO,KAAK;AAC/E,MAAI,MAAM;AACV,QAAM,SAAiC,CAAC;AACxC,SAAO,MAAM,KAAK,SAAS,GAAG;AAC5B,UAAM,OAAO,KAAK,QAAQ,GAAG,GAAG;AAAG,QAAI,OAAO,EAAG;AACjD,UAAM,MAAM,KAAK,SAAS,QAAQ,KAAK,IAAI;AAAG,UAAM,OAAO;AAC3D,UAAM,OAAO,KAAK,QAAQ,GAAG,GAAG;AAAG,QAAI,OAAO,EAAG;AACjD,WAAO,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK,IAAI;AAAG,UAAM,OAAO;AAAA,EAC/D;AACA,SAAO,EAAE,MAAM,OAAO,QAAQ,WAAW,UAAU,OAAO,YAAY,UAAU;AAClF;AAEO,SAAS,cAAc,MAAsB;AAElD,QAAM,MAAM,KAAK,QAAQ,GAAG,CAAC;AAC7B,SAAO,KAAK,SAAS,QAAQ,GAAG,OAAO,IAAI,MAAM,KAAK,MAAM;AAC9D;;;ACrFO,IAAM,8BAAN,cAA0C,MAAM;AAAA,EACrD,YAAY,QAAgB;AAC1B,UAAM,4CAA4C,MAAM,EAAE;AAC1D,SAAK,OAAO;AAAA,EACd;AACF;AAQA,SAAS,uBAAgD;AACvD,SAAO,OAAO,sBAAsB,EAAE,KAAK,CAAC,EAAE,OAAO,MAAM,IAAI,OAAO,CAAC;AACzE;AAGA,SAAS,SAAS,KAAa,UAAkB,UAA2B;AAC1E,QAAM,OAAO,IAAI,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC,GAAG,YAAY,KAAK;AAC1D,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAU,aAAO,UAAU,QAAQ;AAAA,IACxC,KAAK;AAAU,aAAO,YAAY,YAAY,QAAQ;AAAA,IACtD,KAAK;AAAU,aAAO,UAAU,YAAY,QAAQ;AAAA,IACpD,KAAK;AAAU,aAAO,UAAU,YAAY,QAAQ;AAAA,IACpD,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAY,aAAO;AAAA,IACxB;AAAe,aAAO;AAAA,EACxB;AACF;AAIA,IAAM,eAAN,MAAmB;AAAA,EAUjB,YAAoB,SAAkC;AAAlC;AAAA,EAAmC;AAAA,EAT/C,SAA8B;AAAA,EAC9B,WAA4B;AAAA,EAC5B,OAAe,OAAO,MAAM,CAAC;AAAA;AAAA,EAG7B,cAAc,oBAAI,IAAoB;AAAA;AAAA,EAEtC,WAAW,oBAAI,IAA+C;AAAA,EAItE,MAAM,YAAY,MAA+B;AAC/C,SAAK,OAAO,KAAK,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,KAAK,MAAM,IAAI,CAAC,IAAI;AAGtE,QAAI,KAAK,WAAW,WAAW;AAE7B,UAAI,KAAK,KAAK,SAAS,EAAG,QAAO,OAAO,MAAM,CAAC;AAC/C,YAAM,SAAS,KAAK,KAAK,YAAY,CAAC;AACtC,UAAI,KAAK,KAAK,SAAS,OAAQ,QAAO,OAAO,MAAM,CAAC;AAEpD,YAAM,MAAM,KAAK,KAAK,SAAS,GAAG,MAAM;AACxC,WAAK,OAAO,KAAK,KAAK,SAAS,MAAM;AAErC,YAAM,SAAe,gBAAgB,GAAG;AACxC,UAAI,WAAW,OAAQ,QAAO,OAAO,KAAK,GAAG;AAC7C,WAAK,SAAS;AACd,aAAa,gBAAgB;AAAA,IAC/B;AAGA,UAAM,YAAsB,CAAC;AAE7B,WAAO,KAAK,KAAK,UAAU,GAAG;AAC5B,YAAM,UAAU,OAAO,aAAa,KAAK,KAAK,CAAC,CAAC;AAChD,YAAM,SAAU,KAAK,KAAK,YAAY,CAAC;AACvC,YAAM,WAAW,IAAI;AACrB,UAAI,KAAK,KAAK,SAAS,SAAU;AAEjC,YAAM,UAAU,KAAK,KAAK,SAAS,GAAG,QAAQ;AAC9C,WAAK,OAAO,KAAK,KAAK,SAAS,QAAQ;AAGvC,UAAI,YAAY,KAAK;AACnB,cAAM,MAAM,QAAQ,QAAQ,CAAC;AAC7B,cAAM,MAAM,QAAQ,SAAS,QAAQ,GAAG,OAAO,IAAI,MAAM,QAAQ,MAAM;AACvE,kBAAU,KAAK,MAAM,KAAK,WAAW,GAAG,CAAC;AACzC;AAAA,MACF;AAEA,cAAQ,SAAS;AAAA,QACf,KAAK,KAAK;AACR,gBAAM,UAAU,QAAQ,QAAQ,CAAC;AACjC,gBAAM,WAAW,UAAU,IAAI,QAAQ,SAAS,QAAQ,GAAG,OAAO,IAAI;AACtE,gBAAM,aAAa,UAAU;AAC7B,gBAAM,WAAW,QAAQ,QAAQ,GAAG,UAAU;AAC9C,gBAAM,MAAM,QAAQ,SAAS,QAAQ,YAAY,YAAY,IAAI,WAAW,QAAQ,MAAM;AAC1F,eAAK,YAAY,IAAI,UAAU,GAAG;AAClC,oBAAU,KAAW,cAAc,CAAC;AACpC;AAAA,QACF;AAAA,QACA,KAAK,KAAK;AACR,gBAAM,EAAE,QAAQ,WAAW,OAAO,IAAI,KAAK,WAAW,OAAO;AAC7D,gBAAM,WAAW,KAAK,YAAY,IAAI,SAAS,KAAK;AACpD,eAAK,SAAS,IAAI,QAAQ,EAAE,KAAK,UAAU,OAAO,CAAC;AACnD,oBAAU,KAAW,aAAa,CAAC;AACnC;AAAA,QACF;AAAA,QACA,KAAK,KAAK;AACR,oBAAU,KAAW,OAAO,CAAC;AAC7B;AAAA,QACF;AAAA,QACA,KAAK,KAAK;AACR,gBAAM,YAAY,QAAQ,QAAQ,CAAC;AACnC,gBAAM,aAAa,YAAY,IAAI,QAAQ,SAAS,QAAQ,GAAG,SAAS,IAAI;AAC5E,gBAAM,QAAQ,KAAK,SAAS,IAAI,UAAU;AAC1C,cAAI,SAAS,MAAM,KAAK;AACtB,kBAAM,IAAI,MAAM,KAAK,qBAAqB,MAAM,KAAK,MAAM,MAAM;AACjE,sBAAU,KAAK,CAAC;AAAA,UAClB;AACA;AAAA,QACF;AAAA,QACA,KAAK,KAAK;AACR,oBAAU,KAAW,cAAc,KAAK,QAAQ,CAAC;AACjD;AAAA,QACF;AAAA,QACA,KAAK,KAAK;AACR;AAAA,QACF;AAAA,QACA;AAEE;AAAA,MACJ;AAAA,IACF;AAEA,WAAO,UAAU,SAAS,IAAI,OAAO,OAAO,SAAS,IAAI,OAAO,MAAM,CAAC;AAAA,EACzE;AAAA;AAAA,EAGQ,WAAW,SAA0E;AAC3F,QAAI,MAAM;AAEV,UAAM,YAAY,QAAQ,QAAQ,GAAG,GAAG;AACxC,UAAM,SAAS,YAAY,MAAM,QAAQ,SAAS,QAAQ,KAAK,SAAS,IAAI;AAC5E,UAAM,YAAY;AAElB,UAAM,UAAU,QAAQ,QAAQ,GAAG,GAAG;AACtC,UAAM,YAAY,UAAU,MAAM,QAAQ,SAAS,QAAQ,KAAK,OAAO,IAAI;AAC3E,UAAM,UAAU;AAEhB,UAAM,aAAa,QAAQ,YAAY,GAAG;AAAG,WAAO;AACpD,WAAO,aAAa;AAEpB,UAAM,YAAY,QAAQ,YAAY,GAAG;AAAG,WAAO;AACnD,UAAM,SAAmB,CAAC;AAC1B,aAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,YAAM,MAAM,QAAQ,YAAY,GAAG;AAAG,aAAO;AAC7C,UAAI,QAAQ,IAAI;AACd,eAAO,KAAK,MAAM;AAAA,MACpB,OAAO;AACL,eAAO,KAAK,QAAQ,SAAS,QAAQ,KAAK,MAAM,GAAG,CAAC;AACpD,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO,EAAE,QAAQ,WAAW,OAAO;AAAA,EACrC;AAAA;AAAA,EAGA,MAAc,qBAAqB,KAAa,QAAmC;AACjF,UAAM,UAAU,IAAI,KAAK;AACzB,UAAM,QAAQ,QAAQ,YAAY;AAElC,QAAI,UAAU,SAAY;AAAE,WAAK,WAAW;AAAK,aAAa,gBAAgB,OAAO;AAAA,IAAG;AACxF,QAAI,UAAU,UAAY;AAAE,WAAK,WAAW;AAAK,aAAa,gBAAgB,QAAQ;AAAA,IAAG;AACzF,QAAI,UAAU,YAAY;AAAE,WAAK,WAAW;AAAK,aAAa,gBAAgB,UAAU;AAAA,IAAG;AAE3F,UAAM,KAAK,MAAM,KAAK;AACtB,QAAI;AAEF,YAAM,SAAS,MAAM,GAAG,MAAM,SAAS,MAAM;AAC7C,YAAM,SAAkC,OAAO,UAAU,CAAC;AAC1D,YAAM,OAAyC,OAAO,QAAQ,CAAC;AAE/D,YAAM,OAAiB,CAAC;AACxB,UAAI,OAAO,SAAS,GAAG;AACrB,aAAK,KAAW,eAAe,OAAO,IAAI,CAAC,MAAwB,EAAE,IAAI,CAAC,CAAC;AAC3E,mBAAW,OAAO,MAAM;AACtB,eAAK,KAAW,QAAQ,OAAO,IAAI,CAAC,MAAwB;AAC1D,kBAAM,IAAI,IAAI,EAAE,IAAI;AACpB,mBAAO,MAAM,QAAQ,MAAM,SAAY,OAAO,OAAO,CAAC;AAAA,UACxD,CAAC,CAAC,CAAC;AAAA,QACL;AAAA,MACF;AAEA,YAAM,MAAM,SAAS,SAAS,KAAK,QAAQ,OAAO,YAAkC;AACpF,WAAK,KAAW,gBAAgB,GAAG,CAAC;AACpC,aAAO,OAAO,OAAO,IAAI;AAAA,IAC3B,SAAS,KAAK;AACZ,aAAa,cAAc,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC7E;AAAA,EACF;AAAA,EAEA,MAAc,WAAW,KAA8B;AACrD,UAAM,UAAU,IAAI,KAAK;AACzB,UAAM,QAAU,QAAQ,YAAY;AAEpC,QAAI,UAAU,SAAY;AAAE,WAAK,WAAW;AAAK,aAAO,OAAO,OAAO,CAAO,gBAAgB,OAAO,GAAY,cAAc,GAAG,CAAC,CAAC;AAAA,IAAG;AACtI,QAAI,UAAU,UAAY;AAAE,WAAK,WAAW;AAAK,aAAO,OAAO,OAAO,CAAO,gBAAgB,QAAQ,GAAW,cAAc,GAAG,CAAC,CAAC;AAAA,IAAG;AACtI,QAAI,UAAU,YAAY;AAAE,WAAK,WAAW;AAAK,aAAO,OAAO,OAAO,CAAO,gBAAgB,UAAU,GAAS,cAAc,GAAG,CAAC,CAAC;AAAA,IAAG;AAEtI,UAAM,KAAK,MAAM,KAAK;AACtB,QAAI;AACF,YAAM,SAAS,MAAM,GAAG,MAAM,OAAO;AACrC,YAAM,SAAkC,OAAO,UAAU,CAAC;AAC1D,YAAM,OAAyC,OAAO,QAAS,CAAC;AAEhE,YAAM,OAAiB,CAAC;AAExB,UAAI,OAAO,SAAS,GAAG;AACrB,aAAK,KAAW,eAAe,OAAO,IAAI,CAAC,MAAwB,EAAE,IAAI,CAAC,CAAC;AAC3E,mBAAW,OAAO,MAAM;AACtB,eAAK,KAAW,QAAQ,OAAO,IAAI,CAAC,MAAwB;AAC1D,kBAAM,IAAI,IAAI,EAAE,IAAI;AACpB,mBAAO,MAAM,QAAQ,MAAM,SAAY,OAAO,OAAO,CAAC;AAAA,UACxD,CAAC,CAAC,CAAC;AAAA,QACL;AAAA,MACF;AAEA,YAAM,MAAM,SAAS,SAAS,KAAK,QAAQ,OAAO,YAAkC;AACpF,UAAI,QAAQ,QAAS,MAAK,WAAW;AAAA,eAC5B,QAAQ,YAAY,QAAQ,WAAY,MAAK,WAAW;AAEjE,WAAK,KAAW,gBAAgB,GAAG,CAAC;AACpC,WAAK,KAAW,cAAc,KAAK,QAAQ,CAAC;AAC5C,aAAO,OAAO,OAAO,IAAI;AAAA,IAC3B,SAAS,KAAK;AACZ,aAAO,OAAO,OAAO;AAAA,QACb,cAAc,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAC9D,cAAc,KAAK,aAAa,MAAM,MAAM,GAAG;AAAA,MACvD,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAIO,IAAM,SAAN,MAAa;AAAA;AAAA,EAEV;AAAA;AAAA,EAEA,eAA8B,QAAQ,QAAQ;AAAA,EAC9C,eAAe,oBAAI,IAA0B;AAAA,EAErD,cAAc;AACZ,SAAK,UAAU,qBAAqB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAuB;AAC3B,UAAM,KAAK;AACX,UAAM,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,OAAe,MAAkD;AAExE,SAAK,eAAe,KAAK,aAAa,KAAK,MAAM,KAAK,YAAY,OAAO,IAAI,CAAC;AAAA,EAChF;AAAA,EAEA,MAAc,YAAY,OAAe,MAA2D;AAClG,QAAI,KAAK,WAAW,EAAG;AACvB,UAAM,KAAK,MAAM,KAAK;AACtB,UAAM,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC;AAChC,UAAM,UAAU,KAAK,IAAI,OAAK,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI;AACtD,UAAM,GAAG,KAAK,+BAA+B,KAAK,MAAM,OAAO,GAAG;AAClE,eAAW,OAAO,MAAM;AACtB,YAAM,OAAO,KAAK,IAAI,OAAK,IAAI,CAAC,MAAM,OAAO,SAAS,IAAI,OAAO,IAAI,CAAC,CAAC,EAAE,QAAQ,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAC1G,YAAM,GAAG,KAAK,gBAAgB,KAAK,MAAM,KAAK,IAAI,OAAK,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,aAAa,IAAI,GAAG;AAAA,IACjG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAmC,KAAsE;AAC7G,UAAM,KAAK,MAAM,KAAK;AACtB,WAAO,GAAG,MAAM,GAAG;AAAA,EACrB;AAAA,EAEA,gBAAgC;AAC9B,WAAO,OAAO,MAAc,QAAmD;AAC7E,UAAI,CAAC,KAAK,aAAa,IAAI,IAAI,QAAQ,GAAG;AACxC,aAAK,aAAa,IAAI,IAAI,UAAU,IAAI,aAAa,KAAK,OAAO,CAAC;AAAA,MACpE;AACA,aAAO,KAAK,aAAa,IAAI,IAAI,QAAQ,EAAG,YAAY,IAAI;AAAA,IAC9D;AAAA,EACF;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/protocol.ts","../src/index.ts"],"sourcesContent":["// PG wire protocol v3 encoding helpers\nconst int32 = (n: number): Buffer => { const b = Buffer.alloc(4); b.writeInt32BE(n); return b; };\nconst int16 = (n: number): Buffer => { const b = Buffer.alloc(2); b.writeInt16BE(n); return b; };\nconst cstr = (s: string): Buffer => Buffer.from(s + '\\0', 'utf8');\n\nfunction pgMsg(type: string, payload: Buffer): Buffer {\n return Buffer.concat([Buffer.from(type), int32(payload.length + 4), payload]);\n}\n\nexport const authOk = (): Buffer => pgMsg('R', int32(0));\nexport const paramStatus = (k: string, v: string): Buffer => pgMsg('S', Buffer.concat([cstr(k), cstr(v)]));\nexport const backendKeyData = (): Buffer => pgMsg('K', Buffer.concat([int32(1), int32(1)]));\nexport const readyForQuery = (s: 'I' | 'T' | 'E'): Buffer => pgMsg('Z', Buffer.from(s));\n\nexport function rowDescription(cols: string[]): Buffer {\n const parts: Buffer[] = [int16(cols.length)];\n for (const c of cols) {\n // name, tableOID, colAttrNum, typeOID(text=25), typeSize(-1), typeMod(-1), formatCode(0=text)\n const typeSizeBuf = Buffer.alloc(2);\n typeSizeBuf.writeInt16BE(-1);\n parts.push(cstr(c), int32(0), int16(0), int32(25), typeSizeBuf, int32(-1), int16(0));\n }\n return pgMsg('T', Buffer.concat(parts));\n}\n\nexport function dataRow(vals: (string | null)[]): Buffer {\n const parts: Buffer[] = [int16(vals.length)];\n for (const v of vals) {\n if (v === null) { parts.push(int32(-1)); }\n else { const b = Buffer.from(v, 'utf8'); parts.push(int32(b.length), b); }\n }\n return pgMsg('D', Buffer.concat(parts));\n}\n\nexport const commandComplete = (tag: string): Buffer => pgMsg('C', cstr(tag));\n\nexport function errorResponse(msg: string, code = '42000'): Buffer {\n return pgMsg('E', Buffer.concat([\n Buffer.from('S'), cstr('ERROR'),\n Buffer.from('C'), cstr(code),\n Buffer.from('M'), cstr(msg),\n Buffer.from([0]),\n ]));\n}\n\nexport function startupResponse(): Buffer {\n return Buffer.concat([\n authOk(),\n paramStatus('server_version', '15.0'),\n paramStatus('client_encoding', 'UTF8'),\n backendKeyData(),\n readyForQuery('I'),\n ]);\n}\n\n// Extended protocol response messages\nexport const parseComplete = (): Buffer => pgMsg('1', Buffer.alloc(0));\nexport const bindComplete = (): Buffer => pgMsg('2', Buffer.alloc(0));\nexport const noData = (): Buffer => pgMsg('n', Buffer.alloc(0));\n\n/**\n * Extract the portal name from an Execute message payload so that\n * PgConnection can look up the prepared SQL. Returns empty string\n * (unnamed portal) when no explicit portal name was given.\n */\nexport function parseExecuteMsg(payload: Buffer): string {\n // Execute: portal-name\\0 + maxRows(Int32)\n const nul = payload.indexOf(0);\n return nul > 0 ? payload.toString('utf8', 0, nul) : '';\n}\n\nexport function parseStartupMsg(data: Buffer): { isSSL: boolean } | { user: string; database: string } {\n if (data.length >= 8 && data.readInt32BE(4) === 80877103) return { isSSL: true };\n let off = 8;\n const params: Record<string, string> = {};\n while (off < data.length - 1) {\n const kEnd = data.indexOf(0, off); if (kEnd < 0) break;\n const key = data.toString('utf8', off, kEnd); off = kEnd + 1;\n const vEnd = data.indexOf(0, off); if (vEnd < 0) break;\n params[key] = data.toString('utf8', off, vEnd); off = vEnd + 1;\n }\n return { user: params.user ?? 'unknown', database: params.database ?? 'unknown' };\n}\n\nexport function parseQueryMsg(data: Buffer): string {\n // 'Q' + Int32 length + query\\0\n const nul = data.indexOf(0, 5);\n return data.toString('utf8', 5, nul >= 0 ? nul : data.length);\n}\n","import type { TcpMockHandler, TcpMockContext, TcpHandlerResult } from '@crashlab/tcp';\nimport * as proto from './protocol.js';\n\nexport class CrashlabUnsupportedPGFeature extends Error {\n constructor(detail: string) {\n super(`Crashlab: Unsupported PostgreSQL feature: ${detail}`);\n this.name = 'CrashlabUnsupportedPGFeature';\n }\n}\n\n// ── PGlite helpers ────────────────────────────────────────────────────────────\n\n// Dynamically imported so the package remains optional at load time.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype PGliteInstance = any;\n\nfunction createPGliteInstance(): Promise<PGliteInstance> {\n return import('@electric-sql/pglite').then(({ PGlite }) => new PGlite());\n}\n\n/** Infer a PostgreSQL command tag from the SQL statement and affected-row count. */\nfunction inferTag(sql: string, rowCount: number, affected?: number): string {\n const verb = sql.trim().split(/\\s+/)[0]?.toUpperCase() ?? '';\n switch (verb) {\n case 'SELECT': return `SELECT ${rowCount}`;\n case 'INSERT': return `INSERT 0 ${affected ?? rowCount}`;\n case 'UPDATE': return `UPDATE ${affected ?? rowCount}`;\n case 'DELETE': return `DELETE ${affected ?? rowCount}`;\n case 'CREATE': return 'CREATE TABLE';\n case 'DROP': return 'DROP TABLE';\n case 'BEGIN': return 'BEGIN';\n case 'COMMIT': return 'COMMIT';\n case 'ROLLBACK': return 'ROLLBACK';\n default: return verb;\n }\n}\n\n// ── PgConnection ──────────────────────────────────────────────────────────────\n\nclass PgConnection {\n private _phase: 'startup' | 'ready' = 'startup';\n private _txState: 'I' | 'T' | 'E' = 'I';\n private _buf: Buffer = Buffer.alloc(0);\n\n /** Prepared statements: statement name → SQL. '' = unnamed statement. */\n private _statements = new Map<string, string>();\n /** Bound portals: portal name → { sql, params }. '' = unnamed portal. */\n private _portals = new Map<string, { sql: string; params: string[] }>();\n\n constructor(private _pglite: Promise<PGliteInstance>) {}\n\n async processData(data: Buffer): Promise<Buffer> {\n this._buf = this._buf.length > 0 ? Buffer.concat([this._buf, data]) : data;\n\n // ── Startup / SSL handshake ───────────────────────────────────────────────\n if (this._phase === 'startup') {\n // SSL probe is exactly 8 bytes; startup message has a 4-byte length prefix\n if (this._buf.length < 4) return Buffer.alloc(0);\n const msgLen = this._buf.readInt32BE(0);\n if (this._buf.length < msgLen) return Buffer.alloc(0);\n\n const msg = this._buf.subarray(0, msgLen);\n this._buf = this._buf.subarray(msgLen);\n\n const parsed = proto.parseStartupMsg(msg);\n if ('isSSL' in parsed) return Buffer.from('N');\n this._phase = 'ready';\n return proto.startupResponse();\n }\n\n // ── Ready phase: consume complete framed messages ──────────────────────────\n const responses: Buffer[] = [];\n\n while (this._buf.length >= 5) {\n const msgType = String.fromCharCode(this._buf[0]);\n const msgLen = this._buf.readInt32BE(1); // includes self (4 bytes) but not type byte\n const totalLen = 1 + msgLen;\n if (this._buf.length < totalLen) break; // incomplete — wait for more data\n\n const payload = this._buf.subarray(5, totalLen);\n this._buf = this._buf.subarray(totalLen);\n\n // Simple Query ('Q')\n if (msgType === 'Q') {\n const nul = payload.indexOf(0);\n const sql = payload.toString('utf8', 0, nul >= 0 ? nul : payload.length);\n responses.push(await this._execQuery(sql));\n continue;\n }\n\n switch (msgType) {\n case 'P': { // Parse: statement_name\\0 + query\\0 + Int16(numParams) + ...\n const nameEnd = payload.indexOf(0);\n const stmtName = nameEnd > 0 ? payload.toString('utf8', 0, nameEnd) : '';\n const queryStart = nameEnd + 1;\n const queryEnd = payload.indexOf(0, queryStart);\n const sql = payload.toString('utf8', queryStart, queryEnd >= 0 ? queryEnd : payload.length);\n this._statements.set(stmtName, sql);\n responses.push(proto.parseComplete());\n break;\n }\n case 'B': { // Bind: portal\\0 + statement\\0 + formats + params + result_formats\n const { portal, statement, params } = this._parseBind(payload);\n const boundSql = this._statements.get(statement) ?? '';\n this._portals.set(portal, { sql: boundSql, params });\n responses.push(proto.bindComplete());\n break;\n }\n case 'D': { // Describe\n responses.push(proto.noData());\n break;\n }\n case 'E': { // Execute: portal\\0 + maxRows(Int32)\n const portalEnd = payload.indexOf(0);\n const portalName = portalEnd > 0 ? payload.toString('utf8', 0, portalEnd) : '';\n const bound = this._portals.get(portalName);\n if (bound && bound.sql) {\n const r = await this._execQueryWithParams(bound.sql, bound.params);\n responses.push(r);\n }\n break;\n }\n case 'S': { // Sync\n responses.push(proto.readyForQuery(this._txState));\n break;\n }\n case 'X': { // Terminate\n break;\n }\n default:\n // Silently ignore unknown message types\n break;\n }\n }\n\n return responses.length > 0 ? Buffer.concat(responses) : Buffer.alloc(0);\n }\n\n /** Parse a Bind message payload into portal, statement, and parameter values. */\n private _parseBind(payload: Buffer): { portal: string; statement: string; params: string[] } {\n let off = 0;\n // portal name \\0\n const portalEnd = payload.indexOf(0, off);\n const portal = portalEnd > off ? payload.toString('utf8', off, portalEnd) : '';\n off = portalEnd + 1;\n // statement name \\0\n const stmtEnd = payload.indexOf(0, off);\n const statement = stmtEnd > off ? payload.toString('utf8', off, stmtEnd) : '';\n off = stmtEnd + 1;\n // Int16 num format codes + format codes (skip)\n const numFormats = payload.readInt16BE(off); off += 2;\n off += numFormats * 2; // skip format codes\n // Int16 num params\n const numParams = payload.readInt16BE(off); off += 2;\n const params: string[] = [];\n for (let i = 0; i < numParams; i++) {\n const len = payload.readInt32BE(off); off += 4;\n if (len === -1) {\n params.push('NULL');\n } else {\n params.push(payload.toString('utf8', off, off + len));\n off += len;\n }\n }\n return { portal, statement, params };\n }\n\n /** Execute a parameterized query — substitutes $1, $2, … and runs via PGlite. */\n private async _execQueryWithParams(sql: string, params: string[]): Promise<Buffer> {\n const trimmed = sql.trim();\n const upper = trimmed.toUpperCase();\n\n if (upper === 'BEGIN') { this._txState = 'T'; return proto.commandComplete('BEGIN'); }\n if (upper === 'COMMIT') { this._txState = 'I'; return proto.commandComplete('COMMIT'); }\n if (upper === 'ROLLBACK') { this._txState = 'I'; return proto.commandComplete('ROLLBACK'); }\n\n const db = await this._pglite;\n try {\n // PGlite supports parameterized queries directly\n const result = await db.query(trimmed, params);\n const fields: Array<{ name: string }> = result.fields ?? [];\n const rows: Array<Record<string, unknown>> = result.rows ?? [];\n\n const bufs: Buffer[] = [];\n if (fields.length > 0) {\n bufs.push(proto.rowDescription(fields.map((f: { name: string }) => f.name)));\n for (const row of rows) {\n bufs.push(proto.dataRow(fields.map((f: { name: string }) => {\n const v = row[f.name];\n return v === null || v === undefined ? null : String(v);\n })));\n }\n }\n\n const tag = inferTag(trimmed, rows.length, result.affectedRows as number | undefined);\n bufs.push(proto.commandComplete(tag));\n return Buffer.concat(bufs);\n } catch (err) {\n return proto.errorResponse(err instanceof Error ? err.message : String(err));\n }\n }\n\n private async _execQuery(sql: string): Promise<Buffer> {\n const trimmed = sql.trim();\n const upper = trimmed.toUpperCase();\n\n if (upper === 'BEGIN') { this._txState = 'T'; return Buffer.concat([proto.commandComplete('BEGIN'), proto.readyForQuery('T')]); }\n if (upper === 'COMMIT') { this._txState = 'I'; return Buffer.concat([proto.commandComplete('COMMIT'), proto.readyForQuery('I')]); }\n if (upper === 'ROLLBACK') { this._txState = 'I'; return Buffer.concat([proto.commandComplete('ROLLBACK'), proto.readyForQuery('I')]); }\n\n const db = await this._pglite;\n try {\n const result = await db.query(trimmed);\n const fields: Array<{ name: string }> = result.fields ?? [];\n const rows: Array<Record<string, unknown>> = result.rows ?? [];\n\n const bufs: Buffer[] = [];\n\n if (fields.length > 0) {\n bufs.push(proto.rowDescription(fields.map((f: { name: string }) => f.name)));\n for (const row of rows) {\n bufs.push(proto.dataRow(fields.map((f: { name: string }) => {\n const v = row[f.name];\n return v === null || v === undefined ? null : String(v);\n })));\n }\n }\n\n const tag = inferTag(trimmed, rows.length, result.affectedRows as number | undefined);\n if (tag === 'BEGIN') this._txState = 'T';\n else if (tag === 'COMMIT' || tag === 'ROLLBACK') this._txState = 'I';\n\n bufs.push(proto.commandComplete(tag));\n bufs.push(proto.readyForQuery(this._txState));\n return Buffer.concat(bufs);\n } catch (err) {\n return Buffer.concat([\n proto.errorResponse(err instanceof Error ? err.message : String(err)),\n proto.readyForQuery(this._txState === 'T' ? 'E' : 'I'),\n ]);\n }\n }\n}\n\n// ── PgMock ────────────────────────────────────────────────────────────────────\n\nexport class PgMock {\n /** Shared PGlite instance (one per PgMock, lazy-initialised). */\n private _pglite: Promise<PGliteInstance>;\n /** Tracks all in-flight seed operations so ready() can await them. */\n private _seedPromise: Promise<void> = Promise.resolve();\n private _connections = new Map<number, PgConnection>();\n\n constructor() {\n this._pglite = createPGliteInstance();\n }\n\n /**\n * Resolves once PGlite is initialised AND all pending seedData() calls have\n * been mirrored into PGlite. Await this before making wire-protocol queries\n * in tests that call seedData().\n */\n async ready(): Promise<void> {\n await this._pglite;\n await this._seedPromise;\n }\n\n /**\n * Seed data directly into PGlite.\n * Creates a simple text-column table with the supplied rows.\n */\n seedData(table: string, rows: Array<Record<string, string | null>>): void {\n // Write directly to PGlite (no legacy sync store).\n this._seedPromise = this._seedPromise.then(() => this._seedPGlite(table, rows));\n }\n\n private async _seedPGlite(table: string, rows: Array<Record<string, string | null>>): Promise<void> {\n if (rows.length === 0) return;\n const db = await this._pglite;\n const cols = Object.keys(rows[0]);\n const colDefs = cols.map(c => `\"${c}\" TEXT`).join(', ');\n await db.exec(`CREATE TABLE IF NOT EXISTS \"${table}\" (${colDefs})`);\n for (const row of rows) {\n const vals = cols.map(c => row[c] === null ? 'NULL' : `'${String(row[c]).replace(/'/g, \"''\")}'`).join(', ');\n await db.exec(`INSERT INTO \"${table}\" (${cols.map(c => `\"${c}\"`).join(', ')}) VALUES (${vals})`);\n }\n }\n\n /**\n * Execute a raw SQL query against the embedded PGlite instance.\n * Returns rows as plain objects keyed by column name.\n */\n async query<T = Record<string, unknown>>(sql: string): Promise<{ rows: T[]; fields: Array<{ name: string }> }> {\n const db = await this._pglite;\n return db.query(sql) as Promise<{ rows: T[]; fields: Array<{ name: string }> }>;\n }\n\n createHandler(): TcpMockHandler {\n return async (data: Buffer, ctx: TcpMockContext): Promise<TcpHandlerResult> => {\n if (!this._connections.has(ctx.socketId)) {\n this._connections.set(ctx.socketId, new PgConnection(this._pglite));\n }\n return this._connections.get(ctx.socketId)!.processData(data);\n };\n }\n}\n\nexport { proto };\n"],"mappings":";;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,IAAM,QAAQ,CAAC,MAAsB;AAAE,QAAM,IAAI,OAAO,MAAM,CAAC;AAAG,IAAE,aAAa,CAAC;AAAG,SAAO;AAAG;AAC/F,IAAM,QAAQ,CAAC,MAAsB;AAAE,QAAM,IAAI,OAAO,MAAM,CAAC;AAAG,IAAE,aAAa,CAAC;AAAG,SAAO;AAAG;AAC/F,IAAM,OAAO,CAAC,MAAsB,OAAO,KAAK,IAAI,MAAM,MAAM;AAEhE,SAAS,MAAM,MAAc,SAAyB;AACpD,SAAO,OAAO,OAAO,CAAC,OAAO,KAAK,IAAI,GAAG,MAAM,QAAQ,SAAS,CAAC,GAAG,OAAO,CAAC;AAC9E;AAEO,IAAM,SAAS,MAAc,MAAM,KAAK,MAAM,CAAC,CAAC;AAChD,IAAM,cAAc,CAAC,GAAW,MAAsB,MAAM,KAAK,OAAO,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AAClG,IAAM,iBAAiB,MAAc,MAAM,KAAK,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;AACnF,IAAM,gBAAgB,CAAC,MAA+B,MAAM,KAAK,OAAO,KAAK,CAAC,CAAC;AAE/E,SAAS,eAAe,MAAwB;AACrD,QAAM,QAAkB,CAAC,MAAM,KAAK,MAAM,CAAC;AAC3C,aAAW,KAAK,MAAM;AAEpB,UAAM,cAAc,OAAO,MAAM,CAAC;AAClC,gBAAY,aAAa,EAAE;AAC3B,UAAM,KAAK,KAAK,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,EAAE,GAAG,aAAa,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC;AAAA,EACrF;AACA,SAAO,MAAM,KAAK,OAAO,OAAO,KAAK,CAAC;AACxC;AAEO,SAAS,QAAQ,MAAiC;AACvD,QAAM,QAAkB,CAAC,MAAM,KAAK,MAAM,CAAC;AAC3C,aAAW,KAAK,MAAM;AACpB,QAAI,MAAM,MAAM;AAAE,YAAM,KAAK,MAAM,EAAE,CAAC;AAAA,IAAG,OACpC;AAAE,YAAM,IAAI,OAAO,KAAK,GAAG,MAAM;AAAG,YAAM,KAAK,MAAM,EAAE,MAAM,GAAG,CAAC;AAAA,IAAG;AAAA,EAC3E;AACA,SAAO,MAAM,KAAK,OAAO,OAAO,KAAK,CAAC;AACxC;AAEO,IAAM,kBAAkB,CAAC,QAAwB,MAAM,KAAK,KAAK,GAAG,CAAC;AAErE,SAAS,cAAc,KAAa,OAAO,SAAiB;AACjE,SAAO,MAAM,KAAK,OAAO,OAAO;AAAA,IAC9B,OAAO,KAAK,GAAG;AAAA,IAAG,KAAK,OAAO;AAAA,IAC9B,OAAO,KAAK,GAAG;AAAA,IAAG,KAAK,IAAI;AAAA,IAC3B,OAAO,KAAK,GAAG;AAAA,IAAG,KAAK,GAAG;AAAA,IAC1B,OAAO,KAAK,CAAC,CAAC,CAAC;AAAA,EACjB,CAAC,CAAC;AACJ;AAEO,SAAS,kBAA0B;AACxC,SAAO,OAAO,OAAO;AAAA,IACnB,OAAO;AAAA,IACP,YAAY,kBAAkB,MAAM;AAAA,IACpC,YAAY,mBAAmB,MAAM;AAAA,IACrC,eAAe;AAAA,IACf,cAAc,GAAG;AAAA,EACnB,CAAC;AACH;AAGO,IAAM,gBAAiB,MAAc,MAAM,KAAK,OAAO,MAAM,CAAC,CAAC;AAC/D,IAAM,eAAiB,MAAc,MAAM,KAAK,OAAO,MAAM,CAAC,CAAC;AAC/D,IAAM,SAAiB,MAAc,MAAM,KAAK,OAAO,MAAM,CAAC,CAAC;AAO/D,SAAS,gBAAgB,SAAyB;AAEvD,QAAM,MAAM,QAAQ,QAAQ,CAAC;AAC7B,SAAO,MAAM,IAAI,QAAQ,SAAS,QAAQ,GAAG,GAAG,IAAI;AACtD;AAEO,SAAS,gBAAgB,MAAuE;AACrG,MAAI,KAAK,UAAU,KAAK,KAAK,YAAY,CAAC,MAAM,SAAU,QAAO,EAAE,OAAO,KAAK;AAC/E,MAAI,MAAM;AACV,QAAM,SAAiC,CAAC;AACxC,SAAO,MAAM,KAAK,SAAS,GAAG;AAC5B,UAAM,OAAO,KAAK,QAAQ,GAAG,GAAG;AAAG,QAAI,OAAO,EAAG;AACjD,UAAM,MAAM,KAAK,SAAS,QAAQ,KAAK,IAAI;AAAG,UAAM,OAAO;AAC3D,UAAM,OAAO,KAAK,QAAQ,GAAG,GAAG;AAAG,QAAI,OAAO,EAAG;AACjD,WAAO,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK,IAAI;AAAG,UAAM,OAAO;AAAA,EAC/D;AACA,SAAO,EAAE,MAAM,OAAO,QAAQ,WAAW,UAAU,OAAO,YAAY,UAAU;AAClF;AAEO,SAAS,cAAc,MAAsB;AAElD,QAAM,MAAM,KAAK,QAAQ,GAAG,CAAC;AAC7B,SAAO,KAAK,SAAS,QAAQ,GAAG,OAAO,IAAI,MAAM,KAAK,MAAM;AAC9D;;;ACrFO,IAAM,+BAAN,cAA2C,MAAM;AAAA,EACtD,YAAY,QAAgB;AAC1B,UAAM,6CAA6C,MAAM,EAAE;AAC3D,SAAK,OAAO;AAAA,EACd;AACF;AAQA,SAAS,uBAAgD;AACvD,SAAO,OAAO,sBAAsB,EAAE,KAAK,CAAC,EAAE,OAAO,MAAM,IAAI,OAAO,CAAC;AACzE;AAGA,SAAS,SAAS,KAAa,UAAkB,UAA2B;AAC1E,QAAM,OAAO,IAAI,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC,GAAG,YAAY,KAAK;AAC1D,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAU,aAAO,UAAU,QAAQ;AAAA,IACxC,KAAK;AAAU,aAAO,YAAY,YAAY,QAAQ;AAAA,IACtD,KAAK;AAAU,aAAO,UAAU,YAAY,QAAQ;AAAA,IACpD,KAAK;AAAU,aAAO,UAAU,YAAY,QAAQ;AAAA,IACpD,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAY,aAAO;AAAA,IACxB;AAAe,aAAO;AAAA,EACxB;AACF;AAIA,IAAM,eAAN,MAAmB;AAAA,EAUjB,YAAoB,SAAkC;AAAlC;AAAA,EAAmC;AAAA,EAT/C,SAA8B;AAAA,EAC9B,WAA4B;AAAA,EAC5B,OAAe,OAAO,MAAM,CAAC;AAAA;AAAA,EAG7B,cAAc,oBAAI,IAAoB;AAAA;AAAA,EAEtC,WAAW,oBAAI,IAA+C;AAAA,EAItE,MAAM,YAAY,MAA+B;AAC/C,SAAK,OAAO,KAAK,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,KAAK,MAAM,IAAI,CAAC,IAAI;AAGtE,QAAI,KAAK,WAAW,WAAW;AAE7B,UAAI,KAAK,KAAK,SAAS,EAAG,QAAO,OAAO,MAAM,CAAC;AAC/C,YAAM,SAAS,KAAK,KAAK,YAAY,CAAC;AACtC,UAAI,KAAK,KAAK,SAAS,OAAQ,QAAO,OAAO,MAAM,CAAC;AAEpD,YAAM,MAAM,KAAK,KAAK,SAAS,GAAG,MAAM;AACxC,WAAK,OAAO,KAAK,KAAK,SAAS,MAAM;AAErC,YAAM,SAAe,gBAAgB,GAAG;AACxC,UAAI,WAAW,OAAQ,QAAO,OAAO,KAAK,GAAG;AAC7C,WAAK,SAAS;AACd,aAAa,gBAAgB;AAAA,IAC/B;AAGA,UAAM,YAAsB,CAAC;AAE7B,WAAO,KAAK,KAAK,UAAU,GAAG;AAC5B,YAAM,UAAU,OAAO,aAAa,KAAK,KAAK,CAAC,CAAC;AAChD,YAAM,SAAU,KAAK,KAAK,YAAY,CAAC;AACvC,YAAM,WAAW,IAAI;AACrB,UAAI,KAAK,KAAK,SAAS,SAAU;AAEjC,YAAM,UAAU,KAAK,KAAK,SAAS,GAAG,QAAQ;AAC9C,WAAK,OAAO,KAAK,KAAK,SAAS,QAAQ;AAGvC,UAAI,YAAY,KAAK;AACnB,cAAM,MAAM,QAAQ,QAAQ,CAAC;AAC7B,cAAM,MAAM,QAAQ,SAAS,QAAQ,GAAG,OAAO,IAAI,MAAM,QAAQ,MAAM;AACvE,kBAAU,KAAK,MAAM,KAAK,WAAW,GAAG,CAAC;AACzC;AAAA,MACF;AAEA,cAAQ,SAAS;AAAA,QACf,KAAK,KAAK;AACR,gBAAM,UAAU,QAAQ,QAAQ,CAAC;AACjC,gBAAM,WAAW,UAAU,IAAI,QAAQ,SAAS,QAAQ,GAAG,OAAO,IAAI;AACtE,gBAAM,aAAa,UAAU;AAC7B,gBAAM,WAAW,QAAQ,QAAQ,GAAG,UAAU;AAC9C,gBAAM,MAAM,QAAQ,SAAS,QAAQ,YAAY,YAAY,IAAI,WAAW,QAAQ,MAAM;AAC1F,eAAK,YAAY,IAAI,UAAU,GAAG;AAClC,oBAAU,KAAW,cAAc,CAAC;AACpC;AAAA,QACF;AAAA,QACA,KAAK,KAAK;AACR,gBAAM,EAAE,QAAQ,WAAW,OAAO,IAAI,KAAK,WAAW,OAAO;AAC7D,gBAAM,WAAW,KAAK,YAAY,IAAI,SAAS,KAAK;AACpD,eAAK,SAAS,IAAI,QAAQ,EAAE,KAAK,UAAU,OAAO,CAAC;AACnD,oBAAU,KAAW,aAAa,CAAC;AACnC;AAAA,QACF;AAAA,QACA,KAAK,KAAK;AACR,oBAAU,KAAW,OAAO,CAAC;AAC7B;AAAA,QACF;AAAA,QACA,KAAK,KAAK;AACR,gBAAM,YAAY,QAAQ,QAAQ,CAAC;AACnC,gBAAM,aAAa,YAAY,IAAI,QAAQ,SAAS,QAAQ,GAAG,SAAS,IAAI;AAC5E,gBAAM,QAAQ,KAAK,SAAS,IAAI,UAAU;AAC1C,cAAI,SAAS,MAAM,KAAK;AACtB,kBAAM,IAAI,MAAM,KAAK,qBAAqB,MAAM,KAAK,MAAM,MAAM;AACjE,sBAAU,KAAK,CAAC;AAAA,UAClB;AACA;AAAA,QACF;AAAA,QACA,KAAK,KAAK;AACR,oBAAU,KAAW,cAAc,KAAK,QAAQ,CAAC;AACjD;AAAA,QACF;AAAA,QACA,KAAK,KAAK;AACR;AAAA,QACF;AAAA,QACA;AAEE;AAAA,MACJ;AAAA,IACF;AAEA,WAAO,UAAU,SAAS,IAAI,OAAO,OAAO,SAAS,IAAI,OAAO,MAAM,CAAC;AAAA,EACzE;AAAA;AAAA,EAGQ,WAAW,SAA0E;AAC3F,QAAI,MAAM;AAEV,UAAM,YAAY,QAAQ,QAAQ,GAAG,GAAG;AACxC,UAAM,SAAS,YAAY,MAAM,QAAQ,SAAS,QAAQ,KAAK,SAAS,IAAI;AAC5E,UAAM,YAAY;AAElB,UAAM,UAAU,QAAQ,QAAQ,GAAG,GAAG;AACtC,UAAM,YAAY,UAAU,MAAM,QAAQ,SAAS,QAAQ,KAAK,OAAO,IAAI;AAC3E,UAAM,UAAU;AAEhB,UAAM,aAAa,QAAQ,YAAY,GAAG;AAAG,WAAO;AACpD,WAAO,aAAa;AAEpB,UAAM,YAAY,QAAQ,YAAY,GAAG;AAAG,WAAO;AACnD,UAAM,SAAmB,CAAC;AAC1B,aAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,YAAM,MAAM,QAAQ,YAAY,GAAG;AAAG,aAAO;AAC7C,UAAI,QAAQ,IAAI;AACd,eAAO,KAAK,MAAM;AAAA,MACpB,OAAO;AACL,eAAO,KAAK,QAAQ,SAAS,QAAQ,KAAK,MAAM,GAAG,CAAC;AACpD,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO,EAAE,QAAQ,WAAW,OAAO;AAAA,EACrC;AAAA;AAAA,EAGA,MAAc,qBAAqB,KAAa,QAAmC;AACjF,UAAM,UAAU,IAAI,KAAK;AACzB,UAAM,QAAQ,QAAQ,YAAY;AAElC,QAAI,UAAU,SAAY;AAAE,WAAK,WAAW;AAAK,aAAa,gBAAgB,OAAO;AAAA,IAAG;AACxF,QAAI,UAAU,UAAY;AAAE,WAAK,WAAW;AAAK,aAAa,gBAAgB,QAAQ;AAAA,IAAG;AACzF,QAAI,UAAU,YAAY;AAAE,WAAK,WAAW;AAAK,aAAa,gBAAgB,UAAU;AAAA,IAAG;AAE3F,UAAM,KAAK,MAAM,KAAK;AACtB,QAAI;AAEF,YAAM,SAAS,MAAM,GAAG,MAAM,SAAS,MAAM;AAC7C,YAAM,SAAkC,OAAO,UAAU,CAAC;AAC1D,YAAM,OAAyC,OAAO,QAAQ,CAAC;AAE/D,YAAM,OAAiB,CAAC;AACxB,UAAI,OAAO,SAAS,GAAG;AACrB,aAAK,KAAW,eAAe,OAAO,IAAI,CAAC,MAAwB,EAAE,IAAI,CAAC,CAAC;AAC3E,mBAAW,OAAO,MAAM;AACtB,eAAK,KAAW,QAAQ,OAAO,IAAI,CAAC,MAAwB;AAC1D,kBAAM,IAAI,IAAI,EAAE,IAAI;AACpB,mBAAO,MAAM,QAAQ,MAAM,SAAY,OAAO,OAAO,CAAC;AAAA,UACxD,CAAC,CAAC,CAAC;AAAA,QACL;AAAA,MACF;AAEA,YAAM,MAAM,SAAS,SAAS,KAAK,QAAQ,OAAO,YAAkC;AACpF,WAAK,KAAW,gBAAgB,GAAG,CAAC;AACpC,aAAO,OAAO,OAAO,IAAI;AAAA,IAC3B,SAAS,KAAK;AACZ,aAAa,cAAc,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,IAC7E;AAAA,EACF;AAAA,EAEA,MAAc,WAAW,KAA8B;AACrD,UAAM,UAAU,IAAI,KAAK;AACzB,UAAM,QAAU,QAAQ,YAAY;AAEpC,QAAI,UAAU,SAAY;AAAE,WAAK,WAAW;AAAK,aAAO,OAAO,OAAO,CAAO,gBAAgB,OAAO,GAAY,cAAc,GAAG,CAAC,CAAC;AAAA,IAAG;AACtI,QAAI,UAAU,UAAY;AAAE,WAAK,WAAW;AAAK,aAAO,OAAO,OAAO,CAAO,gBAAgB,QAAQ,GAAW,cAAc,GAAG,CAAC,CAAC;AAAA,IAAG;AACtI,QAAI,UAAU,YAAY;AAAE,WAAK,WAAW;AAAK,aAAO,OAAO,OAAO,CAAO,gBAAgB,UAAU,GAAS,cAAc,GAAG,CAAC,CAAC;AAAA,IAAG;AAEtI,UAAM,KAAK,MAAM,KAAK;AACtB,QAAI;AACF,YAAM,SAAS,MAAM,GAAG,MAAM,OAAO;AACrC,YAAM,SAAkC,OAAO,UAAU,CAAC;AAC1D,YAAM,OAAyC,OAAO,QAAS,CAAC;AAEhE,YAAM,OAAiB,CAAC;AAExB,UAAI,OAAO,SAAS,GAAG;AACrB,aAAK,KAAW,eAAe,OAAO,IAAI,CAAC,MAAwB,EAAE,IAAI,CAAC,CAAC;AAC3E,mBAAW,OAAO,MAAM;AACtB,eAAK,KAAW,QAAQ,OAAO,IAAI,CAAC,MAAwB;AAC1D,kBAAM,IAAI,IAAI,EAAE,IAAI;AACpB,mBAAO,MAAM,QAAQ,MAAM,SAAY,OAAO,OAAO,CAAC;AAAA,UACxD,CAAC,CAAC,CAAC;AAAA,QACL;AAAA,MACF;AAEA,YAAM,MAAM,SAAS,SAAS,KAAK,QAAQ,OAAO,YAAkC;AACpF,UAAI,QAAQ,QAAS,MAAK,WAAW;AAAA,eAC5B,QAAQ,YAAY,QAAQ,WAAY,MAAK,WAAW;AAEjE,WAAK,KAAW,gBAAgB,GAAG,CAAC;AACpC,WAAK,KAAW,cAAc,KAAK,QAAQ,CAAC;AAC5C,aAAO,OAAO,OAAO,IAAI;AAAA,IAC3B,SAAS,KAAK;AACZ,aAAO,OAAO,OAAO;AAAA,QACb,cAAc,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAC9D,cAAc,KAAK,aAAa,MAAM,MAAM,GAAG;AAAA,MACvD,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAIO,IAAM,SAAN,MAAa;AAAA;AAAA,EAEV;AAAA;AAAA,EAEA,eAA8B,QAAQ,QAAQ;AAAA,EAC9C,eAAe,oBAAI,IAA0B;AAAA,EAErD,cAAc;AACZ,SAAK,UAAU,qBAAqB;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAuB;AAC3B,UAAM,KAAK;AACX,UAAM,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAS,OAAe,MAAkD;AAExE,SAAK,eAAe,KAAK,aAAa,KAAK,MAAM,KAAK,YAAY,OAAO,IAAI,CAAC;AAAA,EAChF;AAAA,EAEA,MAAc,YAAY,OAAe,MAA2D;AAClG,QAAI,KAAK,WAAW,EAAG;AACvB,UAAM,KAAK,MAAM,KAAK;AACtB,UAAM,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC;AAChC,UAAM,UAAU,KAAK,IAAI,OAAK,IAAI,CAAC,QAAQ,EAAE,KAAK,IAAI;AACtD,UAAM,GAAG,KAAK,+BAA+B,KAAK,MAAM,OAAO,GAAG;AAClE,eAAW,OAAO,MAAM;AACtB,YAAM,OAAO,KAAK,IAAI,OAAK,IAAI,CAAC,MAAM,OAAO,SAAS,IAAI,OAAO,IAAI,CAAC,CAAC,EAAE,QAAQ,MAAM,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAC1G,YAAM,GAAG,KAAK,gBAAgB,KAAK,MAAM,KAAK,IAAI,OAAK,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,CAAC,aAAa,IAAI,GAAG;AAAA,IACjG;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAmC,KAAsE;AAC7G,UAAM,KAAK,MAAM,KAAK;AACtB,WAAO,GAAG,MAAM,GAAG;AAAA,EACrB;AAAA,EAEA,gBAAgC;AAC9B,WAAO,OAAO,MAAc,QAAmD;AAC7E,UAAI,CAAC,KAAK,aAAa,IAAI,IAAI,QAAQ,GAAG;AACxC,aAAK,aAAa,IAAI,IAAI,UAAU,IAAI,aAAa,KAAK,OAAO,CAAC;AAAA,MACpE;AACA,aAAO,KAAK,aAAa,IAAI,IAAI,QAAQ,EAAG,YAAY,IAAI;AAAA,IAC9D;AAAA,EACF;AACF;","names":[]}
|