orez 0.2.27 → 0.2.29

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 (150) hide show
  1. package/package.json +3 -4
  2. package/src/admin/admin-data.test.ts +0 -348
  3. package/src/admin/http-proxy.ts +0 -252
  4. package/src/admin/log-store.ts +0 -192
  5. package/src/admin/server.ts +0 -471
  6. package/src/admin/ui.ts +0 -1322
  7. package/src/bench/proxy-throughput.bench.ts +0 -343
  8. package/src/bench/serial-mutations.bench.ts +0 -270
  9. package/src/browser.ts +0 -203
  10. package/src/cf-do/.wrangler/cache/cf.json +0 -1
  11. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
  12. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
  13. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
  14. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0ffaabee41a60e04dd0eb7db3073f0a40139e6a97ccd26823967acb652b89a7b.sqlite +0 -0
  15. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
  16. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
  17. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
  18. package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-insertion-facade.js +0 -11
  19. package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-loader.entry.ts +0 -134
  20. package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-insertion-facade.js +0 -11
  21. package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-loader.entry.ts +0 -134
  22. package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js +0 -1059
  23. package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js.map +0 -8
  24. package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js +0 -1059
  25. package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js.map +0 -8
  26. package/src/cf-do/ARCHITECTURE.md +0 -93
  27. package/src/cf-do/CHAT_E2E.md +0 -213
  28. package/src/cf-do/watermark.test.ts +0 -103
  29. package/src/cf-do/watermark.ts +0 -118
  30. package/src/cf-do/worker.ts +0 -1041
  31. package/src/cf-do/wrangler.toml +0 -11
  32. package/src/cf-pglite/README.md +0 -19
  33. package/src/change-tracking.ts +0 -25
  34. package/src/child-process.test.ts +0 -147
  35. package/src/child-process.ts +0 -90
  36. package/src/cli-entry.ts +0 -72
  37. package/src/cli.test.ts +0 -40
  38. package/src/cli.ts +0 -1214
  39. package/src/config.ts +0 -150
  40. package/src/do-sql-tracking.test.ts +0 -19
  41. package/src/do-sql-tracking.ts +0 -19
  42. package/src/index.ts +0 -1215
  43. package/src/integration/integration.test.ts +0 -517
  44. package/src/integration/native-binary.guard.test.ts +0 -13
  45. package/src/integration/native-startup.test.ts +0 -44
  46. package/src/integration/replication-latency.test.ts +0 -428
  47. package/src/integration/restore-live-stress.test.ts +0 -433
  48. package/src/integration/restore-reset.test.ts +0 -400
  49. package/src/integration/restore.test.ts +0 -274
  50. package/src/integration/test-permissions.ts +0 -147
  51. package/src/load-config.ts +0 -46
  52. package/src/log.ts +0 -96
  53. package/src/mutex.ts +0 -47
  54. package/src/pg-proxy-browser.singledb.test.ts +0 -233
  55. package/src/pg-proxy-browser.ts +0 -2022
  56. package/src/pg-proxy-do-backend.test.ts +0 -3890
  57. package/src/pg-proxy-do-backend.ts +0 -7191
  58. package/src/pg-proxy.ts +0 -1087
  59. package/src/pg-sqlite-compiler/README.md +0 -53
  60. package/src/pg-sqlite-compiler/catalog/seed.ts +0 -524
  61. package/src/pg-sqlite-compiler/fixtures/pgsqlite/arithmetic.json +0 -307
  62. package/src/pg-sqlite-compiler/fixtures/pgsqlite/array.json +0 -377
  63. package/src/pg-sqlite-compiler/fixtures/pgsqlite/cast.json +0 -12
  64. package/src/pg-sqlite-compiler/fixtures/pgsqlite/catalog.json +0 -447
  65. package/src/pg-sqlite-compiler/fixtures/pgsqlite/create-table.json +0 -32
  66. package/src/pg-sqlite-compiler/fixtures/pgsqlite/datetime.json +0 -397
  67. package/src/pg-sqlite-compiler/fixtures/pgsqlite/enum.json +0 -337
  68. package/src/pg-sqlite-compiler/fixtures/pgsqlite/insert.json +0 -337
  69. package/src/pg-sqlite-compiler/fixtures/pgsqlite/json.json +0 -537
  70. package/src/pg-sqlite-compiler/fixtures/pgsqlite/misc.json +0 -1837
  71. package/src/pg-sqlite-compiler/index.ts +0 -73
  72. package/src/pg-sqlite-compiler/integration.test.ts +0 -136
  73. package/src/pg-sqlite-compiler/passes/ast-utils.ts +0 -113
  74. package/src/pg-sqlite-compiler/passes/catalog.ts +0 -65
  75. package/src/pg-sqlite-compiler/passes/datetime.ts +0 -74
  76. package/src/pg-sqlite-compiler/passes/index.ts +0 -49
  77. package/src/pg-sqlite-compiler/passes/types.ts +0 -156
  78. package/src/pg-sqlite-compiler/smoke.test.ts +0 -69
  79. package/src/pg-sqlite-compiler/test/catalog.test.ts +0 -171
  80. package/src/pg-sqlite-compiler/test/corpus.test.ts +0 -161
  81. package/src/pg-sqlite-compiler/test/datetime.oracle.test.ts +0 -102
  82. package/src/pg-sqlite-compiler/test/oracle.ts +0 -237
  83. package/src/pg-sqlite-compiler/test/types.test.ts +0 -109
  84. package/src/pg-sqlite-compiler/types.ts +0 -63
  85. package/src/pglite-ipc.test.ts +0 -116
  86. package/src/pglite-ipc.ts +0 -266
  87. package/src/pglite-manager.ts +0 -557
  88. package/src/pglite-web-proxy.test.ts +0 -57
  89. package/src/pglite-web-proxy.ts +0 -221
  90. package/src/pglite-web-worker.ts +0 -152
  91. package/src/pglite-worker-thread.ts +0 -253
  92. package/src/port.ts +0 -25
  93. package/src/process-title.ts +0 -9
  94. package/src/recovery.ts +0 -155
  95. package/src/replication/change-tracker.test.ts +0 -357
  96. package/src/replication/change-tracker.ts +0 -279
  97. package/src/replication/handler.test.ts +0 -511
  98. package/src/replication/handler.ts +0 -1190
  99. package/src/replication/pgoutput-encoder.test.ts +0 -697
  100. package/src/replication/pgoutput-encoder.ts +0 -373
  101. package/src/replication/tcp-replication.test.ts +0 -876
  102. package/src/replication/zero-compat.test.ts +0 -1150
  103. package/src/restore-stress.test.ts +0 -188
  104. package/src/s3-local.ts +0 -203
  105. package/src/shim/hooks.mjs +0 -120
  106. package/src/shim/register.mjs +0 -4
  107. package/src/sqlite-mode/apply-mode.ts +0 -224
  108. package/src/sqlite-mode/index.ts +0 -15
  109. package/src/sqlite-mode/native-binary.ts +0 -89
  110. package/src/sqlite-mode/package-resolve.ts +0 -17
  111. package/src/sqlite-mode/resolve-mode.ts +0 -80
  112. package/src/sqlite-mode/shim-template.ts +0 -159
  113. package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
  114. package/src/sqlite-mode/types.ts +0 -30
  115. package/src/vite-plugin.ts +0 -67
  116. package/src/wasm-sqlite.test.ts +0 -537
  117. package/src/worker/browser-admin.ts +0 -52
  118. package/src/worker/browser-build-config.test.ts +0 -71
  119. package/src/worker/browser-build-config.ts +0 -109
  120. package/src/worker/browser-embed-admin.test.ts +0 -75
  121. package/src/worker/browser-embed.ts +0 -345
  122. package/src/worker/cf-patches.ts +0 -384
  123. package/src/worker/embed-integration.test.ts +0 -321
  124. package/src/worker/index.ts +0 -138
  125. package/src/worker/shims/fastify.test.ts +0 -255
  126. package/src/worker/shims/fastify.ts +0 -306
  127. package/src/worker/shims/http-service.test.ts +0 -355
  128. package/src/worker/shims/http-service.ts +0 -293
  129. package/src/worker/shims/node-stub.ts +0 -290
  130. package/src/worker/shims/oxfmt.ts +0 -3
  131. package/src/worker/shims/postgres-browser.ts +0 -59
  132. package/src/worker/shims/postgres-socket.test.ts +0 -576
  133. package/src/worker/shims/postgres-socket.ts +0 -310
  134. package/src/worker/shims/postgres.test.ts +0 -364
  135. package/src/worker/shims/postgres.ts +0 -1454
  136. package/src/worker/shims/sqlite-browser.test.ts +0 -233
  137. package/src/worker/shims/sqlite-browser.ts +0 -175
  138. package/src/worker/shims/sqlite.test.ts +0 -786
  139. package/src/worker/shims/sqlite.ts +0 -978
  140. package/src/worker/shims/stream-browser.ts +0 -15
  141. package/src/worker/shims/ws-browser.test.ts +0 -205
  142. package/src/worker/shims/ws-browser.ts +0 -248
  143. package/src/worker/shims/ws.test.ts +0 -288
  144. package/src/worker/shims/ws.ts +0 -467
  145. package/src/worker/shims/zero-process-env.ts +0 -11
  146. package/src/worker/types.ts +0 -75
  147. package/src/worker/worker-integration.test.ts +0 -223
  148. package/src/worker/worker.test.ts +0 -136
  149. package/src/worker/zero-cache-embed-cf.ts +0 -463
  150. package/src/worker/zero-cache-embed.ts +0 -277
