orez 0.2.27 → 0.2.30
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/cf-do/worker.d.ts +3 -0
- package/dist/cf-do/worker.d.ts.map +1 -1
- package/dist/cf-do/worker.js +37 -15
- package/dist/cf-do/worker.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/admin/admin-data.test.ts +0 -348
- package/src/admin/http-proxy.ts +0 -252
- package/src/admin/log-store.ts +0 -192
- package/src/admin/server.ts +0 -471
- package/src/admin/ui.ts +0 -1322
- package/src/bench/proxy-throughput.bench.ts +0 -343
- package/src/bench/serial-mutations.bench.ts +0 -270
- package/src/browser.ts +0 -203
- package/src/cf-do/.wrangler/cache/cf.json +0 -1
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0ffaabee41a60e04dd0eb7db3073f0a40139e6a97ccd26823967acb652b89a7b.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
- package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-insertion-facade.js +0 -11
- package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-loader.entry.ts +0 -134
- package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-insertion-facade.js +0 -11
- package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-loader.entry.ts +0 -134
- package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js +0 -1059
- package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js.map +0 -8
- package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js +0 -1059
- package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js.map +0 -8
- package/src/cf-do/ARCHITECTURE.md +0 -93
- package/src/cf-do/CHAT_E2E.md +0 -213
- package/src/cf-do/watermark.test.ts +0 -103
- package/src/cf-do/watermark.ts +0 -118
- package/src/cf-do/worker.ts +0 -1041
- package/src/cf-do/wrangler.toml +0 -11
- package/src/cf-pglite/README.md +0 -19
- package/src/change-tracking.ts +0 -25
- package/src/child-process.test.ts +0 -147
- package/src/child-process.ts +0 -90
- package/src/cli-entry.ts +0 -72
- package/src/cli.test.ts +0 -40
- package/src/cli.ts +0 -1214
- package/src/config.ts +0 -150
- package/src/do-sql-tracking.test.ts +0 -19
- package/src/do-sql-tracking.ts +0 -19
- package/src/index.ts +0 -1215
- package/src/integration/integration.test.ts +0 -517
- package/src/integration/native-binary.guard.test.ts +0 -13
- package/src/integration/native-startup.test.ts +0 -44
- package/src/integration/replication-latency.test.ts +0 -428
- package/src/integration/restore-live-stress.test.ts +0 -433
- package/src/integration/restore-reset.test.ts +0 -400
- package/src/integration/restore.test.ts +0 -274
- package/src/integration/test-permissions.ts +0 -147
- package/src/load-config.ts +0 -46
- package/src/log.ts +0 -96
- package/src/mutex.ts +0 -47
- package/src/pg-proxy-browser.singledb.test.ts +0 -233
- package/src/pg-proxy-browser.ts +0 -2022
- package/src/pg-proxy-do-backend.test.ts +0 -3890
- package/src/pg-proxy-do-backend.ts +0 -7191
- package/src/pg-proxy.ts +0 -1087
- package/src/pg-sqlite-compiler/README.md +0 -53
- package/src/pg-sqlite-compiler/catalog/seed.ts +0 -524
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/arithmetic.json +0 -307
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/array.json +0 -377
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/cast.json +0 -12
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/catalog.json +0 -447
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/create-table.json +0 -32
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/datetime.json +0 -397
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/enum.json +0 -337
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/insert.json +0 -337
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/json.json +0 -537
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/misc.json +0 -1837
- package/src/pg-sqlite-compiler/index.ts +0 -73
- package/src/pg-sqlite-compiler/integration.test.ts +0 -136
- package/src/pg-sqlite-compiler/passes/ast-utils.ts +0 -113
- package/src/pg-sqlite-compiler/passes/catalog.ts +0 -65
- package/src/pg-sqlite-compiler/passes/datetime.ts +0 -74
- package/src/pg-sqlite-compiler/passes/index.ts +0 -49
- package/src/pg-sqlite-compiler/passes/types.ts +0 -156
- package/src/pg-sqlite-compiler/smoke.test.ts +0 -69
- package/src/pg-sqlite-compiler/test/catalog.test.ts +0 -171
- package/src/pg-sqlite-compiler/test/corpus.test.ts +0 -161
- package/src/pg-sqlite-compiler/test/datetime.oracle.test.ts +0 -102
- package/src/pg-sqlite-compiler/test/oracle.ts +0 -237
- package/src/pg-sqlite-compiler/test/types.test.ts +0 -109
- package/src/pg-sqlite-compiler/types.ts +0 -63
- package/src/pglite-ipc.test.ts +0 -116
- package/src/pglite-ipc.ts +0 -266
- package/src/pglite-manager.ts +0 -557
- package/src/pglite-web-proxy.test.ts +0 -57
- package/src/pglite-web-proxy.ts +0 -221
- package/src/pglite-web-worker.ts +0 -152
- package/src/pglite-worker-thread.ts +0 -253
- package/src/port.ts +0 -25
- package/src/process-title.ts +0 -9
- package/src/recovery.ts +0 -155
- package/src/replication/change-tracker.test.ts +0 -357
- package/src/replication/change-tracker.ts +0 -279
- package/src/replication/handler.test.ts +0 -511
- package/src/replication/handler.ts +0 -1190
- package/src/replication/pgoutput-encoder.test.ts +0 -697
- package/src/replication/pgoutput-encoder.ts +0 -373
- package/src/replication/tcp-replication.test.ts +0 -876
- package/src/replication/zero-compat.test.ts +0 -1150
- package/src/restore-stress.test.ts +0 -188
- package/src/s3-local.ts +0 -203
- package/src/shim/hooks.mjs +0 -120
- package/src/shim/register.mjs +0 -4
- package/src/sqlite-mode/apply-mode.ts +0 -224
- package/src/sqlite-mode/index.ts +0 -15
- package/src/sqlite-mode/native-binary.ts +0 -89
- package/src/sqlite-mode/package-resolve.ts +0 -17
- package/src/sqlite-mode/resolve-mode.ts +0 -80
- package/src/sqlite-mode/shim-template.ts +0 -159
- package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
- package/src/sqlite-mode/types.ts +0 -30
- package/src/vite-plugin.ts +0 -67
- package/src/wasm-sqlite.test.ts +0 -537
- package/src/worker/browser-admin.ts +0 -52
- package/src/worker/browser-build-config.test.ts +0 -71
- package/src/worker/browser-build-config.ts +0 -109
- package/src/worker/browser-embed-admin.test.ts +0 -75
- package/src/worker/browser-embed.ts +0 -345
- package/src/worker/cf-patches.ts +0 -384
- package/src/worker/embed-integration.test.ts +0 -321
- package/src/worker/index.ts +0 -138
- package/src/worker/shims/fastify.test.ts +0 -255
- package/src/worker/shims/fastify.ts +0 -306
- package/src/worker/shims/http-service.test.ts +0 -355
- package/src/worker/shims/http-service.ts +0 -293
- package/src/worker/shims/node-stub.ts +0 -290
- package/src/worker/shims/oxfmt.ts +0 -3
- package/src/worker/shims/postgres-browser.ts +0 -59
- package/src/worker/shims/postgres-socket.test.ts +0 -576
- package/src/worker/shims/postgres-socket.ts +0 -310
- package/src/worker/shims/postgres.test.ts +0 -364
- package/src/worker/shims/postgres.ts +0 -1454
- package/src/worker/shims/sqlite-browser.test.ts +0 -233
- package/src/worker/shims/sqlite-browser.ts +0 -175
- package/src/worker/shims/sqlite.test.ts +0 -786
- package/src/worker/shims/sqlite.ts +0 -978
- package/src/worker/shims/stream-browser.ts +0 -15
- package/src/worker/shims/ws-browser.test.ts +0 -205
- package/src/worker/shims/ws-browser.ts +0 -248
- package/src/worker/shims/ws.test.ts +0 -288
- package/src/worker/shims/ws.ts +0 -467
- package/src/worker/shims/zero-process-env.ts +0 -11
- package/src/worker/types.ts +0 -75
- package/src/worker/worker-integration.test.ts +0 -223
- package/src/worker/worker.test.ts +0 -136
- package/src/worker/zero-cache-embed-cf.ts +0 -463
- package/src/worker/zero-cache-embed.ts +0 -277
package/src/pglite-web-proxy.ts
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PGlite Web Worker proxy — browser equivalent of pglite-ipc.ts.
|
|
3
|
-
*
|
|
4
|
-
* runs in the zero-cache worker, proxies calls to a Web Worker
|
|
5
|
-
* running the actual PGlite instance. mirrors PGliteWorkerProxy
|
|
6
|
-
* from pglite-ipc.ts but uses Web Worker postMessage instead of
|
|
7
|
-
* node worker_threads.
|
|
8
|
-
*
|
|
9
|
-
* ArrayBuffers are transferred (not copied) for execProtocolRaw
|
|
10
|
-
* to keep IPC overhead near-zero for wire protocol data.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { signalReplicationChange } from './replication/handler.js'
|
|
14
|
-
|
|
15
|
-
interface PendingRequest {
|
|
16
|
-
resolve: (value: any) => void
|
|
17
|
-
reject: (error: Error) => void
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const WRITE_PREFIXES = ['insert', 'update', 'delete', 'copy', 'truncate']
|
|
21
|
-
const SHARD_INTERNAL_TABLES = ['"replicas"', '"mutations"', '"replicationState"']
|
|
22
|
-
|
|
23
|
-
function isReplicatedWrite(sql: string): boolean {
|
|
24
|
-
const q = sql.trimStart().toLowerCase()
|
|
25
|
-
if (!WRITE_PREFIXES.some((p) => q.startsWith(p))) return false
|
|
26
|
-
for (const t of SHARD_INTERNAL_TABLES) {
|
|
27
|
-
if (q.includes(t.toLowerCase())) return false
|
|
28
|
-
}
|
|
29
|
-
return true
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export class PGliteWebProxy {
|
|
33
|
-
private worker: Worker
|
|
34
|
-
private pending = new Map<number, PendingRequest>()
|
|
35
|
-
private nextId = 1
|
|
36
|
-
private notificationCallbacks = new Map<string, Set<(payload: string) => void>>()
|
|
37
|
-
private failure: Error | null = null
|
|
38
|
-
readonly name: string
|
|
39
|
-
|
|
40
|
-
readonly waitReady: Promise<void>
|
|
41
|
-
|
|
42
|
-
// PGlite compat flags
|
|
43
|
-
closed = false
|
|
44
|
-
ready = false
|
|
45
|
-
|
|
46
|
-
constructor(worker: Worker, name: string) {
|
|
47
|
-
this.name = name
|
|
48
|
-
this.worker = worker
|
|
49
|
-
|
|
50
|
-
let onReady: () => void
|
|
51
|
-
this.waitReady = new Promise<void>((resolveReady, rejectReady) => {
|
|
52
|
-
onReady = () => {
|
|
53
|
-
this.ready = true
|
|
54
|
-
resolveReady()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const onMessage = (ev: MessageEvent) => {
|
|
58
|
-
const msg = ev.data
|
|
59
|
-
if (msg?.type === 'ready') {
|
|
60
|
-
this.worker.removeEventListener('message', onMessage)
|
|
61
|
-
this.installMessageHandler()
|
|
62
|
-
onReady()
|
|
63
|
-
} else if (msg?.type === 'error' && msg.id === 0) {
|
|
64
|
-
rejectReady(new Error(msg.message))
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
this.worker.addEventListener('message', onMessage)
|
|
69
|
-
this.worker.addEventListener('error', (ev) => {
|
|
70
|
-
const error = this.errorFromEvent(ev, 'worker failed during startup')
|
|
71
|
-
this.failPending(error)
|
|
72
|
-
rejectReady(error)
|
|
73
|
-
})
|
|
74
|
-
this.worker.addEventListener('messageerror', (ev) => {
|
|
75
|
-
const error = this.errorFromEvent(ev, 'worker message error during startup')
|
|
76
|
-
this.failPending(error)
|
|
77
|
-
rejectReady(error)
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
private installMessageHandler() {
|
|
83
|
-
this.worker.addEventListener('error', (ev) => {
|
|
84
|
-
this.failPending(this.errorFromEvent(ev, 'worker failed'))
|
|
85
|
-
})
|
|
86
|
-
this.worker.addEventListener('messageerror', (ev) => {
|
|
87
|
-
this.failPending(this.errorFromEvent(ev, 'worker message error'))
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
this.worker.addEventListener('message', (ev: MessageEvent) => {
|
|
91
|
-
const msg = ev.data
|
|
92
|
-
if (!msg || typeof msg !== 'object') return
|
|
93
|
-
|
|
94
|
-
if (msg.type === 'notification') {
|
|
95
|
-
const callbacks = this.notificationCallbacks.get(msg.channel)
|
|
96
|
-
if (callbacks) {
|
|
97
|
-
for (const cb of callbacks) {
|
|
98
|
-
try {
|
|
99
|
-
cb(msg.payload)
|
|
100
|
-
} catch {}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const req = this.pending.get(msg.id)
|
|
107
|
-
if (!req) return
|
|
108
|
-
this.pending.delete(msg.id)
|
|
109
|
-
|
|
110
|
-
if (msg.type === 'error') {
|
|
111
|
-
const err = new Error(msg.message) as Error & { code?: string }
|
|
112
|
-
if (msg.code) err.code = msg.code
|
|
113
|
-
req.reject(err)
|
|
114
|
-
} else {
|
|
115
|
-
req.resolve(msg)
|
|
116
|
-
}
|
|
117
|
-
})
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
private errorFromEvent(ev: Event, fallback: string): Error {
|
|
121
|
-
const maybeError = ev as ErrorEvent
|
|
122
|
-
if (maybeError.error instanceof Error) return maybeError.error
|
|
123
|
-
if (maybeError.message) return new Error(maybeError.message)
|
|
124
|
-
return new Error(fallback)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
private failPending(error: Error) {
|
|
128
|
-
if (!this.failure) this.failure = error
|
|
129
|
-
this.closed = true
|
|
130
|
-
this.ready = false
|
|
131
|
-
for (const [, req] of this.pending) {
|
|
132
|
-
req.reject(error)
|
|
133
|
-
}
|
|
134
|
-
this.pending.clear()
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
private send(msg: Record<string, unknown>, transfer?: Transferable[]): Promise<any> {
|
|
138
|
-
if (this.failure) return Promise.reject(this.failure)
|
|
139
|
-
if (this.closed) return Promise.reject(new Error('worker is closed'))
|
|
140
|
-
|
|
141
|
-
const id = this.nextId++
|
|
142
|
-
msg.id = id
|
|
143
|
-
return new Promise((resolve, reject) => {
|
|
144
|
-
this.pending.set(id, { resolve, reject })
|
|
145
|
-
try {
|
|
146
|
-
if (transfer?.length) {
|
|
147
|
-
this.worker.postMessage(msg, transfer)
|
|
148
|
-
} else {
|
|
149
|
-
this.worker.postMessage(msg)
|
|
150
|
-
}
|
|
151
|
-
} catch (err) {
|
|
152
|
-
this.pending.delete(id)
|
|
153
|
-
reject(err instanceof Error ? err : new Error(String(err)))
|
|
154
|
-
}
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async execProtocolRaw(
|
|
159
|
-
data: Uint8Array,
|
|
160
|
-
options?: { syncToFs?: boolean; throwOnError?: boolean }
|
|
161
|
-
): Promise<Uint8Array> {
|
|
162
|
-
// copy to a transferable buffer then transfer
|
|
163
|
-
const buf = new ArrayBuffer(data.byteLength)
|
|
164
|
-
new Uint8Array(buf).set(data)
|
|
165
|
-
const result = await this.send({ type: 'execProtocolRaw', data: buf, options }, [buf])
|
|
166
|
-
return new Uint8Array(result.data)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async query<T = any>(
|
|
170
|
-
sql: string,
|
|
171
|
-
params?: any[]
|
|
172
|
-
): Promise<{ rows: T[]; affectedRows?: number }> {
|
|
173
|
-
const result = await this.send({ type: 'query', sql, params })
|
|
174
|
-
// signal replication after writes on postgres instance (like orez-node's PGliteWorkerProxy)
|
|
175
|
-
if (this.name === 'postgres' && isReplicatedWrite(sql)) {
|
|
176
|
-
signalReplicationChange()
|
|
177
|
-
}
|
|
178
|
-
return { rows: result.rows ?? [], affectedRows: result.affectedRows }
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
async exec(sql: string): Promise<{ affectedRows?: number }[]> {
|
|
182
|
-
const result = await this.send({ type: 'exec', sql })
|
|
183
|
-
if (this.name === 'postgres' && isReplicatedWrite(sql)) {
|
|
184
|
-
signalReplicationChange()
|
|
185
|
-
}
|
|
186
|
-
return result.results ?? []
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async listen(
|
|
190
|
-
channel: string,
|
|
191
|
-
callback: (payload: string) => void
|
|
192
|
-
): Promise<() => Promise<void>> {
|
|
193
|
-
let callbacks = this.notificationCallbacks.get(channel)
|
|
194
|
-
if (!callbacks) {
|
|
195
|
-
callbacks = new Set()
|
|
196
|
-
this.notificationCallbacks.set(channel, callbacks)
|
|
197
|
-
}
|
|
198
|
-
callbacks.add(callback)
|
|
199
|
-
|
|
200
|
-
const result = await this.send({ type: 'listen', channel })
|
|
201
|
-
const listenId = result.id
|
|
202
|
-
|
|
203
|
-
return async () => {
|
|
204
|
-
callbacks!.delete(callback)
|
|
205
|
-
if (callbacks!.size === 0) {
|
|
206
|
-
this.notificationCallbacks.delete(channel)
|
|
207
|
-
}
|
|
208
|
-
await this.send({ type: 'unlisten', listenId }).catch(() => {})
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async close(): Promise<void> {
|
|
213
|
-
try {
|
|
214
|
-
await this.send({ type: 'close' })
|
|
215
|
-
} catch {}
|
|
216
|
-
this.closed = true
|
|
217
|
-
this.ready = false
|
|
218
|
-
this.failPending(new Error('worker is closed'))
|
|
219
|
-
this.worker.terminate()
|
|
220
|
-
}
|
|
221
|
-
}
|
package/src/pglite-web-worker.ts
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PGlite Web Worker — browser equivalent of pglite-worker-thread.ts.
|
|
3
|
-
*
|
|
4
|
-
* runs a single PGlite instance in a Web Worker. receives commands via
|
|
5
|
-
* postMessage, executes on PGlite, sends results back. ArrayBuffers
|
|
6
|
-
* are transferred (not copied) for execProtocolRaw.
|
|
7
|
-
*
|
|
8
|
-
* message protocol (same as pglite-worker-thread.ts):
|
|
9
|
-
* init: { type: 'init', dataDir, name, withExtensions, pgliteOptions }
|
|
10
|
-
* → { type: 'ready' }
|
|
11
|
-
*
|
|
12
|
-
* execProtocolRaw: { type: 'execProtocolRaw', id, data: ArrayBuffer, options }
|
|
13
|
-
* → { type: 'result', id, data: ArrayBuffer }
|
|
14
|
-
*
|
|
15
|
-
* query: { type: 'query', id, sql, params }
|
|
16
|
-
* → { type: 'result', id, rows, affectedRows }
|
|
17
|
-
*
|
|
18
|
-
* exec: { type: 'exec', id, sql }
|
|
19
|
-
* → { type: 'result', id, results: [{ affectedRows }] }
|
|
20
|
-
*
|
|
21
|
-
* listen/unlisten/close: same as pglite-worker-thread.ts
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
// NOTE: this file is meant to be bundled with PGlite as external
|
|
25
|
-
// the consumer provides PGlite via importScripts or ESM import
|
|
26
|
-
|
|
27
|
-
declare const self: any
|
|
28
|
-
|
|
29
|
-
const listeners = new Map<number, () => Promise<void>>()
|
|
30
|
-
let db: any // PGlite instance — type depends on how it's loaded
|
|
31
|
-
|
|
32
|
-
self.onmessage = async (ev: MessageEvent) => {
|
|
33
|
-
const msg = ev.data
|
|
34
|
-
if (!msg || typeof msg !== 'object') return
|
|
35
|
-
const { type, id } = msg
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
switch (type) {
|
|
39
|
-
case 'init': {
|
|
40
|
-
// dynamically import PGlite (external, provided by consumer's bundler)
|
|
41
|
-
const { PGlite } = await import('@electric-sql/pglite')
|
|
42
|
-
db = new PGlite({
|
|
43
|
-
dataDir: msg.dataDir || 'idb://orez-pglite',
|
|
44
|
-
relaxedDurability: true,
|
|
45
|
-
initialMemory: 32 * 1024 * 1024,
|
|
46
|
-
startParams: [
|
|
47
|
-
'--single',
|
|
48
|
-
'-F',
|
|
49
|
-
'-O',
|
|
50
|
-
'-j',
|
|
51
|
-
'-c',
|
|
52
|
-
'search_path=public',
|
|
53
|
-
'-c',
|
|
54
|
-
'exit_on_error=false',
|
|
55
|
-
'-c',
|
|
56
|
-
'log_checkpoints=false',
|
|
57
|
-
'-c',
|
|
58
|
-
'shared_buffers=4MB',
|
|
59
|
-
'-c',
|
|
60
|
-
'wal_buffers=1MB',
|
|
61
|
-
],
|
|
62
|
-
...(msg.pgliteOptions || {}),
|
|
63
|
-
// extensions loaded by consumer if needed
|
|
64
|
-
})
|
|
65
|
-
await db.waitReady
|
|
66
|
-
|
|
67
|
-
// tune postgres internals — modest values for embedded use
|
|
68
|
-
await db.exec(`
|
|
69
|
-
SET work_mem = '4MB';
|
|
70
|
-
SET maintenance_work_mem = '16MB';
|
|
71
|
-
SET effective_cache_size = '64MB';
|
|
72
|
-
SET random_page_cost = 1.1;
|
|
73
|
-
SET jit = off;
|
|
74
|
-
`)
|
|
75
|
-
|
|
76
|
-
self.postMessage({ type: 'ready' })
|
|
77
|
-
break
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
case 'execProtocolRaw': {
|
|
81
|
-
const input = new Uint8Array(msg.data as ArrayBuffer)
|
|
82
|
-
const result = await db.execProtocolRaw(input, msg.options)
|
|
83
|
-
const buf = new ArrayBuffer(result.byteLength)
|
|
84
|
-
new Uint8Array(buf).set(result)
|
|
85
|
-
self.postMessage({ type: 'result', id, data: buf }, [buf])
|
|
86
|
-
break
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
case 'query': {
|
|
90
|
-
const result = await db.query(msg.sql, msg.params)
|
|
91
|
-
self.postMessage({
|
|
92
|
-
type: 'result',
|
|
93
|
-
id,
|
|
94
|
-
rows: result.rows,
|
|
95
|
-
affectedRows: result.affectedRows,
|
|
96
|
-
})
|
|
97
|
-
break
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
case 'exec': {
|
|
101
|
-
const result = await db.exec(msg.sql)
|
|
102
|
-
const results = result.map((r: any) => ({ affectedRows: r.affectedRows ?? 0 }))
|
|
103
|
-
self.postMessage({ type: 'result', id, results })
|
|
104
|
-
break
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
case 'listen': {
|
|
108
|
-
const unsub = await db.listen(msg.channel, (payload: string) => {
|
|
109
|
-
self.postMessage({ type: 'notification', channel: msg.channel, payload })
|
|
110
|
-
})
|
|
111
|
-
listeners.set(id, unsub)
|
|
112
|
-
self.postMessage({ type: 'result', id })
|
|
113
|
-
break
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
case 'unlisten': {
|
|
117
|
-
const unsub = listeners.get(msg.listenId)
|
|
118
|
-
if (unsub) {
|
|
119
|
-
await unsub()
|
|
120
|
-
listeners.delete(msg.listenId)
|
|
121
|
-
}
|
|
122
|
-
self.postMessage({ type: 'result', id })
|
|
123
|
-
break
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
case 'close': {
|
|
127
|
-
for (const unsub of listeners.values()) {
|
|
128
|
-
await unsub().catch(() => {})
|
|
129
|
-
}
|
|
130
|
-
listeners.clear()
|
|
131
|
-
await db.close()
|
|
132
|
-
self.postMessage({ type: 'result', id })
|
|
133
|
-
break
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
default:
|
|
137
|
-
self.postMessage({
|
|
138
|
-
type: 'error',
|
|
139
|
-
id,
|
|
140
|
-
message: `unknown message type: ${type}`,
|
|
141
|
-
})
|
|
142
|
-
}
|
|
143
|
-
} catch (err: unknown) {
|
|
144
|
-
const error = err as { message?: string; code?: string }
|
|
145
|
-
self.postMessage({
|
|
146
|
-
type: 'error',
|
|
147
|
-
id,
|
|
148
|
-
message: error?.message || String(err),
|
|
149
|
-
code: error?.code,
|
|
150
|
-
})
|
|
151
|
-
}
|
|
152
|
-
}
|
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* worker thread that runs a single PGlite instance.
|
|
3
|
-
*
|
|
4
|
-
* receives commands via parentPort messages, executes them on the PGlite
|
|
5
|
-
* instance, and sends results back. ArrayBuffers are transferred (not copied)
|
|
6
|
-
* for execProtocolRaw to minimize overhead.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { parentPort, workerData } from 'node:worker_threads'
|
|
10
|
-
|
|
11
|
-
import { PGlite } from '@electric-sql/pglite'
|
|
12
|
-
import { btree_gin } from '@electric-sql/pglite/contrib/btree_gin'
|
|
13
|
-
import { btree_gist } from '@electric-sql/pglite/contrib/btree_gist'
|
|
14
|
-
import { citext } from '@electric-sql/pglite/contrib/citext'
|
|
15
|
-
import { cube } from '@electric-sql/pglite/contrib/cube'
|
|
16
|
-
import { earthdistance } from '@electric-sql/pglite/contrib/earthdistance'
|
|
17
|
-
import { fuzzystrmatch } from '@electric-sql/pglite/contrib/fuzzystrmatch'
|
|
18
|
-
import { hstore } from '@electric-sql/pglite/contrib/hstore'
|
|
19
|
-
import { ltree } from '@electric-sql/pglite/contrib/ltree'
|
|
20
|
-
import { pg_trgm } from '@electric-sql/pglite/contrib/pg_trgm'
|
|
21
|
-
import { pgcrypto } from '@electric-sql/pglite/contrib/pgcrypto'
|
|
22
|
-
import { uuid_ossp } from '@electric-sql/pglite/contrib/uuid_ossp'
|
|
23
|
-
import { vector } from '@electric-sql/pglite/vector'
|
|
24
|
-
|
|
25
|
-
export interface WorkerInitConfig {
|
|
26
|
-
dataDir: string
|
|
27
|
-
name: string
|
|
28
|
-
withExtensions: boolean
|
|
29
|
-
debug: number
|
|
30
|
-
pgliteOptions?: Record<string, unknown>
|
|
31
|
-
/** tar dump from another instance's dumpDataDir() — used for read replicas */
|
|
32
|
-
loadDataDir?: ArrayBuffer
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const port = parentPort!
|
|
36
|
-
const config = workerData as WorkerInitConfig
|
|
37
|
-
|
|
38
|
-
// active listen subscriptions
|
|
39
|
-
const listeners = new Map<number, () => Promise<void>>()
|
|
40
|
-
|
|
41
|
-
let db: PGlite
|
|
42
|
-
|
|
43
|
-
const PGLITE_BASE_FLAGS = [
|
|
44
|
-
'--single',
|
|
45
|
-
'-F',
|
|
46
|
-
'-O',
|
|
47
|
-
'-j',
|
|
48
|
-
'-c',
|
|
49
|
-
'search_path=public',
|
|
50
|
-
'-c',
|
|
51
|
-
'exit_on_error=false',
|
|
52
|
-
'-c',
|
|
53
|
-
'log_checkpoints=false',
|
|
54
|
-
]
|
|
55
|
-
|
|
56
|
-
const ZERO_START_PARAMS = [
|
|
57
|
-
...PGLITE_BASE_FLAGS,
|
|
58
|
-
'-c',
|
|
59
|
-
'shared_buffers=128kB',
|
|
60
|
-
'-c',
|
|
61
|
-
'wal_buffers=64kB',
|
|
62
|
-
'-c',
|
|
63
|
-
'work_mem=64kB',
|
|
64
|
-
'-c',
|
|
65
|
-
'maintenance_work_mem=1MB',
|
|
66
|
-
'-c',
|
|
67
|
-
'temp_buffers=800kB',
|
|
68
|
-
]
|
|
69
|
-
|
|
70
|
-
async function init() {
|
|
71
|
-
const { dataDir: _userDataDir, debug: _dbg, ...userOpts } = config.pgliteOptions || {}
|
|
72
|
-
const isMain = config.withExtensions
|
|
73
|
-
|
|
74
|
-
db = new PGlite({
|
|
75
|
-
dataDir: config.dataDir,
|
|
76
|
-
debug: config.debug,
|
|
77
|
-
relaxedDurability: true,
|
|
78
|
-
...(config.loadDataDir ? { loadDataDir: new Blob([config.loadDataDir]) } : {}),
|
|
79
|
-
initialMemory: isMain ? 32 * 1024 * 1024 : 16 * 1024 * 1024,
|
|
80
|
-
...(isMain ? {} : { startParams: ZERO_START_PARAMS }),
|
|
81
|
-
...(isMain
|
|
82
|
-
? {
|
|
83
|
-
startParams: [
|
|
84
|
-
...PGLITE_BASE_FLAGS,
|
|
85
|
-
'-c',
|
|
86
|
-
'shared_buffers=4MB',
|
|
87
|
-
'-c',
|
|
88
|
-
'wal_buffers=1MB',
|
|
89
|
-
],
|
|
90
|
-
...userOpts,
|
|
91
|
-
extensions: userOpts.extensions || {
|
|
92
|
-
vector,
|
|
93
|
-
pg_trgm,
|
|
94
|
-
pgcrypto,
|
|
95
|
-
uuid_ossp,
|
|
96
|
-
citext,
|
|
97
|
-
hstore,
|
|
98
|
-
ltree,
|
|
99
|
-
fuzzystrmatch,
|
|
100
|
-
btree_gin,
|
|
101
|
-
btree_gist,
|
|
102
|
-
cube,
|
|
103
|
-
earthdistance,
|
|
104
|
-
},
|
|
105
|
-
}
|
|
106
|
-
: { extensions: {} }),
|
|
107
|
-
} as any)
|
|
108
|
-
|
|
109
|
-
await db.waitReady
|
|
110
|
-
|
|
111
|
-
if (isMain) {
|
|
112
|
-
await db.exec(`
|
|
113
|
-
SET work_mem = '4MB';
|
|
114
|
-
SET maintenance_work_mem = '16MB';
|
|
115
|
-
SET effective_cache_size = '64MB';
|
|
116
|
-
SET random_page_cost = 1.1;
|
|
117
|
-
SET jit = off;
|
|
118
|
-
`)
|
|
119
|
-
} else {
|
|
120
|
-
await db.exec(`SET jit = off;`)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
port.postMessage({ type: 'ready' })
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
port.on('message', async (msg: { type: string; id: number; [key: string]: unknown }) => {
|
|
127
|
-
const { type, id } = msg
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
switch (type) {
|
|
131
|
-
case 'execProtocolRaw': {
|
|
132
|
-
const input = new Uint8Array(msg.data as ArrayBuffer)
|
|
133
|
-
const result = await db.execProtocolRaw(input, msg.options as any)
|
|
134
|
-
// copy result to a transferable buffer (pglite may reuse wasm memory)
|
|
135
|
-
const buf = new ArrayBuffer(result.byteLength)
|
|
136
|
-
new Uint8Array(buf).set(result)
|
|
137
|
-
port.postMessage({ type: 'result', id, data: buf }, [buf])
|
|
138
|
-
break
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
case 'execProtocolRawBatch': {
|
|
142
|
-
// execute multiple wire protocol messages serially, return concatenated result.
|
|
143
|
-
// eliminates N-1 IPC round-trips for extended protocol pipelines.
|
|
144
|
-
const buffers = msg.buffers as ArrayBuffer[]
|
|
145
|
-
const opts = msg.options as any
|
|
146
|
-
const resultParts: Uint8Array[] = []
|
|
147
|
-
let totalLen = 0
|
|
148
|
-
for (const ab of buffers) {
|
|
149
|
-
const input = new Uint8Array(ab)
|
|
150
|
-
const result = await db.execProtocolRaw(input, opts)
|
|
151
|
-
// copy each result (pglite reuses wasm memory between calls)
|
|
152
|
-
const copy = new Uint8Array(result.byteLength)
|
|
153
|
-
copy.set(result)
|
|
154
|
-
resultParts.push(copy)
|
|
155
|
-
totalLen += copy.byteLength
|
|
156
|
-
}
|
|
157
|
-
// concatenate all results into one transferable buffer
|
|
158
|
-
const combined = new ArrayBuffer(totalLen)
|
|
159
|
-
const view = new Uint8Array(combined)
|
|
160
|
-
let offset = 0
|
|
161
|
-
for (const part of resultParts) {
|
|
162
|
-
view.set(part, offset)
|
|
163
|
-
offset += part.byteLength
|
|
164
|
-
}
|
|
165
|
-
port.postMessage({ type: 'result', id, data: combined }, [combined])
|
|
166
|
-
break
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
case 'query': {
|
|
170
|
-
const result = await db.query(msg.sql as string, msg.params as any[])
|
|
171
|
-
port.postMessage({
|
|
172
|
-
type: 'result',
|
|
173
|
-
id,
|
|
174
|
-
rows: result.rows,
|
|
175
|
-
affectedRows: result.affectedRows,
|
|
176
|
-
})
|
|
177
|
-
break
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
case 'exec': {
|
|
181
|
-
const result = await db.exec(msg.sql as string)
|
|
182
|
-
// serialize exec results (array of { affectedRows })
|
|
183
|
-
const results = result.map((r) => ({ affectedRows: r.affectedRows ?? 0 }))
|
|
184
|
-
port.postMessage({ type: 'result', id, results })
|
|
185
|
-
break
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
case 'listen': {
|
|
189
|
-
const channel = msg.channel as string
|
|
190
|
-
const unsub = await db.listen(channel, (payload) => {
|
|
191
|
-
port.postMessage({ type: 'notification', channel, payload })
|
|
192
|
-
})
|
|
193
|
-
listeners.set(id, unsub)
|
|
194
|
-
port.postMessage({ type: 'result', id })
|
|
195
|
-
break
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
case 'unlisten': {
|
|
199
|
-
const listenId = msg.listenId as number
|
|
200
|
-
const unsub = listeners.get(listenId)
|
|
201
|
-
if (unsub) {
|
|
202
|
-
await unsub()
|
|
203
|
-
listeners.delete(listenId)
|
|
204
|
-
}
|
|
205
|
-
port.postMessage({ type: 'result', id })
|
|
206
|
-
break
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
case 'dumpDataDir': {
|
|
210
|
-
const dump = await db.dumpDataDir('none')
|
|
211
|
-
// convert Blob/File to ArrayBuffer for transfer
|
|
212
|
-
const arrayBuf = await (dump as Blob).arrayBuffer()
|
|
213
|
-
port.postMessage({ type: 'result', id, data: arrayBuf }, [arrayBuf])
|
|
214
|
-
break
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
case 'close': {
|
|
218
|
-
for (const unsub of listeners.values()) {
|
|
219
|
-
await unsub().catch(() => {})
|
|
220
|
-
}
|
|
221
|
-
listeners.clear()
|
|
222
|
-
await db.close()
|
|
223
|
-
port.postMessage({ type: 'result', id })
|
|
224
|
-
break
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
default:
|
|
228
|
-
port.postMessage({
|
|
229
|
-
type: 'error',
|
|
230
|
-
id,
|
|
231
|
-
message: `unknown message type: ${type}`,
|
|
232
|
-
})
|
|
233
|
-
}
|
|
234
|
-
} catch (err: unknown) {
|
|
235
|
-
const error = err as { message?: string; code?: string }
|
|
236
|
-
port.postMessage({
|
|
237
|
-
type: 'error',
|
|
238
|
-
id,
|
|
239
|
-
message: error?.message || String(err),
|
|
240
|
-
code: error?.code,
|
|
241
|
-
})
|
|
242
|
-
}
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
init().catch((err: unknown) => {
|
|
246
|
-
const error = err as { message?: string }
|
|
247
|
-
port.postMessage({
|
|
248
|
-
type: 'error',
|
|
249
|
-
id: 0,
|
|
250
|
-
message: `worker init failed: ${error?.message || String(err)}`,
|
|
251
|
-
})
|
|
252
|
-
process.exit(1)
|
|
253
|
-
})
|
package/src/port.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { createServer } from 'node:net'
|
|
2
|
-
|
|
3
|
-
export function findPort(preferred: number, maxAttempts = 20): Promise<number> {
|
|
4
|
-
return new Promise((resolve, reject) => {
|
|
5
|
-
let attempt = 0
|
|
6
|
-
|
|
7
|
-
function tryPort(port: number) {
|
|
8
|
-
const server = createServer()
|
|
9
|
-
server.unref()
|
|
10
|
-
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
11
|
-
if (err.code === 'EADDRINUSE' && attempt < maxAttempts) {
|
|
12
|
-
attempt++
|
|
13
|
-
tryPort(port + 1)
|
|
14
|
-
} else {
|
|
15
|
-
reject(err)
|
|
16
|
-
}
|
|
17
|
-
})
|
|
18
|
-
server.listen(port, '127.0.0.1', () => {
|
|
19
|
-
server.close(() => resolve(port))
|
|
20
|
-
})
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
tryPort(preferred)
|
|
24
|
-
})
|
|
25
|
-
}
|
package/src/process-title.ts
DELETED