orez 0.2.9 → 0.2.11

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/src/cli.ts CHANGED
@@ -1107,6 +1107,7 @@ const main = defineCommand({
1107
1107
  const {
1108
1108
  config,
1109
1109
  stop,
1110
+ instances,
1110
1111
  zeroEnv,
1111
1112
  logStore,
1112
1113
  httpLog,
@@ -1162,6 +1163,7 @@ const main = defineCommand({
1162
1163
  zeroEnv,
1163
1164
  actions: { restartZero, stopZero, resetZero, resetZeroFull },
1164
1165
  startTime: Date.now(),
1166
+ db: instances,
1165
1167
  })
1166
1168
  log.orez(`admin: ${url(`http://localhost:${config.adminPort}`)}`)
1167
1169
  }
@@ -29,6 +29,41 @@ import type { PGlite } from '@electric-sql/pglite'
29
29
  const textEncoder = new TextEncoder()
30
30
  const textDecoder = new TextDecoder()
31
31
 
32
+ // verbose wire-protocol logging. off by default — every postgres message hits
33
+ // the hot path, so unconditional console.debug calls are surprisingly expensive
34
+ // even when devtools is set to hide them (the interpolated strings still get
35
+ // built, and devtools retains them).
36
+ //
37
+ // re-evaluated on every call so you can flip it at runtime from any iframe,
38
+ // worker, or console without restarting. toggle via either:
39
+ // - env: OREZ_DEBUG_WIRE=1 (picked up at process start)
40
+ // - global: globalThis.__OREZ_DEBUG_WIRE__ = true (works anywhere, anytime)
41
+ function isDebugWire(): boolean {
42
+ try {
43
+ if ((globalThis as any).__OREZ_DEBUG_WIRE__ === true) return true
44
+ if (typeof process !== 'undefined' && process.env?.OREZ_DEBUG_WIRE) return true
45
+ } catch {}
46
+ return false
47
+ }
48
+
49
+ // postgres frontend/backend message type bytes — only used for debug-wire logging
50
+ const PG_MSG_TYPE_NAMES: Record<number, string> = {
51
+ 0x50: 'Parse',
52
+ 0x42: 'Bind',
53
+ 0x44: 'Describe',
54
+ 0x45: 'Execute',
55
+ 0x43: 'Close',
56
+ 0x48: 'Flush',
57
+ 0x53: 'Sync',
58
+ 0x51: 'Query',
59
+ 0x58: 'Terminate',
60
+ 0x70: 'Password',
61
+ 0x46: 'FunctionCall',
62
+ 0x64: 'CopyData',
63
+ 0x63: 'CopyDone',
64
+ 0x66: 'CopyFail',
65
+ }
66
+
32
67
  // schema query cache: identical information_schema/catalog queries from multiple
33
68
  // zero-cache clients are deduplicated. first query executes, all others get cached result.
34
69
  interface CachedQueryResult {
@@ -619,7 +654,7 @@ function messagePortToDuplex(port: MessagePort): {
619
654
  start(controller) {
620
655
  port.onmessage = (ev: MessageEvent) => {
621
656
  msgCount++
622
- if (msgCount <= 3) {
657
+ if (isDebugWire() && msgCount <= 3) {
623
658
  console.debug(
624
659
  `[pg-proxy-duplex] msg#${msgCount} type=${typeof ev.data} isAB=${ev.data instanceof ArrayBuffer} isU8=${ev.data instanceof Uint8Array} len=${ev.data?.byteLength ?? ev.data?.length ?? '?'}`
625
660
  )
@@ -641,7 +676,7 @@ function messagePortToDuplex(port: MessagePort): {
641
676
  const writable = new WritableStream<Uint8Array>({
642
677
  write(chunk) {
643
678
  _globalWriteCount++
644
- if (_globalWriteCount <= 200) {
679
+ if (isDebugWire() && _globalWriteCount <= 200) {
645
680
  console.debug(`[pg-proxy-ws-write] #${_globalWriteCount} len=${chunk.byteLength}`)
646
681
  }
647
682
  // transfer the ArrayBuffer for zero-copy
@@ -779,7 +814,8 @@ export async function createBrowserProxy(
779
814
  const params = parseStartupParams(data)
780
815
  const dbName = params.database || 'postgres'
781
816
  const isRepl = params.replication === 'database'
782
- console.debug(`[pg-proxy] connection: db=${dbName} repl=${isRepl}`)
817
+ if (isDebugWire())
818
+ console.debug(`[pg-proxy] connection: db=${dbName} repl=${isRepl}`)
783
819
  // all connections handled with raw MessagePort (no pg-gateway).
784
820
  // pg-gateway uses for-await on ReadableStream which is broken
785
821
  // in browser Web Workers (same root cause as patches #9, #18, #20).
@@ -847,9 +883,11 @@ export async function createBrowserProxy(
847
883
  port.onmessage = (ev: MessageEvent) => {
848
884
  const data2 =
849
885
  ev.data instanceof ArrayBuffer ? new Uint8Array(ev.data) : (ev.data as Uint8Array)
850
- console.debug(
851
- `[pg-proxy-raw-auth] ${dbName} repl=${isReplicationConnection} got msg type=0x${data2?.[0]?.toString(16)} len=${data2?.length}`
852
- )
886
+ if (isDebugWire()) {
887
+ console.debug(
888
+ `[pg-proxy-raw-auth] ${dbName} repl=${isReplicationConnection} got msg type=0x${data2?.[0]?.toString(16)} len=${data2?.length}`
889
+ )
890
+ }
853
891
  if (!data2 || data2[0] !== 0x70) {
854
892
  console.warn(
855
893
  '[pg-proxy-raw-auth] expected password, got type=0x' + data2?.[0]?.toString(16)
@@ -895,7 +933,8 @@ export async function createBrowserProxy(
895
933
  }
896
934
  write(combined)
897
935
 
898
- console.debug('[pg-proxy-repl-raw] auth complete, ready for queries')
936
+ if (isDebugWire())
937
+ console.debug('[pg-proxy-repl-raw] auth complete, ready for queries')
899
938
 
900
939
  // step 3: handle subsequent messages (queries, replication commands)
901
940
  installQueryHandler()
@@ -962,25 +1001,10 @@ export async function createBrowserProxy(
962
1001
  async function processMessage(data: Uint8Array) {
963
1002
  _pmCount++
964
1003
  const msgType = data[0]
965
- // log every message with type name for debugging
966
- const typeNames: Record<number, string> = {
967
- 0x50: 'Parse',
968
- 0x42: 'Bind',
969
- 0x44: 'Describe',
970
- 0x45: 'Execute',
971
- 0x43: 'Close',
972
- 0x48: 'Flush',
973
- 0x53: 'Sync',
974
- 0x51: 'Query',
975
- 0x58: 'Terminate',
976
- 0x70: 'Password',
977
- 0x46: 'FunctionCall',
978
- 0x64: 'CopyData',
979
- 0x63: 'CopyDone',
980
- 0x66: 'CopyFail',
1004
+ if (isDebugWire()) {
1005
+ const name = PG_MSG_TYPE_NAMES[msgType] || `unknown(0x${msgType.toString(16)})`
1006
+ console.debug(`[pg-proxy-pm] #${_pmCount} ${dbName} ${name} len=${data.length}`)
981
1007
  }
982
- const name = typeNames[msgType] || `unknown(0x${msgType.toString(16)})`
983
- console.debug(`[pg-proxy-pm] #${_pmCount} ${dbName} ${name} len=${data.length}`)
984
1008
 
985
1009
  // replication connection: handle replication commands
986
1010
  if (isReplicationConnection && msgType === 0x51) {
@@ -1049,11 +1073,13 @@ export async function createBrowserProxy(
1049
1073
  }
1050
1074
 
1051
1075
  // regular query handling (SimpleQuery or extended protocol)
1052
- if (msgType === 0x50) {
1053
- const q = extractParseQuery(data)
1054
- if (q) console.debug(`[pg-proxy-raw] ${dbName}: Parse ${q.slice(0, 80)}`)
1055
- } else if (msgType === 0x51) {
1056
- console.debug(`[pg-proxy-raw] ${dbName}: SimpleQuery len=${data.length}`)
1076
+ if (isDebugWire()) {
1077
+ if (msgType === 0x50) {
1078
+ const q = extractParseQuery(data)
1079
+ if (q) console.debug(`[pg-proxy-raw] ${dbName}: Parse ${q.slice(0, 80)}`)
1080
+ } else if (msgType === 0x51) {
1081
+ console.debug(`[pg-proxy-raw] ${dbName}: SimpleQuery len=${data.length}`)
1082
+ }
1057
1083
  }
1058
1084
 
1059
1085
  // extended protocol pipeline: Parse(0x50), Bind(0x42), Describe(0x44),
@@ -1137,7 +1163,7 @@ export async function createBrowserProxy(
1137
1163
  return
1138
1164
  }
1139
1165
  const dt = performance.now() - t0
1140
- if (dt > 100)
1166
+ if (isDebugWire() && dt > 100)
1141
1167
  console.debug(`[pg-proxy-raw] slow query on ${dbName}: ${dt.toFixed(0)}ms`)
1142
1168
 
1143
1169
  // update transaction state
@@ -1308,7 +1334,7 @@ export async function createBrowserProxy(
1308
1334
  // but tools like pg_restore also need encoding, datestyle, etc.
1309
1335
  // write directly to the port since pg-gateway owns the writable stream
1310
1336
  onAuthenticated() {
1311
- console.debug(`[pg-proxy-conn] authenticated db=${dbName}`)
1337
+ if (isDebugWire()) console.debug(`[pg-proxy-conn] authenticated db=${dbName}`)
1312
1338
  for (const [name, value] of SERVER_PARAMS) {
1313
1339
  rawWrite(buildParameterStatus(name, value))
1314
1340
  }
@@ -1320,23 +1346,29 @@ export async function createBrowserProxy(
1320
1346
  isReplicationConnection = true
1321
1347
  }
1322
1348
  dbName = params?.database || 'postgres'
1323
- console.debug(
1324
- `[pg-proxy-conn] startup: db=${dbName} user=${params?.user} repl=${params?.replication || 'none'}`
1325
- )
1349
+ if (isDebugWire()) {
1350
+ console.debug(
1351
+ `[pg-proxy-conn] startup: db=${dbName} user=${params?.user} repl=${params?.replication || 'none'}`
1352
+ )
1353
+ }
1326
1354
  const { db } = getDbContext(dbName)
1327
1355
  await db.waitReady
1328
1356
  },
1329
1357
 
1330
1358
  async onMessage(data, state) {
1331
1359
  if (!state.isAuthenticated) {
1360
+ if (isDebugWire()) {
1361
+ console.debug(
1362
+ `[pg-proxy-conn] msg before auth, type=0x${data[0].toString(16)}`
1363
+ )
1364
+ }
1365
+ return
1366
+ }
1367
+ if (isDebugWire()) {
1332
1368
  console.debug(
1333
- `[pg-proxy-conn] msg before auth, type=0x${data[0].toString(16)}`
1369
+ `[pg-proxy-conn] msg db=${dbName} type=0x${data[0].toString(16)} len=${data.length}`
1334
1370
  )
1335
- return
1336
1371
  }
1337
- console.debug(
1338
- `[pg-proxy-conn] msg db=${dbName} type=0x${data[0].toString(16)} len=${data.length}`
1339
- )
1340
1372
 
1341
1373
  // handle replication connections (always go to postgres instance)
1342
1374
  if (isReplicationConnection) {
@@ -1605,17 +1637,25 @@ async function handleReplicationMessageBrowser(
1605
1637
  mutex: Mutex,
1606
1638
  connection: PostgresConnection
1607
1639
  ): Promise<Uint8Array | undefined> {
1608
- console.debug(`[pg-proxy-repl] ENTRY type=0x${data[0].toString(16)} len=${data.length}`)
1640
+ if (isDebugWire()) {
1641
+ console.debug(
1642
+ `[pg-proxy-repl] ENTRY type=0x${data[0].toString(16)} len=${data.length}`
1643
+ )
1644
+ }
1609
1645
 
1610
1646
  // for non-SimpleQuery messages (extended protocol), execute against PGlite directly.
1611
1647
  if (data[0] !== 0x51) {
1612
- console.debug(
1613
- `[pg-proxy-repl] ext protocol msg type=0x${data[0].toString(16)} len=${data.length}`
1614
- )
1648
+ if (isDebugWire()) {
1649
+ console.debug(
1650
+ `[pg-proxy-repl] ext protocol msg type=0x${data[0].toString(16)} len=${data.length}`
1651
+ )
1652
+ }
1615
1653
  await mutex.acquire()
1616
1654
  try {
1617
1655
  const result = await db.execProtocolRaw(data, { syncToFs: false })
1618
- console.debug(`[pg-proxy-repl] ext protocol result len=${result.length}`)
1656
+ if (isDebugWire()) {
1657
+ console.debug(`[pg-proxy-repl] ext protocol result len=${result.length}`)
1658
+ }
1619
1659
  return result
1620
1660
  } finally {
1621
1661
  mutex.release()
@@ -1667,17 +1707,23 @@ async function handleReplicationMessageBrowser(
1667
1707
  }
1668
1708
 
1669
1709
  // handle replication queries + fallthrough to pglite, all under mutex
1670
- console.debug(`[pg-proxy-repl] query: ${query.slice(0, 100)}`)
1671
- console.debug(`[pg-proxy-repl] acquiring mutex...`)
1710
+ if (isDebugWire()) {
1711
+ console.debug(`[pg-proxy-repl] query: ${query.slice(0, 100)}`)
1712
+ console.debug(`[pg-proxy-repl] acquiring mutex...`)
1713
+ }
1672
1714
  await mutex.acquire()
1673
- console.debug(`[pg-proxy-repl] mutex acquired, testing db access...`)
1715
+ if (isDebugWire()) console.debug(`[pg-proxy-repl] mutex acquired, testing db access...`)
1674
1716
  try {
1675
1717
  const testResult = await db.query('SELECT 1 as test')
1676
- console.debug(`[pg-proxy-repl] db.query works: ${JSON.stringify(testResult.rows)}`)
1718
+ if (isDebugWire()) {
1719
+ console.debug(`[pg-proxy-repl] db.query works: ${JSON.stringify(testResult.rows)}`)
1720
+ }
1677
1721
  const response = await handleReplicationQuery(query, db)
1678
- console.debug(
1679
- `[pg-proxy-repl] handleReplicationQuery result: ${response ? 'bytes(' + response.length + ')' : 'null'}`
1680
- )
1722
+ if (isDebugWire()) {
1723
+ console.debug(
1724
+ `[pg-proxy-repl] handleReplicationQuery result: ${response ? 'bytes(' + response.length + ')' : 'null'}`
1725
+ )
1726
+ }
1681
1727
  if (response) return response
1682
1728
 
1683
1729
  // apply query rewrites before forwarding
@@ -42,6 +42,7 @@ export function orezPlugin(options?: OrezPluginOptions): Plugin {
42
42
  resetZeroFull: result.resetZeroFull,
43
43
  },
44
44
  startTime: Date.now(),
45
+ db: result.instances,
45
46
  })
46
47
  }
47
48
 
@@ -100,8 +100,32 @@ export function messagePortToWs(port: MessagePort): WsCompatible {
100
100
  }
101
101
 
102
102
  // forward port messages → ws 'message' events
103
+ // filter out control messages from sync-ws-patch.js (__close, __open)
103
104
  port.onmessage = (event: MessageEvent) => {
104
- emit('message', { data: event.data })
105
+ const data = event.data
106
+ // control messages from sync-ws-patch.js — handle as close/open events
107
+ if (
108
+ data &&
109
+ typeof data === 'object' &&
110
+ !ArrayBuffer.isView(data) &&
111
+ !(data instanceof ArrayBuffer)
112
+ ) {
113
+ if (data.__close) {
114
+ closed = true
115
+ port.close()
116
+ emit('close', {
117
+ code: data.code ?? 1000,
118
+ reason: data.reason ?? '',
119
+ wasClean: true,
120
+ })
121
+ return
122
+ }
123
+ if (data.__open) {
124
+ // already open, ignore
125
+ return
126
+ }
127
+ }
128
+ emit('message', { data })
105
129
  }
106
130
 
107
131
  port.onmessageerror = (event: MessageEvent) => {