@@ -1,517 +0,0 @@
1
- /**
2
- * integration test adapted from zero-cache's integration.pg.test.ts
3
- *
4
- * validates the full sync pipeline: pglite → change tracking → replication
5
- * protocol → zero-cache → websocket poke messages to clients.
6
- *
7
- * uses orez's startZeroLite() instead of real postgres + manual zero-cache.
8
- */
9
-
10
- import { describe, expect, test, beforeAll, afterAll, beforeEach } from 'vitest'
11
- import WebSocket from 'ws'
12
-
13
- import { startZeroLite } from '../index.js'
14
- import { installChangeTracking } from '../replication/change-tracker.js'
15
- import {
16
- ensureTablesInPublications,
17
- hasNonNullPermissions,
18
- installAllowAllPermissions,
19
- } from './test-permissions.js'
20
-
21
- import type { PGlite } from '@electric-sql/pglite'
22
-
23
- const SYNC_PROTOCOL_VERSION = 49
24
- const CLIENT_SCHEMA = {
25
- tables: {
26
- foo: {
27
- columns: {
28
- id: { type: 'string' },
29
- value: { type: 'string' },
30
- num: { type: 'number' },
31
- },
32
- primaryKey: ['id'],
33
- },
34
- },
35
- }
36
-
37
- function encodeSecProtocols(
38
- initConnectionMessage: unknown,
39
- authToken: string | undefined
40
- ): string {
41
- const payload = JSON.stringify({ initConnectionMessage, authToken })
42
- return encodeURIComponent(Buffer.from(payload, 'utf-8').toString('base64'))
43
- }
44
-
45
- // simple async queue for collecting websocket messages
46
- class Queue<T> {
47
- private items: T[] = []
48
- private waiters: Array<{
49
- resolve: (v: T) => void
50
- timer?: ReturnType<typeof setTimeout>
51
- }> = []
52
-
53
- enqueue(item: T) {
54
- const waiter = this.waiters.shift()
55
- if (waiter) {
56
- if (waiter.timer) clearTimeout(waiter.timer)
57
- waiter.resolve(item)
58
- } else {
59
- this.items.push(item)
60
- }
61
- }
62
-
63
- dequeue(fallback?: T, timeoutMs = 10000): Promise<T> {
64
- if (this.items.length > 0) {
65
- return Promise.resolve(this.items.shift()!)
66
- }
67
- return new Promise<T>((resolve) => {
68
- const waiter: { resolve: (v: T) => void; timer?: ReturnType<typeof setTimeout> } = {
69
- resolve,
70
- }
71
- if (fallback !== undefined) {
72
- waiter.timer = setTimeout(() => {
73
- const idx = this.waiters.indexOf(waiter)
74
- if (idx >= 0) this.waiters.splice(idx, 1)
75
- resolve(fallback)
76
- }, timeoutMs)
77
- }
78
- this.waiters.push(waiter)
79
- })
80
- }
81
- }
82
-
83
- describe('orez integration', { timeout: 120000 }, () => {
84
- let db: PGlite
85
- let zeroPort: number
86
- let pgPort: number
87
- let shutdown: () => Promise<void>
88
- let restartZero: (() => Promise<void>) | undefined
89
- let resetZeroFull: (() => Promise<void>) | undefined
90
- let dataDir: string
91
-
92
- beforeAll(async () => {
93
- const testPgPort = 23000 + Math.floor(Math.random() * 1000)
94
- const testZeroPort = testPgPort + 100
95
-
96
- dataDir = `.orez-integration-test-${Date.now()}`
97
- console.log(`[test] starting orez on pg:${testPgPort} zero:${testZeroPort}`)
98
- const result = await startZeroLite({
99
- pgPort: testPgPort,
100
- zeroPort: testZeroPort,
101
- dataDir,
102
- logLevel: 'info',
103
- skipZeroCache: false,
104
- ...(process.env.FORCE_WASM === '1' ? { forceWasmSqlite: true } : {}),
105
- })
106
-
107
- db = result.db
108
- zeroPort = result.zeroPort
109
- pgPort = result.pgPort
110
- shutdown = result.stop
111
- restartZero = result.restartZero
112
- resetZeroFull = result.resetZeroFull
113
-
114
- console.log(`[test] orez started, creating tables`)
115
-
116
- // create test tables
117
- await db.exec(`
118
- CREATE TABLE IF NOT EXISTS foo (
119
- id TEXT PRIMARY KEY,
120
- value TEXT,
121
- num INTEGER
122
- );
123
-
124
- CREATE TABLE IF NOT EXISTS bar (
125
- id TEXT PRIMARY KEY,
126
- foo_id TEXT
127
- );
128
- `)
129
- await ensureTablesInPublications(db, ['foo', 'bar'])
130
- const pubName = process.env.ZERO_APP_PUBLICATIONS?.trim()
131
- if (pubName) {
132
- const quotedPub = '"' + pubName.replace(/"/g, '""') + '"'
133
- await db
134
- .exec(`ALTER PUBLICATION ${quotedPub} ADD TABLE "public"."foo"`)
135
- .catch(() => {})
136
- await db
137
- .exec(`ALTER PUBLICATION ${quotedPub} ADD TABLE "public"."bar"`)
138
- .catch(() => {})
139
- await installChangeTracking(db)
140
- }
141
- await installAllowAllPermissions(db, ['foo', 'bar'])
142
- expect(await hasNonNullPermissions(db)).toBe(true)
143
- if (resetZeroFull) {
144
- await resetZeroFull()
145
- } else if (restartZero) {
146
- await restartZero()
147
- }
148
- const pubNameAfterReset = process.env.ZERO_APP_PUBLICATIONS?.trim()
149
- if (pubNameAfterReset) {
150
- const pubRows = await db.query<{ tablename: string }>(
151
- `SELECT tablename
152
- FROM pg_publication_tables
153
- WHERE pubname = $1
154
- AND schemaname = 'public'`,
155
- [pubNameAfterReset]
156
- )
157
- expect(pubRows.rows.map((r) => r.tablename)).toEqual(
158
- expect.arrayContaining(['foo', 'bar'])
159
- )
160
- const shardCfg = await db.query<{ publications: string[] }>(
161
- `SELECT publications FROM "zero_0"."shardConfig" WHERE lock = true`
162
- )
163
- expect(shardCfg.rows[0]?.publications || []).toContain(pubNameAfterReset)
164
- }
165
- expect(await hasNonNullPermissions(db)).toBe(true)
166
-
167
- console.log(`[test] tables created, waiting for zero-cache`)
168
- // wait for zero-cache to be ready
169
- await waitForZero(zeroPort, 90000)
170
- console.log(`[test] zero-cache ready`)
171
- }, 120000)
172
-
173
- afterAll(async () => {
174
- if (shutdown) await shutdown()
175
- if (dataDir) {
176
- const { rmSync } = await import('node:fs')
177
- try {
178
- rmSync(dataDir, { recursive: true, force: true })
179
- } catch {}
180
- }
181
- })
182
-
183
- beforeEach(async () => {
184
- // clean tables between tests
185
- await db.exec(`DELETE FROM foo; DELETE FROM bar;`)
186
- // wait for replication to consume cleanup changes so zero-cache's replica is clean
187
- await waitForReplicationCatchup(db)
188
- // settle time for zero-cache to finish processing previous client views
189
- await new Promise((r) => setTimeout(r, 2000))
190
- })
191
-
192
- // wait until the replication handler has consumed all pending changes.
193
- // zero-cache queries its own sqlite replica, so data written to pglite
194
- // isn't visible to clients until replication streams it through.
195
- async function waitForReplicationCatchup(
196
- pglite: PGlite,
197
- timeoutMs = 15000
198
- ): Promise<void> {
199
- const deadline = Date.now() + timeoutMs
200
- while (Date.now() < deadline) {
201
- const result = await pglite.query<{ count: string }>(
202
- `SELECT count(*)::text as count FROM _orez._zero_changes`
203
- )
204
- if (Number(result.rows[0]?.count) === 0) return
205
- await new Promise((r) => setTimeout(r, 100))
206
- }
207
- }
208
-
209
- test('zero-cache starts and accepts websocket connections', async () => {
210
- const cg = `test-cg-${Date.now()}`
211
- const cid = `test-client-${Date.now()}`
212
- const secProtocol = encodeSecProtocols(
213
- ['initConnection', { desiredQueriesPatch: [] }],
214
- undefined
215
- )
216
- const ws = new WebSocket(
217
- `ws://localhost:${zeroPort}/sync/v${SYNC_PROTOCOL_VERSION}/connect` +
218
- `?clientGroupID=${cg}&clientID=${cid}&wsid=ws1&schemaVersion=1&baseCookie=&ts=${Date.now()}&lmid=0`,
219
- secProtocol
220
- )
221
-
222
- const connected = new Promise<void>((resolve, reject) => {
223
- ws.on('open', resolve)
224
- ws.on('error', reject)
225
- setTimeout(() => reject(new Error('ws connect timeout')), 5000)
226
- })
227
-
228
- await connected
229
-
230
- const firstMessage = await new Promise<unknown>((resolve) => {
231
- ws.on('message', (data) => {
232
- resolve(JSON.parse(data.toString()))
233
- })
234
- })
235
-
236
- expect(firstMessage).toMatchObject(['connected', { wsid: 'ws1' }])
237
-
238
- ws.close()
239
- })
240
-
241
- test('initial sync delivers existing rows via poke', async () => {
242
- // insert data before connecting
243
- await db.query(`INSERT INTO foo (id, value, num) VALUES ($1, $2, $3)`, [
244
- 'row1',
245
- 'hello',
246
- 42,
247
- ])
248
-
249
- // wait for replication to deliver the row to zero-cache's replica
250
- await waitForReplicationCatchup(db)
251
- // extra settle time for zero-cache to process the replication stream
252
- await new Promise((r) => setTimeout(r, 1000))
253
-
254
- const downstream = new Queue<unknown>()
255
- const ws = connectAndSubscribe(zeroPort, downstream, {
256
- table: 'foo',
257
- orderBy: [['id', 'asc']],
258
- })
259
-
260
- // drain until we get a pokePart with rowsPatch containing our data
261
- const poke = await waitForPokePart(downstream, 30000)
262
- expect(poke.rowsPatch).toEqual(
263
- expect.arrayContaining([
264
- expect.objectContaining({
265
- op: 'put',
266
- tableName: 'foo',
267
- value: expect.objectContaining({
268
- id: 'row1',
269
- value: 'hello',
270
- }),
271
- }),
272
- ])
273
- )
274
-
275
- ws.close()
276
- })
277
-
278
- test('live replication: insert triggers poke', async () => {
279
- const downstream = new Queue<unknown>()
280
- const ws = connectAndSubscribe(zeroPort, downstream, {
281
- table: 'foo',
282
- orderBy: [['id', 'asc']],
283
- })
284
-
285
- // drain initial connection + sync pokes
286
- await drainInitialPokes(downstream)
287
-
288
- // now insert data - this should trigger a replication poke
289
- await db.query(`INSERT INTO foo (id, value, num) VALUES ($1, $2, $3)`, [
290
- 'live-row',
291
- 'live-value',
292
- 99,
293
- ])
294
-
295
- // wait for the replication poke
296
- const poke = await waitForPokePart(downstream, 30000)
297
- expect(poke.rowsPatch).toEqual(
298
- expect.arrayContaining([
299
- expect.objectContaining({
300
- op: 'put',
301
- tableName: 'foo',
302
- value: expect.objectContaining({
303
- id: 'live-row',
304
- value: 'live-value',
305
- }),
306
- }),
307
- ])
308
- )
309
-
310
- ws.close()
311
- })
312
-
313
- test('live replication: update triggers poke', async () => {
314
- // insert initial data
315
- await db.query(`INSERT INTO foo (id, value, num) VALUES ($1, $2, $3)`, [
316
- 'upd-row',
317
- 'original',
318
- 1,
319
- ])
320
-
321
- const downstream = new Queue<unknown>()
322
- const ws = connectAndSubscribe(zeroPort, downstream, {
323
- table: 'foo',
324
- orderBy: [['id', 'asc']],
325
- })
326
-
327
- await drainInitialPokes(downstream)
328
-
329
- // update the row
330
- await db.query(`UPDATE foo SET value = $1, num = $2 WHERE id = $3`, [
331
- 'updated',
332
- 2,
333
- 'upd-row',
334
- ])
335
-
336
- const poke = await waitForPokePart(downstream, 30000)
337
- expect(poke.rowsPatch).toEqual(
338
- expect.arrayContaining([
339
- expect.objectContaining({
340
- op: 'put',
341
- tableName: 'foo',
342
- value: expect.objectContaining({
343
- id: 'upd-row',
344
- value: 'updated',
345
- }),
346
- }),
347
- ])
348
- )
349
-
350
- ws.close()
351
- })
352
-
353
- test('live replication: delete triggers poke', async () => {
354
- await db.query(`INSERT INTO foo (id, value, num) VALUES ($1, $2, $3)`, [
355
- 'del-row',
356
- 'to-delete',
357
- 1,
358
- ])
359
-
360
- const downstream = new Queue<unknown>()
361
- const ws = connectAndSubscribe(zeroPort, downstream, {
362
- table: 'foo',
363
- orderBy: [['id', 'asc']],
364
- })
365
-
366
- await drainInitialPokes(downstream)
367
-
368
- // delete the row
369
- await db.query(`DELETE FROM foo WHERE id = $1`, ['del-row'])
370
-
371
- const poke = await waitForPokePart(downstream, 30000)
372
- expect(poke.rowsPatch).toEqual(
373
- expect.arrayContaining([
374
- expect.objectContaining({
375
- op: 'del',
376
- tableName: 'foo',
377
- }),
378
- ])
379
- )
380
-
381
- ws.close()
382
- })
383
-
384
- test('concurrent inserts all replicate', { timeout: 60000 }, async () => {
385
- const downstream = new Queue<unknown>()
386
- const ws = connectAndSubscribe(zeroPort, downstream, {
387
- table: 'foo',
388
- orderBy: [['id', 'asc']],
389
- })
390
-
391
- await drainInitialPokes(downstream)
392
-
393
- // insert 5 rows concurrently
394
- await Promise.all(
395
- Array.from({ length: 5 }, (_, i) =>
396
- db.query(`INSERT INTO foo (id, value, num) VALUES ($1, $2, $3)`, [
397
- `concurrent-${i}`,
398
- `value-${i}`,
399
- i,
400
- ])
401
- )
402
- )
403
-
404
- // collect all poke parts within a window
405
- const allRows = await collectPokeRows(downstream, 30000)
406
- const ids = allRows
407
- .filter((r: any) => r.op === 'put' && r.tableName === 'foo')
408
- .map((r: any) => r.value.id)
409
- .sort()
410
-
411
- expect(ids).toEqual([
412
- 'concurrent-0',
413
- 'concurrent-1',
414
- 'concurrent-2',
415
- 'concurrent-3',
416
- 'concurrent-4',
417
- ])
418
-
419
- ws.close()
420
- })
421
-
422
- // --- helpers ---
423
-
424
- function connectAndSubscribe(
425
- port: number,
426
- downstream: Queue<unknown>,
427
- query: Record<string, unknown>
428
- ): WebSocket {
429
- const cg = `test-cg-${Date.now()}`
430
- const cid = `test-client-${Date.now()}`
431
- const secProtocol = encodeSecProtocols(
432
- [
433
- 'initConnection',
434
- {
435
- desiredQueriesPatch: [{ op: 'put', hash: 'q1', ast: query }],
436
- clientSchema: CLIENT_SCHEMA,
437
- },
438
- ],
439
- undefined
440
- )
441
- const ws = new WebSocket(
442
- `ws://localhost:${port}/sync/v${SYNC_PROTOCOL_VERSION}/connect` +
443
- `?clientGroupID=${cg}&clientID=${cid}&wsid=ws1&schemaVersion=1&baseCookie=&ts=${Date.now()}&lmid=0`,
444
- secProtocol
445
- )
446
-
447
- ws.on('message', (data) => {
448
- downstream.enqueue(JSON.parse(data.toString()))
449
- })
450
-
451
- return ws
452
- }
453
-
454
- async function drainInitialPokes(downstream: Queue<unknown>) {
455
- // drain messages until we've seen the initial data sync complete
456
- let settled = false
457
- const timeout = Date.now() + 30000
458
-
459
- while (!settled && Date.now() < timeout) {
460
- const msg = (await downstream.dequeue('timeout' as any, 3000)) as any
461
- if (msg === 'timeout') {
462
- settled = true
463
- } else if (Array.isArray(msg) && msg[0] === 'pokeEnd') {
464
- const next = (await downstream.dequeue('timeout' as any, 2000)) as any
465
- if (next === 'timeout') {
466
- settled = true
467
- }
468
- }
469
- }
470
- }
471
-
472
- async function waitForPokePart(
473
- downstream: Queue<unknown>,
474
- timeoutMs = 10000
475
- ): Promise<Record<string, any>> {
476
- const deadline = Date.now() + timeoutMs
477
- while (Date.now() < deadline) {
478
- const remaining = Math.max(1000, deadline - Date.now())
479
- const msg = (await downstream.dequeue('timeout' as any, remaining)) as any
480
- if (msg === 'timeout') throw new Error('timed out waiting for pokePart')
481
- if (Array.isArray(msg) && msg[0] === 'pokePart' && msg[1]?.rowsPatch) {
482
- return msg[1]
483
- }
484
- }
485
- throw new Error('timed out waiting for pokePart')
486
- }
487
-
488
- async function collectPokeRows(
489
- downstream: Queue<unknown>,
490
- windowMs = 5000
491
- ): Promise<any[]> {
492
- const rows: any[] = []
493
- const deadline = Date.now() + windowMs
494
- // collect all poke parts until timeout
495
- while (Date.now() < deadline) {
496
- const remaining = Math.max(1000, deadline - Date.now())
497
- const msg = (await downstream.dequeue('timeout' as any, remaining)) as any
498
- if (msg === 'timeout') break
499
- if (Array.isArray(msg) && msg[0] === 'pokePart' && msg[1]?.rowsPatch) {
500
- rows.push(...msg[1].rowsPatch)
501
- }
502
- }
503
- return rows
504
- }
505
- })
506
-
507
- async function waitForZero(port: number, timeoutMs = 30000) {
508
- const deadline = Date.now() + timeoutMs
509
- while (Date.now() < deadline) {
510
- try {
511
- const res = await fetch(`http://localhost:${port}/`)
512
- if (res.ok || res.status === 404) return
513
- } catch {}
514
- await new Promise((r) => setTimeout(r, 500))
515
- }
516
- throw new Error(`zero-cache not ready on port ${port} after ${timeoutMs}ms`)
517
- }
@@ -1,13 +0,0 @@
1
- import { describe, expect, test } from 'vitest'
2
-
3
- import {
4
- formatNativeBootstrapInstructions,
5
- inspectNativeSqliteBinary,
6
- } from '../sqlite-mode/native-binary.js'
7
-
8
- describe('native sqlite binary guard', () => {
9
- test('better_sqlite3.node is present before native integration tests', () => {
10
- const check = inspectNativeSqliteBinary()
11
- expect(check.found, formatNativeBootstrapInstructions(check)).toBe(true)
12
- })
13
- })
@@ -1,44 +0,0 @@
1
- import { rmSync } from 'node:fs'
2
-
3
- import { afterAll, beforeAll, describe, expect, test } from 'vitest'
4
-
5
- import { startZeroLite } from '../index.js'
6
-
7
- describe('native sqlite startup integration', { timeout: 120_000 }, () => {
8
- let shutdown: (() => Promise<void>) | undefined
9
- let zeroPort = 0
10
- let dataDir = ''
11
-
12
- beforeAll(async () => {
13
- const basePort = 29000 + Math.floor(Math.random() * 1000)
14
- dataDir = `.orez-native-startup-test-${Date.now()}`
15
-
16
- const started = await startZeroLite({
17
- pgPort: basePort,
18
- zeroPort: basePort + 100,
19
- dataDir,
20
- logLevel: 'warn',
21
- skipZeroCache: false,
22
- disableWasmSqlite: true,
23
- })
24
-
25
- shutdown = started.stop
26
- zeroPort = started.zeroPort
27
- }, 60_000)
28
-
29
- afterAll(async () => {
30
- if (shutdown) await shutdown()
31
- if (dataDir) {
32
- try {
33
- rmSync(dataDir, { recursive: true, force: true })
34
- } catch {
35
- // ignore cleanup failures in test teardown
36
- }
37
- }
38
- })
39
-
40
- test('zero-cache responds in native mode', async () => {
41
- const response = await fetch(`http://127.0.0.1:${zeroPort}/`)
42
- expect([200, 404]).toContain(response.status)
43
- })
44
- })