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.
- package/dist/admin/http-proxy.d.ts.map +1 -1
- package/dist/admin/http-proxy.js +3 -1
- package/dist/admin/http-proxy.js.map +1 -1
- package/dist/admin/log-store.d.ts.map +1 -1
- package/dist/admin/log-store.js +5 -1
- package/dist/admin/log-store.js.map +1 -1
- package/dist/admin/server.d.ts.map +1 -1
- package/dist/admin/server.js +25 -25
- package/dist/admin/server.js.map +1 -1
- package/dist/browser.d.ts +54 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +110 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/pg-proxy-browser.d.ts +26 -0
- package/dist/pg-proxy-browser.d.ts.map +1 -0
- package/dist/pg-proxy-browser.js +1460 -0
- package/dist/pg-proxy-browser.js.map +1 -0
- package/dist/pg-proxy.d.ts.map +1 -1
- package/dist/pg-proxy.js +48 -34
- package/dist/pg-proxy.js.map +1 -1
- package/dist/pglite-ipc.d.ts.map +1 -1
- package/dist/pglite-ipc.js +3 -2
- package/dist/pglite-ipc.js.map +1 -1
- package/dist/pglite-manager.d.ts.map +1 -1
- package/dist/pglite-manager.js +33 -85
- package/dist/pglite-manager.js.map +1 -1
- package/dist/pglite-web-proxy.d.ts +38 -0
- package/dist/pglite-web-proxy.d.ts.map +1 -0
- package/dist/pglite-web-proxy.js +155 -0
- package/dist/pglite-web-proxy.js.map +1 -0
- package/dist/pglite-web-worker.d.ts +24 -0
- package/dist/pglite-web-worker.d.ts.map +1 -0
- package/dist/pglite-web-worker.js +119 -0
- package/dist/pglite-web-worker.js.map +1 -0
- package/dist/recovery.js +2 -2
- package/dist/recovery.js.map +1 -1
- package/dist/replication/change-tracker.js +9 -9
- package/dist/replication/change-tracker.js.map +1 -1
- package/dist/replication/handler.d.ts.map +1 -1
- package/dist/replication/handler.js +34 -26
- package/dist/replication/handler.js.map +1 -1
- package/dist/worker/browser-build-config.d.ts.map +1 -1
- package/dist/worker/browser-build-config.js +5 -2
- package/dist/worker/browser-build-config.js.map +1 -1
- package/dist/worker/browser-embed.d.ts.map +1 -1
- package/dist/worker/browser-embed.js +31 -26
- package/dist/worker/browser-embed.js.map +1 -1
- package/dist/worker/shims/fastify.d.ts +1 -0
- package/dist/worker/shims/fastify.d.ts.map +1 -1
- package/dist/worker/shims/fastify.js +31 -20
- package/dist/worker/shims/fastify.js.map +1 -1
- package/dist/worker/shims/postgres-browser.d.ts +12 -0
- package/dist/worker/shims/postgres-browser.d.ts.map +1 -0
- package/dist/worker/shims/postgres-browser.js +52 -0
- package/dist/worker/shims/postgres-browser.js.map +1 -0
- package/dist/worker/shims/postgres-socket.d.ts +83 -0
- package/dist/worker/shims/postgres-socket.d.ts.map +1 -0
- package/dist/worker/shims/postgres-socket.js +278 -0
- package/dist/worker/shims/postgres-socket.js.map +1 -0
- package/dist/worker/shims/postgres.d.ts.map +1 -1
- package/dist/worker/shims/postgres.js +18 -9
- package/dist/worker/shims/postgres.js.map +1 -1
- package/dist/worker/shims/stream-browser.d.ts +5 -4
- package/dist/worker/shims/stream-browser.d.ts.map +1 -1
- package/dist/worker/shims/stream-browser.js +7 -6
- package/dist/worker/shims/stream-browser.js.map +1 -1
- package/dist/worker/shims/ws-browser.d.ts.map +1 -1
- package/dist/worker/shims/ws-browser.js +43 -21
- package/dist/worker/shims/ws-browser.js.map +1 -1
- package/dist/worker/shims/ws.d.ts.map +1 -1
- package/dist/worker/shims/ws.js +81 -17
- package/dist/worker/shims/ws.js.map +1 -1
- package/package.json +11 -58
- package/src/admin/http-proxy.ts +4 -1
- package/src/admin/log-store.ts +5 -1
- package/src/admin/server.ts +26 -25
- package/src/browser.ts +195 -0
- package/src/cli.ts +1 -1
- package/src/index.ts +5 -2
- package/src/integration/integration.test.ts +1 -1
- package/src/integration/restore-live-stress.test.ts +2 -2
- package/src/pg-proxy-browser.ts +1673 -0
- package/src/pg-proxy.ts +48 -40
- package/src/pglite-ipc.ts +3 -2
- package/src/pglite-manager.ts +45 -107
- package/src/pglite-web-proxy.ts +180 -0
- package/src/pglite-web-worker.ts +132 -0
- package/src/recovery.ts +2 -2
- package/src/replication/change-tracker.test.ts +1 -1
- package/src/replication/change-tracker.ts +9 -9
- package/src/replication/handler.ts +37 -26
- package/src/worker/browser-build-config.test.ts +1 -1
- package/src/worker/browser-build-config.ts +5 -2
- package/src/worker/browser-embed.ts +33 -30
- package/src/worker/shims/fastify.ts +37 -24
- package/src/worker/shims/postgres-browser.ts +59 -0
- package/src/worker/shims/postgres-socket.test.ts +576 -0
- package/src/worker/shims/postgres-socket.ts +310 -0
- package/src/worker/shims/postgres.ts +30 -15
- package/src/worker/shims/stream-browser.ts +15 -0
- package/src/worker/shims/ws-browser.ts +38 -20
- package/src/worker/shims/ws.ts +76 -21
package/src/browser.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* orez browser entry — aligned to orez-node architecture.
|
|
3
|
+
*
|
|
4
|
+
* mirrors index.ts startup sequence:
|
|
5
|
+
* 1. create PGlite Web Workers (3 instances)
|
|
6
|
+
* 2. install change tracking
|
|
7
|
+
* 3. start pg-proxy-browser (wire protocol proxy)
|
|
8
|
+
* 4. start zero-cache (SINGLE_PROCESS=1, connects to proxy)
|
|
9
|
+
*
|
|
10
|
+
* intended to run inside a Web Worker. PGlite instances run in
|
|
11
|
+
* separate Web Workers (like orez-node's worker threads).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { PGliteWebProxy } from './pglite-web-proxy.js'
|
|
15
|
+
import { installChangeTracking } from './replication/change-tracker.js'
|
|
16
|
+
import { resetReplicationState } from './replication/handler.js'
|
|
17
|
+
|
|
18
|
+
import type { PGlite } from '@electric-sql/pglite'
|
|
19
|
+
|
|
20
|
+
export interface OrezBrowserConfig {
|
|
21
|
+
/** app ID for zero-cache (default: 'zero') */
|
|
22
|
+
appId?: string
|
|
23
|
+
|
|
24
|
+
/** publication names for replication */
|
|
25
|
+
publications?: string[]
|
|
26
|
+
|
|
27
|
+
/** URL for PGlite web worker script */
|
|
28
|
+
pgliteWorkerUrl: string
|
|
29
|
+
|
|
30
|
+
/** URL for bedrock-sqlite WASM */
|
|
31
|
+
bedrockSqliteUrl?: string
|
|
32
|
+
|
|
33
|
+
/** init SQL to run on postgres instance after creation */
|
|
34
|
+
initSql?: string
|
|
35
|
+
|
|
36
|
+
/** log level */
|
|
37
|
+
logLevel?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface OrezBrowserInstance {
|
|
41
|
+
/** PGlite proxies (for direct queries from project-server) */
|
|
42
|
+
instances: {
|
|
43
|
+
postgres: PGliteWebProxy
|
|
44
|
+
cvr: PGliteWebProxy
|
|
45
|
+
cdb: PGliteWebProxy
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** signal the replication handler that changes are available */
|
|
49
|
+
signalReplication(): void
|
|
50
|
+
|
|
51
|
+
/** handle a WebSocket connection from a Zero client */
|
|
52
|
+
handleWebSocket(ws: any, url?: string, headers?: Record<string, string>): void
|
|
53
|
+
|
|
54
|
+
/** handle an HTTP request (push/pull) */
|
|
55
|
+
handleHttp(request: {
|
|
56
|
+
method: string
|
|
57
|
+
url: string
|
|
58
|
+
headers?: Record<string, string>
|
|
59
|
+
body?: string | null
|
|
60
|
+
}): Promise<{ status: number; headers: Record<string, string>; body: string }>
|
|
61
|
+
|
|
62
|
+
/** stop everything */
|
|
63
|
+
stop(): Promise<void>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function startOrezBrowser(
|
|
67
|
+
config: OrezBrowserConfig
|
|
68
|
+
): Promise<OrezBrowserInstance> {
|
|
69
|
+
const appId = config.appId || 'zero'
|
|
70
|
+
const publications = config.publications?.join(',') || `orez_${appId}_public`
|
|
71
|
+
|
|
72
|
+
// step 1: create PGlite Web Workers (3 instances, like orez-node)
|
|
73
|
+
const pgPostgresWorker = new Worker(config.pgliteWorkerUrl, {
|
|
74
|
+
type: 'module',
|
|
75
|
+
name: 'pglite-postgres',
|
|
76
|
+
})
|
|
77
|
+
const pgCvrWorker = new Worker(config.pgliteWorkerUrl, {
|
|
78
|
+
type: 'module',
|
|
79
|
+
name: 'pglite-cvr',
|
|
80
|
+
})
|
|
81
|
+
const pgCdbWorker = new Worker(config.pgliteWorkerUrl, {
|
|
82
|
+
type: 'module',
|
|
83
|
+
name: 'pglite-cdb',
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// init each PGlite worker
|
|
87
|
+
pgPostgresWorker.postMessage({
|
|
88
|
+
type: 'init',
|
|
89
|
+
dataDir: 'idb://orez-postgres',
|
|
90
|
+
name: 'postgres',
|
|
91
|
+
})
|
|
92
|
+
pgCvrWorker.postMessage({ type: 'init', dataDir: 'idb://orez-cvr', name: 'cvr' })
|
|
93
|
+
pgCdbWorker.postMessage({ type: 'init', dataDir: 'idb://orez-cdb', name: 'cdb' })
|
|
94
|
+
|
|
95
|
+
// create proxies (like orez-node's PGliteWorkerProxy)
|
|
96
|
+
const pgPostgres = new PGliteWebProxy(pgPostgresWorker, 'postgres')
|
|
97
|
+
const pgCvr = new PGliteWebProxy(pgCvrWorker, 'cvr')
|
|
98
|
+
const pgCdb = new PGliteWebProxy(pgCdbWorker, 'cdb')
|
|
99
|
+
|
|
100
|
+
await Promise.all([pgPostgres.waitReady, pgCvr.waitReady, pgCdb.waitReady])
|
|
101
|
+
console.debug('[orez-browser] all 3 PGlite workers ready')
|
|
102
|
+
|
|
103
|
+
// step 2: install change tracking (like orez-node)
|
|
104
|
+
await installChangeTracking(pgPostgres as unknown as PGlite)
|
|
105
|
+
console.debug('[orez-browser] change tracking installed')
|
|
106
|
+
|
|
107
|
+
// run user init SQL if provided
|
|
108
|
+
if (config.initSql) {
|
|
109
|
+
await pgPostgres.exec(config.initSql)
|
|
110
|
+
console.debug('[orez-browser] init SQL complete')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// create publication
|
|
114
|
+
try {
|
|
115
|
+
const pubs = await pgPostgres.query<{ count: string }>(
|
|
116
|
+
`SELECT count(*) as count FROM pg_publication WHERE pubname = $1`,
|
|
117
|
+
[publications]
|
|
118
|
+
)
|
|
119
|
+
if (Number(pubs.rows[0]?.count) === 0) {
|
|
120
|
+
await pgPostgres.exec(`CREATE PUBLICATION "${publications}"`)
|
|
121
|
+
}
|
|
122
|
+
} catch {}
|
|
123
|
+
|
|
124
|
+
// step 3: start pg-proxy-browser
|
|
125
|
+
// the proxy handles wire protocol, replication, mutexes — like orez-node's pg-proxy.
|
|
126
|
+
// zero-cache's postgres shim routes queries through this proxy.
|
|
127
|
+
const { createBrowserProxy } = await import('./pg-proxy-browser.js')
|
|
128
|
+
const proxy = await createBrowserProxy(
|
|
129
|
+
{
|
|
130
|
+
postgres: pgPostgres as unknown as PGlite,
|
|
131
|
+
cvr: pgCvr as unknown as PGlite,
|
|
132
|
+
cdb: pgCdb as unknown as PGlite,
|
|
133
|
+
},
|
|
134
|
+
{ pgPassword: '', pgUser: 'user' }
|
|
135
|
+
)
|
|
136
|
+
console.debug('[orez-browser] pg-proxy-browser started')
|
|
137
|
+
|
|
138
|
+
// step 4: start zero-cache (SINGLE_PROCESS=1)
|
|
139
|
+
// zero-cache uses the real postgres package with a MessagePort socket
|
|
140
|
+
// that connects to pg-proxy-browser. identical to orez-node where
|
|
141
|
+
// zero-cache uses real postgres over TCP to pg-proxy.
|
|
142
|
+
//
|
|
143
|
+
// set up the proxy connect function for postgres-browser.ts shim
|
|
144
|
+
;(globalThis as any).__orez_proxy_connect = (port: MessagePort) => {
|
|
145
|
+
proxy.handleConnection(port)
|
|
146
|
+
}
|
|
147
|
+
;(globalThis as any).__orez_proxy_password = ''
|
|
148
|
+
;(globalThis as any).__orez_proxy_user = 'user'
|
|
149
|
+
|
|
150
|
+
// PGlite global still needed for browser-embed's sqlite setup
|
|
151
|
+
;(globalThis as any).__orez_pglite = pgPostgres
|
|
152
|
+
|
|
153
|
+
// start zero-cache via browser-embed's runWorker
|
|
154
|
+
const { startZeroCacheEmbedBrowser } = await import('./worker/browser-embed.js')
|
|
155
|
+
const zc = await startZeroCacheEmbedBrowser({
|
|
156
|
+
pglite: pgPostgres as unknown as PGlite,
|
|
157
|
+
appId,
|
|
158
|
+
publications: config.publications,
|
|
159
|
+
env: {
|
|
160
|
+
ZERO_LOG_LEVEL: config.logLevel || 'info',
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
console.debug('[orez-browser] zero-cache started')
|
|
164
|
+
|
|
165
|
+
// step 5: expose API
|
|
166
|
+
const { signalReplicationChange } = await import('./replication/handler.js')
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
instances: { postgres: pgPostgres, cvr: pgCvr, cdb: pgCdb },
|
|
170
|
+
|
|
171
|
+
signalReplication() {
|
|
172
|
+
signalReplicationChange()
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
handleWebSocket(ws: any, url = '/', headers?: Record<string, string>) {
|
|
176
|
+
zc.handleWebSocket(ws, url)
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
async handleHttp(request: {
|
|
180
|
+
method: string
|
|
181
|
+
url: string
|
|
182
|
+
headers?: Record<string, string>
|
|
183
|
+
body?: string | null
|
|
184
|
+
}) {
|
|
185
|
+
return zc.handleHttp(request)
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
async stop() {
|
|
189
|
+
await zc.stop()
|
|
190
|
+
proxy.close()
|
|
191
|
+
resetReplicationState()
|
|
192
|
+
await Promise.all([pgPostgres.close(), pgCvr.close(), pgCdb.close()])
|
|
193
|
+
},
|
|
194
|
+
}
|
|
195
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -621,7 +621,7 @@ async function tryWireRestore(opts: {
|
|
|
621
621
|
)
|
|
622
622
|
|
|
623
623
|
// clear zero replication state (in _orez schema)
|
|
624
|
-
await sql.unsafe('TRUNCATE _orez.
|
|
624
|
+
await sql.unsafe('TRUNCATE _orez._zero_changes').catch(() => {})
|
|
625
625
|
await sql.unsafe('TRUNCATE _orez._zero_replication_slots').catch(() => {})
|
|
626
626
|
log.orez('cleared zero replication state')
|
|
627
627
|
|
package/src/index.ts
CHANGED
|
@@ -53,6 +53,7 @@ type ZeroChildProcess = ChildProcess & { __orezTail?: string[] }
|
|
|
53
53
|
|
|
54
54
|
export { getConfig, getConnectionString } from './config.js'
|
|
55
55
|
export type { Hook, LogLevel, ZeroLiteConfig } from './config.js'
|
|
56
|
+
export { installChangeTracking } from './replication/change-tracker.js'
|
|
56
57
|
|
|
57
58
|
// helper to run a hook (string command or callback function)
|
|
58
59
|
async function runHook(
|
|
@@ -540,9 +541,11 @@ export async function startZeroLite(overrides: Partial<ZeroLiteConfig> = {}) {
|
|
|
540
541
|
|
|
541
542
|
// clear upstream replication tracking so zero-cache starts from a
|
|
542
543
|
// clean change stream baseline after full reset.
|
|
543
|
-
await db.exec(`TRUNCATE _orez.
|
|
544
|
+
await db.exec(`TRUNCATE _orez._zero_changes`).catch(() => {})
|
|
544
545
|
await db.exec(`TRUNCATE _orez._zero_replication_slots`).catch(() => {})
|
|
545
|
-
await db
|
|
546
|
+
await db
|
|
547
|
+
.exec(`ALTER SEQUENCE _orez._zero_watermark RESTART WITH 1`)
|
|
548
|
+
.catch(() => {})
|
|
546
549
|
log.orez('cleared upstream replication tracking state')
|
|
547
550
|
}
|
|
548
551
|
|
|
@@ -198,7 +198,7 @@ describe('orez integration', { timeout: 120000 }, () => {
|
|
|
198
198
|
const deadline = Date.now() + timeoutMs
|
|
199
199
|
while (Date.now() < deadline) {
|
|
200
200
|
const result = await pglite.query<{ count: string }>(
|
|
201
|
-
`SELECT count(*)::text as count FROM _orez.
|
|
201
|
+
`SELECT count(*)::text as count FROM _orez._zero_changes`
|
|
202
202
|
)
|
|
203
203
|
if (Number(result.rows[0]?.count) === 0) return
|
|
204
204
|
await new Promise((r) => setTimeout(r, 100))
|
|
@@ -419,11 +419,11 @@ describe('live restore stress with connected frontend', { timeout: 360_000 }, ()
|
|
|
419
419
|
])
|
|
420
420
|
const tracked = await db.query<{ count: string }>(
|
|
421
421
|
`SELECT count(*)::text as count
|
|
422
|
-
FROM _orez.
|
|
422
|
+
FROM _orez._zero_changes
|
|
423
423
|
WHERE table_name = 'public.restore_live_probe'`
|
|
424
424
|
)
|
|
425
425
|
if (Number(tracked.rows[0]?.count || '0') === 0) {
|
|
426
|
-
throw new Error('post-reset write was not captured in _orez.
|
|
426
|
+
throw new Error('post-reset write was not captured in _orez._zero_changes')
|
|
427
427
|
}
|
|
428
428
|
|
|
429
429
|
await waitForPokeWithValue(downstreamAfterReset, marker, 30_000)
|