orez 0.1.43 → 0.1.44

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.
Files changed (108) hide show
  1. package/dist/admin/http-proxy.d.ts.map +1 -1
  2. package/dist/admin/http-proxy.js +3 -1
  3. package/dist/admin/http-proxy.js.map +1 -1
  4. package/dist/admin/log-store.d.ts.map +1 -1
  5. package/dist/admin/log-store.js +5 -1
  6. package/dist/admin/log-store.js.map +1 -1
  7. package/dist/admin/server.d.ts.map +1 -1
  8. package/dist/admin/server.js +25 -25
  9. package/dist/admin/server.js.map +1 -1
  10. package/dist/browser.d.ts +54 -0
  11. package/dist/browser.d.ts.map +1 -0
  12. package/dist/browser.js +110 -0
  13. package/dist/browser.js.map +1 -0
  14. package/dist/cli.js +1 -1
  15. package/dist/cli.js.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +5 -2
  19. package/dist/index.js.map +1 -1
  20. package/dist/pg-proxy-browser.d.ts +26 -0
  21. package/dist/pg-proxy-browser.d.ts.map +1 -0
  22. package/dist/pg-proxy-browser.js +1460 -0
  23. package/dist/pg-proxy-browser.js.map +1 -0
  24. package/dist/pg-proxy.d.ts.map +1 -1
  25. package/dist/pg-proxy.js +48 -34
  26. package/dist/pg-proxy.js.map +1 -1
  27. package/dist/pglite-ipc.d.ts.map +1 -1
  28. package/dist/pglite-ipc.js +3 -2
  29. package/dist/pglite-ipc.js.map +1 -1
  30. package/dist/pglite-manager.d.ts.map +1 -1
  31. package/dist/pglite-manager.js +33 -85
  32. package/dist/pglite-manager.js.map +1 -1
  33. package/dist/pglite-web-proxy.d.ts +38 -0
  34. package/dist/pglite-web-proxy.d.ts.map +1 -0
  35. package/dist/pglite-web-proxy.js +155 -0
  36. package/dist/pglite-web-proxy.js.map +1 -0
  37. package/dist/pglite-web-worker.d.ts +24 -0
  38. package/dist/pglite-web-worker.d.ts.map +1 -0
  39. package/dist/pglite-web-worker.js +119 -0
  40. package/dist/pglite-web-worker.js.map +1 -0
  41. package/dist/recovery.js +2 -2
  42. package/dist/recovery.js.map +1 -1
  43. package/dist/replication/change-tracker.js +9 -9
  44. package/dist/replication/change-tracker.js.map +1 -1
  45. package/dist/replication/handler.d.ts.map +1 -1
  46. package/dist/replication/handler.js +34 -26
  47. package/dist/replication/handler.js.map +1 -1
  48. package/dist/worker/browser-build-config.d.ts.map +1 -1
  49. package/dist/worker/browser-build-config.js +5 -2
  50. package/dist/worker/browser-build-config.js.map +1 -1
  51. package/dist/worker/browser-embed.d.ts.map +1 -1
  52. package/dist/worker/browser-embed.js +31 -26
  53. package/dist/worker/browser-embed.js.map +1 -1
  54. package/dist/worker/shims/fastify.d.ts +1 -0
  55. package/dist/worker/shims/fastify.d.ts.map +1 -1
  56. package/dist/worker/shims/fastify.js +31 -20
  57. package/dist/worker/shims/fastify.js.map +1 -1
  58. package/dist/worker/shims/postgres-browser.d.ts +12 -0
  59. package/dist/worker/shims/postgres-browser.d.ts.map +1 -0
  60. package/dist/worker/shims/postgres-browser.js +52 -0
  61. package/dist/worker/shims/postgres-browser.js.map +1 -0
  62. package/dist/worker/shims/postgres-socket.d.ts +83 -0
  63. package/dist/worker/shims/postgres-socket.d.ts.map +1 -0
  64. package/dist/worker/shims/postgres-socket.js +278 -0
  65. package/dist/worker/shims/postgres-socket.js.map +1 -0
  66. package/dist/worker/shims/postgres.d.ts.map +1 -1
  67. package/dist/worker/shims/postgres.js +18 -9
  68. package/dist/worker/shims/postgres.js.map +1 -1
  69. package/dist/worker/shims/stream-browser.d.ts +5 -4
  70. package/dist/worker/shims/stream-browser.d.ts.map +1 -1
  71. package/dist/worker/shims/stream-browser.js +7 -6
  72. package/dist/worker/shims/stream-browser.js.map +1 -1
  73. package/dist/worker/shims/ws-browser.d.ts.map +1 -1
  74. package/dist/worker/shims/ws-browser.js +43 -21
  75. package/dist/worker/shims/ws-browser.js.map +1 -1
  76. package/dist/worker/shims/ws.d.ts.map +1 -1
  77. package/dist/worker/shims/ws.js +81 -17
  78. package/dist/worker/shims/ws.js.map +1 -1
  79. package/package.json +11 -58
  80. package/src/admin/http-proxy.ts +4 -1
  81. package/src/admin/log-store.ts +5 -1
  82. package/src/admin/server.ts +26 -25
  83. package/src/browser.ts +195 -0
  84. package/src/cli.ts +1 -1
  85. package/src/index.ts +5 -2
  86. package/src/integration/integration.test.ts +1 -1
  87. package/src/integration/restore-live-stress.test.ts +2 -2
  88. package/src/pg-proxy-browser.ts +1673 -0
  89. package/src/pg-proxy.ts +48 -40
  90. package/src/pglite-ipc.ts +3 -2
  91. package/src/pglite-manager.ts +45 -107
  92. package/src/pglite-web-proxy.ts +180 -0
  93. package/src/pglite-web-worker.ts +132 -0
  94. package/src/recovery.ts +2 -2
  95. package/src/replication/change-tracker.test.ts +1 -1
  96. package/src/replication/change-tracker.ts +9 -9
  97. package/src/replication/handler.ts +37 -26
  98. package/src/worker/browser-build-config.test.ts +1 -1
  99. package/src/worker/browser-build-config.ts +5 -2
  100. package/src/worker/browser-embed.ts +33 -30
  101. package/src/worker/shims/fastify.ts +37 -24
  102. package/src/worker/shims/postgres-browser.ts +59 -0
  103. package/src/worker/shims/postgres-socket.test.ts +576 -0
  104. package/src/worker/shims/postgres-socket.ts +310 -0
  105. package/src/worker/shims/postgres.ts +30 -15
  106. package/src/worker/shims/stream-browser.ts +15 -0
  107. package/src/worker/shims/ws-browser.ts +38 -20
  108. package/src/worker/shims/ws.ts +76 -21
@@ -17,10 +17,10 @@ export async function installChangeTracking(db: PGlite): Promise<void> {
17
17
  // create changes table and watermark sequence
18
18
  // watermark is the primary key - monotonically increasing, no separate id needed
19
19
  await db.exec(`
20
- CREATE SEQUENCE IF NOT EXISTS _orez.watermark;
20
+ CREATE SEQUENCE IF NOT EXISTS _orez._zero_watermark;
21
21
 
22
- CREATE TABLE IF NOT EXISTS _orez.changes (
23
- watermark BIGINT NOT NULL DEFAULT nextval('_orez.watermark') PRIMARY KEY,
22
+ CREATE TABLE IF NOT EXISTS _orez._zero_changes (
23
+ watermark BIGINT NOT NULL DEFAULT nextval('_orez._zero_watermark') PRIMARY KEY,
24
24
  table_name TEXT NOT NULL,
25
25
  op TEXT NOT NULL CHECK (op IN ('INSERT', 'UPDATE', 'DELETE')),
26
26
  row_data JSONB,
@@ -47,7 +47,7 @@ export async function installChangeTracking(db: PGlite): Promise<void> {
47
47
  CREATE OR REPLACE FUNCTION public._zero_track_change() RETURNS TRIGGER AS $$
48
48
  BEGIN
49
49
  IF TG_OP = 'DELETE' THEN
50
- INSERT INTO _orez.changes (table_name, op, old_data)
50
+ INSERT INTO _orez._zero_changes (table_name, op, old_data)
51
51
  VALUES (TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME, 'DELETE', to_jsonb(OLD));
52
52
  RETURN OLD;
53
53
  ELSIF TG_OP = 'UPDATE' THEN
@@ -55,11 +55,11 @@ export async function installChangeTracking(db: PGlite): Promise<void> {
55
55
  IF to_jsonb(NEW) = to_jsonb(OLD) THEN
56
56
  RETURN NEW;
57
57
  END IF;
58
- INSERT INTO _orez.changes (table_name, op, row_data, old_data)
58
+ INSERT INTO _orez._zero_changes (table_name, op, row_data, old_data)
59
59
  VALUES (TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME, 'UPDATE', to_jsonb(NEW), to_jsonb(OLD));
60
60
  RETURN NEW;
61
61
  ELSE
62
- INSERT INTO _orez.changes (table_name, op, row_data)
62
+ INSERT INTO _orez._zero_changes (table_name, op, row_data)
63
63
  VALUES (TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME, 'INSERT', to_jsonb(NEW));
64
64
  RETURN NEW;
65
65
  END IF;
@@ -228,7 +228,7 @@ export async function getChangesSince(
228
228
  limit = 50000
229
229
  ): Promise<ChangeRecord[]> {
230
230
  const result = await db.query<ChangeRecord>(
231
- 'SELECT watermark, table_name, op, row_data, old_data FROM _orez.changes WHERE watermark > $1 ORDER BY watermark LIMIT $2',
231
+ 'SELECT watermark, table_name, op, row_data, old_data FROM _orez._zero_changes WHERE watermark > $1 ORDER BY watermark LIMIT $2',
232
232
  [watermark, limit]
233
233
  )
234
234
  return result.rows
@@ -239,14 +239,14 @@ export async function purgeConsumedChanges(
239
239
  watermark: number
240
240
  ): Promise<number> {
241
241
  const result = await db.exec(
242
- `DELETE FROM _orez.changes WHERE watermark <= ${Number(watermark)}`
242
+ `DELETE FROM _orez._zero_changes WHERE watermark <= ${Number(watermark)}`
243
243
  )
244
244
  return result[0]?.affectedRows ?? 0
245
245
  }
246
246
 
247
247
  export async function getCurrentWatermark(db: PGlite): Promise<number> {
248
248
  const result = await db.query<{ last_value: string; is_called: boolean }>(
249
- 'SELECT last_value, is_called FROM _orez.watermark'
249
+ 'SELECT last_value, is_called FROM _orez._zero_watermark'
250
250
  )
251
251
  const { last_value, is_called } = result.rows[0]
252
252
  if (!is_called) return 0
@@ -32,6 +32,32 @@ import {
32
32
  import type { Mutex } from '../mutex.js'
33
33
  import type { PGlite } from '@electric-sql/pglite'
34
34
 
35
+ // types pglite can't replicate — excluded from change tracking columns
36
+ const UNSUPPORTED_TYPES = new Set(['tsvector', 'tsquery', 'USER-DEFINED'])
37
+
38
+ // pg data_type string → wire protocol oid mapping
39
+ const PG_DATA_TYPE_OIDS: Record<string, number> = {
40
+ boolean: 16,
41
+ bytea: 17,
42
+ bigint: 20,
43
+ smallint: 21,
44
+ integer: 23,
45
+ text: 25,
46
+ json: 114,
47
+ real: 700,
48
+ 'double precision': 701,
49
+ character: 1042,
50
+ 'character varying': 1043,
51
+ date: 1082,
52
+ 'time without time zone': 1083,
53
+ 'timestamp without time zone': 1114,
54
+ 'timestamp with time zone': 1184,
55
+ 'time with time zone': 1266,
56
+ numeric: 1700,
57
+ uuid: 2950,
58
+ jsonb: 3802,
59
+ }
60
+
35
61
  export interface ReplicationWriter {
36
62
  write(data: Uint8Array): void
37
63
  readonly closed?: boolean
@@ -393,7 +419,7 @@ export async function handleStartReplication(
393
419
  const all = await db.query<{ tablename: string }>(
394
420
  `SELECT tablename FROM pg_tables
395
421
  WHERE schemaname = 'public'
396
- AND tablename NOT IN ('migrations', 'changes')
422
+ AND tablename NOT IN ('migrations', '_zero_changes')
397
423
  AND tablename NOT LIKE '_zero_%'`
398
424
  )
399
425
  tables = all.rows
@@ -403,7 +429,7 @@ export async function handleStartReplication(
403
429
  const ddlParts: string[] = [
404
430
  `CREATE OR REPLACE FUNCTION public._zero_notify_change() RETURNS TRIGGER AS $$
405
431
  BEGIN
406
- PERFORM pg_notify('changes', TG_TABLE_NAME);
432
+ PERFORM pg_notify('_zero_changes', TG_TABLE_NAME);
407
433
  RETURN NULL;
408
434
  END;
409
435
  $$ LANGUAGE plpgsql;`,
@@ -499,28 +525,6 @@ export async function handleStartReplication(
499
525
  }
500
526
  keys.add(row.column_name)
501
527
  } else {
502
- const UNSUPPORTED_TYPES = new Set(['tsvector', 'tsquery', 'USER-DEFINED'])
503
- const PG_DATA_TYPE_OIDS: Record<string, number> = {
504
- boolean: 16,
505
- bytea: 17,
506
- bigint: 20,
507
- smallint: 21,
508
- integer: 23,
509
- text: 25,
510
- json: 114,
511
- real: 700,
512
- 'double precision': 701,
513
- character: 1042,
514
- 'character varying': 1043,
515
- date: 1082,
516
- 'time without time zone': 1083,
517
- 'timestamp without time zone': 1114,
518
- 'timestamp with time zone': 1184,
519
- 'time with time zone': 1266,
520
- numeric: 1700,
521
- uuid: 2950,
522
- jsonb: 3802,
523
- }
524
528
  if (row.data_type && UNSUPPORTED_TYPES.has(row.data_type)) {
525
529
  let cols = excludedColumns.get(key)
526
530
  if (!cols) {
@@ -612,8 +616,8 @@ export async function handleStartReplication(
612
616
  // also set up LISTEN as secondary signal
613
617
  let unsubscribe: (() => Promise<void>) | null = null
614
618
  try {
615
- unsubscribe = await db.listen('changes', wakeup)
616
- log.debug.proxy('replication: listening for changes notifications')
619
+ unsubscribe = await db.listen('_zero_changes', wakeup)
620
+ log.debug.proxy('replication: listening for _zero_changes notifications')
617
621
  } catch {
618
622
  log.debug.proxy('replication: LISTEN not available')
619
623
  }
@@ -962,6 +966,13 @@ async function streamChanges(
962
966
  for (const msg of messages) {
963
967
  writer.write(msg)
964
968
  }
969
+
970
+ // hook for arch instrumentation (soot-arch sq-write events)
971
+ const hook = (globalThis as any).__orez_on_repl_commit
972
+ if (hook) {
973
+ const tables = new Set(changes.map((c) => c.table_name))
974
+ hook({ changes: changes.length, tables: [...tables], txId })
975
+ }
965
976
  }
966
977
 
967
978
  function normalizeShardClientsRow(
@@ -24,7 +24,7 @@ describe('browser build config', () => {
24
24
  it('includes Node.js polyfills', () => {
25
25
  const aliases = getBrowserAliases()
26
26
  expect(aliases['node:events']).toBe('events')
27
- expect(aliases['node:stream']).toBe('stream-browserify')
27
+ expect(aliases['node:stream']).toBe('orez/worker/shims/stream-browser')
28
28
  expect(aliases['node:path']).toBe('path-browserify')
29
29
  })
30
30
 
@@ -42,7 +42,10 @@
42
42
  export function getBrowserAliases(): Record<string, string> {
43
43
  return {
44
44
  // -- orez shims for zero-cache dependencies --
45
- postgres: 'orez/worker/shims/postgres',
45
+ // postgres-browser uses the real postgres package with MessagePort transport
46
+ // to pg-proxy-browser, matching orez-node's wire protocol architecture.
47
+ // falls back to old PGlite-wrapping shim if postgres-browser isn't available.
48
+ postgres: 'orez/worker/shims/postgres-browser',
46
49
  '@rocicorp/zero-sqlite3': 'orez/worker/shims/sqlite',
47
50
  fastify: 'orez/worker/shims/fastify',
48
51
  ws: 'orez/worker/shims/ws',
@@ -55,7 +58,7 @@ export function getBrowserAliases(): Record<string, string> {
55
58
  'node:process': 'process/browser',
56
59
  'node:crypto': 'orez/worker/shims/node-stub',
57
60
  'crypto-browserify': 'orez/worker/shims/node-stub',
58
- 'node:stream': 'stream-browserify',
61
+ 'node:stream': 'orez/worker/shims/stream-browser',
59
62
  'node:path': 'path-browserify',
60
63
  'node:os': 'os-browserify/browser',
61
64
 
@@ -171,9 +171,9 @@ export async function startZeroCacheEmbedBrowser(
171
171
  ...((globalThis as any).process.env as Record<string, string>),
172
172
  SINGLE_PROCESS: '1',
173
173
  NODE_ENV: 'development',
174
- ZERO_UPSTREAM_DB: 'pglite://in-process',
175
- ZERO_CVR_DB: 'pglite://in-process',
176
- ZERO_CHANGE_DB: 'pglite://in-process',
174
+ ZERO_UPSTREAM_DB: 'pglite://localhost/postgres',
175
+ ZERO_CVR_DB: 'pglite://localhost/zero_cvr',
176
+ ZERO_CHANGE_DB: 'pglite://localhost/zero_cdb',
177
177
  ZERO_REPLICA_FILE: ':browser-sqlite:',
178
178
  ZERO_PORT: '0',
179
179
  ZERO_APP_ID: appId,
@@ -263,40 +263,43 @@ export async function startZeroCacheEmbedBrowser(
263
263
  },
264
264
 
265
265
  async handleWebSocket(ws: WsLike, url = '/', headers?: Record<string, string>) {
266
- // lazily resolve fastifyInstance — ZeroDispatcher is constructed AFTER the
267
- // ready signal fires, so __orez_fastify_instance may not be set yet.
268
- // the dispatcher's instance is the one with the WS handoff handler,
269
- // and it's always the LAST Fastify() call. we need to wait for it.
270
- //
271
- // check: the dispatcher's server will have 'message' listeners (from
272
- // installWebSocketHandoff). poll until we find an instance with listeners.
266
+ // lazily resolve fastify instances — ZeroDispatcher is constructed AFTER the
267
+ // ready signal fires, so instances may not all be registered yet.
268
+ // poll until we have instances with ws routes (tryHandoff will check).
269
+ let instances: any[] = []
273
270
  for (let i = 0; i < 100; i++) {
271
+ instances = (globalThis as any).__orez_fastify_instances || []
274
272
  fastifyInstance = (globalThis as any).__orez_fastify_instance
275
- // the dispatcher's server has 2+ 'message' listeners:
276
- // 1. from FastifyShim#installWsHandoffHandler (every instance has this)
277
- // 2. from ZeroDispatcher's installWebSocketHandoff (only the dispatcher has this)
278
- // wait for >= 2 to ensure we have the dispatcher's instance
279
- if (fastifyInstance?.server?.listenerCount?.('message') >= 2) break
273
+ // wait until we have at least one instance with 2+ message listeners
274
+ // (the dispatcher's instance has both the shim handler + installWebSocketHandoff)
275
+ if (instances.some((inst: any) => inst?.server?.listenerCount?.('message') >= 2))
276
+ break
280
277
  await new Promise((r) => setTimeout(r, 50))
281
278
  }
282
- if (!isReady || !fastifyInstance?.server) return
283
-
284
- const message = {
285
- url,
286
- headers: headers || {},
287
- method: 'GET',
279
+ if (!isReady) return
280
+
281
+ const handoffMsg = {
282
+ message: {
283
+ url,
284
+ headers: headers || {},
285
+ method: 'GET',
286
+ },
287
+ head: new Uint8Array(0),
288
288
  }
289
289
 
290
- // emit handoff
290
+ // try all fastify instances via tryHandoff, stop at first match
291
+ let handled = false
292
+ for (const inst of instances) {
293
+ if (inst?.tryHandoff?.(handoffMsg, ws)) {
294
+ handled = true
295
+ break
296
+ }
297
+ }
291
298
 
292
- // feed the WebSocket into zero-cache's handoff mechanism.
293
- // the fastify shim's server is an EventEmitter with onMessageType.
294
- // installWebSocketHandoff (non-Server branch) listens for "handoff".
295
- fastifyInstance.server.emit(
296
- 'message',
297
- ['handoff', { message, head: new Uint8Array(0) }],
298
- ws // the WebSocket-like object as sendHandle
299
- )
299
+ // fallback: emit directly on the last instance's server
300
+ if (!handled && fastifyInstance?.server) {
301
+ fastifyInstance.server.emit('message', ['handoff', handoffMsg], ws)
302
+ }
300
303
  },
301
304
 
302
305
  async handleHttp(request: HttpRequest): Promise<HttpResponse> {
@@ -124,32 +124,41 @@ class FastifyShim {
124
124
  // and call the handler with the socket.
125
125
  #installWsHandoffHandler() {
126
126
  this.server.onMessageType('handoff', (msg: any, socket?: any) => {
127
- if (!socket || !msg?.message?.url) return
128
- const url = msg.message.url
129
- const parsedUrl = new URL(url, 'http://localhost')
130
- const pathname = parsedUrl.pathname
131
-
132
- for (const route of this.#wsRoutes) {
133
- if (route.pattern.test(pathname)) {
134
- const req = {
135
- url,
136
- headers: msg.message.headers || {},
137
- method: msg.message.method || 'GET',
138
- }
139
- // wrap socket through handleUpgrade so it gets the full WS API
140
- // (ping, on, once, terminate, etc.) needed by zero-cache's streamOut
141
- this.websocketServer.handleUpgrade(
142
- req,
143
- socket,
144
- Buffer.from(new Uint8Array(0)),
145
- (ws: any) => {
146
- route.handler(ws, req)
147
- }
148
- )
149
- return
127
+ this.tryHandoff(msg, socket)
128
+ })
129
+ }
130
+
131
+ // try to match a handoff message against registered websocket routes.
132
+ // returns true if a route matched, false otherwise.
133
+ // this is public so callers (ws shim, browser-embed) can iterate
134
+ // all fastify instances and stop at the first match.
135
+ tryHandoff(msg: any, socket?: any): boolean {
136
+ if (!socket || !msg?.message?.url) return false
137
+ const url = msg.message.url
138
+ const parsedUrl = new URL(url, 'http://localhost')
139
+ const pathname = parsedUrl.pathname
140
+
141
+ for (const route of this.#wsRoutes) {
142
+ if (route.pattern.test(pathname)) {
143
+ const req = {
144
+ url,
145
+ headers: msg.message.headers || {},
146
+ method: msg.message.method || 'GET',
150
147
  }
148
+ // wrap socket through handleUpgrade so it gets the full WS API
149
+ // (ping, on, once, terminate, etc.) needed by zero-cache's streamOut
150
+ this.websocketServer.handleUpgrade(
151
+ req,
152
+ socket,
153
+ Buffer.from(new Uint8Array(0)),
154
+ (ws: any) => {
155
+ route.handler(ws, req)
156
+ }
157
+ )
158
+ return true
151
159
  }
152
- })
160
+ }
161
+ return false
153
162
  }
154
163
 
155
164
  // route registration — supports optional { websocket: true } option
@@ -286,6 +295,10 @@ function Fastify(_opts?: unknown): FastifyShim {
286
295
  // always overwrite — the ZeroDispatcher (which has the WS handoff routes)
287
296
  // is created LAST, so the final instance is the one handleWebSocket needs.
288
297
  ;(globalThis as any).__orez_fastify_instance = instance
298
+ // track all instances so callers can try handoff against each one
299
+ ;(globalThis as any).__orez_fastify_instances =
300
+ (globalThis as any).__orez_fastify_instances || []
301
+ ;(globalThis as any).__orez_fastify_instances.push(instance)
289
302
  return instance
290
303
  }
291
304
 
@@ -0,0 +1,59 @@
1
+ /**
2
+ * postgres browser shim — real postgres package over MessagePort.
3
+ *
4
+ * replaces the old postgres.ts shim. uses the REAL postgres npm package
5
+ * with a custom socket factory that speaks wire protocol over MessagePort
6
+ * to pg-proxy-browser.
7
+ *
8
+ * setup: set globalThis.__orez_proxy_connect before importing.
9
+ */
10
+
11
+ // import the REAL postgres package — the bundler aliases 'postgres-real' to the
12
+ // actual postgres npm package, avoiding circular resolution since 'postgres'
13
+ // is aliased to this file.
14
+ // @ts-expect-error — resolved by bundler alias
15
+ import postgres from 'postgres-real'
16
+
17
+ import { createSocketFactory } from './postgres-socket.js'
18
+
19
+ const getProxyConnect = (): ((port: MessagePort) => void) => {
20
+ const fn = (globalThis as any).__orez_proxy_connect
21
+ if (!fn) throw new Error('__orez_proxy_connect not set')
22
+ return fn
23
+ }
24
+
25
+ function browserPostgres(urlOrOptions?: any, options?: any) {
26
+ const opts: any =
27
+ typeof urlOrOptions === 'string'
28
+ ? { ...(options || {}), host: urlOrOptions }
29
+ : { ...(urlOrOptions || {}) }
30
+
31
+ opts.socket = createSocketFactory(getProxyConnect())
32
+
33
+ if (typeof urlOrOptions === 'string') {
34
+ try {
35
+ const parsed = new URL(urlOrOptions.replace('pglite://', 'http://'))
36
+ opts.database = parsed.pathname.replace(/^\//, '') || 'postgres'
37
+ opts.host = '127.0.0.1'
38
+ opts.port = 0
39
+ } catch {}
40
+ }
41
+
42
+ opts.ssl = false
43
+ opts.password = (globalThis as any).__orez_proxy_password || ''
44
+ opts.username = (globalThis as any).__orez_proxy_user || 'user'
45
+ // disable auto-subscribe
46
+ if (opts.no_subscribe === undefined) opts.no_subscribe = true
47
+ // limit pool size — too many concurrent connections overwhelm the MessagePort proxy
48
+ // limit pool size for browser — too many concurrent connections can overwhelm
49
+ if (!opts.max || opts.max > 2) opts.max = 2
50
+
51
+ console.debug(
52
+ `[postgres-browser] creating client db=${opts.database} repl=${!!opts.connection?.replication} fetch_types=${opts.fetch_types} max=${opts.max} keys=${Object.keys(opts).sort().join(',')}`
53
+ )
54
+ const client = postgres(opts)
55
+ return client
56
+ }
57
+
58
+ Object.assign(browserPostgres, postgres)
59
+ export default browserPostgres