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,786 +0,0 @@
1
- /**
2
- * sqlite shim tests.
3
- *
4
- * uses a mock SqlStorageLike backed by better-sqlite3 to validate that our
5
- * shim correctly bridges between the better-sqlite3 api and DO SqlStorage.
6
- */
7
-
8
- // @ts-expect-error - CJS module
9
- import BedrockSqlite from 'bedrock-sqlite'
10
- const BetterSqlite3 = BedrockSqlite.Database
11
- import { describe, it, expect, beforeEach, afterEach } from 'vitest'
12
-
13
- import {
14
- Database,
15
- Statement,
16
- StatementRunner,
17
- SqliteError,
18
- type SqlStorageLike,
19
- type SqlStorageCursor,
20
- type SqlStorageValue,
21
- } from './sqlite.js'
22
-
23
- // -- mock SqlStorageLike backed by better-sqlite3 --
24
-
25
- function createMockSqlStorage(): SqlStorageLike {
26
- const nativeDb = new BetterSqlite3(':memory:')
27
-
28
- return {
29
- exec(query: string, ...bindings: SqlStorageValue[]): SqlStorageCursor {
30
- const trimmed = query.trim()
31
-
32
- // handle statements that don't return rows
33
- const isWrite =
34
- /^(INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|BEGIN|COMMIT|ROLLBACK|PRAGMA|SAVEPOINT|RELEASE|VACUUM)/i.test(
35
- trimmed
36
- )
37
-
38
- if (isWrite && !trimmed.toUpperCase().startsWith('PRAGMA')) {
39
- const stmt = nativeDb.prepare(query)
40
- const result = stmt.run(...bindings)
41
- return {
42
- toArray: () => [],
43
- get rowsRead() {
44
- return 0
45
- },
46
- get rowsWritten() {
47
- return result.changes
48
- },
49
- get columnNames() {
50
- return []
51
- },
52
- }
53
- }
54
-
55
- // for pragmas — some are SET (contain =), some are GET
56
- if (trimmed.toUpperCase().startsWith('PRAGMA')) {
57
- try {
58
- const stmt = nativeDb.prepare(query)
59
- const rows = stmt.all(...bindings)
60
- return {
61
- toArray: () => rows as Record<string, SqlStorageValue>[],
62
- get rowsRead() {
63
- return rows.length
64
- },
65
- get rowsWritten() {
66
- return 0
67
- },
68
- get columnNames() {
69
- if (rows.length > 0) return Object.keys(rows[0] as object)
70
- return []
71
- },
72
- }
73
- } catch {
74
- // pragma that modifies state (e.g., journal_mode = WAL)
75
- nativeDb.pragma(query.replace(/^PRAGMA\s+/i, ''))
76
- return {
77
- toArray: () => [],
78
- get rowsRead() {
79
- return 0
80
- },
81
- get rowsWritten() {
82
- return 0
83
- },
84
- get columnNames() {
85
- return []
86
- },
87
- }
88
- }
89
- }
90
-
91
- // select / other read queries
92
- const stmt = nativeDb.prepare(query)
93
- const rows = stmt.all(...bindings)
94
- const columns = stmt.columns().map((c: { name: string }) => c.name)
95
- return {
96
- toArray: () => rows as Record<string, SqlStorageValue>[],
97
- get rowsRead() {
98
- return rows.length
99
- },
100
- get rowsWritten() {
101
- return 0
102
- },
103
- get columnNames() {
104
- return columns
105
- },
106
- }
107
- },
108
- // expose native db for cleanup
109
- _nativeDb: nativeDb,
110
- } as SqlStorageLike & { _nativeDb: typeof nativeDb }
111
- }
112
-
113
- // -- tests --
114
-
115
- describe('SqliteError', () => {
116
- it('creates error with message and code', () => {
117
- const err = new SqliteError('table not found', 'SQLITE_ERROR')
118
- expect(err).toBeInstanceOf(Error)
119
- expect(err).toBeInstanceOf(SqliteError)
120
- expect(err.message).toBe('table not found')
121
- expect(err.code).toBe('SQLITE_ERROR')
122
- expect(err.name).toBe('SqliteError')
123
- })
124
-
125
- it('captures stack trace', () => {
126
- const err = new SqliteError('test', 'SQLITE_MISUSE')
127
- expect(err.stack).toBeDefined()
128
- expect(err.stack).toContain('SqliteError')
129
- })
130
- })
131
-
132
- describe('Database', () => {
133
- let mock: SqlStorageLike & { _nativeDb: any }
134
- let db: Database
135
-
136
- beforeEach(() => {
137
- mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
138
- db = new Database(mock)
139
- })
140
-
141
- afterEach(() => {
142
- db.close()
143
- mock._nativeDb.close()
144
- })
145
-
146
- it('constructs with SqlStorageLike', () => {
147
- expect(db.open).toBe(true)
148
- expect(db.name).toBe(':do-storage:')
149
- expect(db.inTransaction).toBe(false)
150
- })
151
-
152
- it('throws when constructed with a string and no globalThis storage', () => {
153
- // clear any global DO storage
154
- const prev = (globalThis as any).__orez_do_sqlite
155
- delete (globalThis as any).__orez_do_sqlite
156
- try {
157
- expect(() => new Database('/path/to/db' as unknown as SqlStorageLike)).toThrow(
158
- 'no DO storage on globalThis.__orez_do_sqlite'
159
- )
160
- } finally {
161
- if (prev) (globalThis as any).__orez_do_sqlite = prev
162
- }
163
- })
164
-
165
- it('close sets open to false', () => {
166
- expect(db.open).toBe(true)
167
- const ret = db.close()
168
- expect(db.open).toBe(false)
169
- expect(ret).toBe(db) // chainable
170
- })
171
-
172
- it('unsafeMode is a no-op that returns this', () => {
173
- expect(db.unsafeMode(true)).toBe(db)
174
- expect(db.unsafeMode(false)).toBe(db)
175
- expect(db.unsafeMode()).toBe(db)
176
- })
177
-
178
- it('defaultSafeIntegers is a no-op that returns this', () => {
179
- expect(db.defaultSafeIntegers(true)).toBe(db)
180
- })
181
- })
182
-
183
- describe('Database.exec', () => {
184
- let mock: SqlStorageLike & { _nativeDb: any }
185
- let db: Database
186
-
187
- beforeEach(() => {
188
- mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
189
- db = new Database(mock)
190
- })
191
-
192
- afterEach(() => {
193
- db.close()
194
- mock._nativeDb.close()
195
- })
196
-
197
- it('executes single statement', () => {
198
- const ret = db.exec('CREATE TABLE t (id INTEGER PRIMARY KEY)')
199
- expect(ret).toBe(db) // chainable
200
- })
201
-
202
- it('executes multiple statements separated by semicolons', () => {
203
- db.exec(
204
- 'CREATE TABLE t (id INTEGER PRIMARY KEY); CREATE TABLE t2 (id INTEGER PRIMARY KEY)'
205
- )
206
- // verify both tables exist
207
- const r1 = db
208
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='t'")
209
- .get()
210
- const r2 = db
211
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='t2'")
212
- .get()
213
- expect(r1).toEqual({ name: 't' })
214
- expect(r2).toEqual({ name: 't2' })
215
- })
216
-
217
- it('throws on closed database', () => {
218
- db.close()
219
- expect(() => db.exec('SELECT 1')).toThrow('not open')
220
- })
221
- })
222
-
223
- describe('Database.prepare / Statement', () => {
224
- let mock: SqlStorageLike & { _nativeDb: any }
225
- let db: Database
226
-
227
- beforeEach(() => {
228
- mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
229
- db = new Database(mock)
230
- db.exec('CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT, val INTEGER)')
231
- })
232
-
233
- afterEach(() => {
234
- db.close()
235
- mock._nativeDb.close()
236
- })
237
-
238
- it('prepare returns a Statement', () => {
239
- const stmt = db.prepare('SELECT 1 as x')
240
- expect(stmt).toBeInstanceOf(Statement)
241
- expect(stmt.source).toBe('SELECT 1 as x')
242
- })
243
-
244
- it('throws prepare on closed db', () => {
245
- db.close()
246
- expect(() => db.prepare('SELECT 1')).toThrow('not open')
247
- })
248
-
249
- it('run executes write and returns RunResult', () => {
250
- const result = db
251
- .prepare('INSERT INTO items (name, val) VALUES (?, ?)')
252
- .run('foo', 42)
253
- expect(result).toHaveProperty('changes')
254
- expect(result).toHaveProperty('lastInsertRowid')
255
- expect(result.changes).toBe(1)
256
- })
257
-
258
- it('get returns first row or undefined', () => {
259
- db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('a', 1)
260
- db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('b', 2)
261
-
262
- const row = db.prepare('SELECT * FROM items WHERE name = ?').get('a')
263
- expect(row).toEqual({ id: 1, name: 'a', val: 1 })
264
-
265
- const missing = db.prepare('SELECT * FROM items WHERE name = ?').get('zzz')
266
- expect(missing).toBeUndefined()
267
- })
268
-
269
- it('all returns all rows', () => {
270
- db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('x', 10)
271
- db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('y', 20)
272
-
273
- const rows = db.prepare('SELECT * FROM items ORDER BY id').all()
274
- expect(rows).toEqual([
275
- { id: 1, name: 'x', val: 10 },
276
- { id: 2, name: 'y', val: 20 },
277
- ])
278
- })
279
-
280
- it('all returns empty array when no rows', () => {
281
- const rows = db.prepare('SELECT * FROM items').all()
282
- expect(rows).toEqual([])
283
- })
284
-
285
- it('iterate yields rows lazily', () => {
286
- db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('a', 1)
287
- db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('b', 2)
288
-
289
- const iter = db.prepare('SELECT * FROM items ORDER BY id').iterate()
290
- const results: unknown[] = []
291
- for (const row of iter) {
292
- results.push(row)
293
- }
294
- expect(results).toEqual([
295
- { id: 1, name: 'a', val: 1 },
296
- { id: 2, name: 'b', val: 2 },
297
- ])
298
- })
299
-
300
- it('safeIntegers is a no-op returning this', () => {
301
- const stmt = db.prepare('SELECT 1 as x')
302
- expect(stmt.safeIntegers(true)).toBe(stmt)
303
- expect(stmt.safeIntegers()).toBe(stmt)
304
- })
305
-
306
- it('statement methods throw on closed db', () => {
307
- const stmt = db.prepare('SELECT 1 as x')
308
- db.close()
309
- expect(() => stmt.run()).toThrow('not open')
310
- expect(() => stmt.get()).toThrow('not open')
311
- expect(() => stmt.all()).toThrow('not open')
312
- })
313
- })
314
-
315
- describe('Database.pragma', () => {
316
- let mock: SqlStorageLike & { _nativeDb: any }
317
- let db: Database
318
-
319
- beforeEach(() => {
320
- mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
321
- db = new Database(mock)
322
- })
323
-
324
- afterEach(() => {
325
- mock._nativeDb.close()
326
- })
327
-
328
- it('reads a pragma and returns array of objects', () => {
329
- const result = db.pragma('journal_mode')
330
- expect(Array.isArray(result)).toBe(true)
331
- expect((result as unknown[]).length).toBeGreaterThan(0)
332
- })
333
-
334
- it('reads pragma with simple option', () => {
335
- const result = db.pragma('journal_mode', { simple: true })
336
- // should return a scalar value, not an array
337
- expect(Array.isArray(result)).toBe(false)
338
- expect(typeof result).toBe('string')
339
- })
340
-
341
- it('sets a pragma', () => {
342
- const result = db.pragma('cache_size = 2000')
343
- // set pragmas return the new value or empty
344
- expect(Array.isArray(result)).toBe(true)
345
- })
346
-
347
- it('skips optimize pragma', () => {
348
- const result = db.pragma('optimize')
349
- expect(result).toEqual([])
350
-
351
- const simple = db.pragma('optimize', { simple: true })
352
- expect(simple).toBeUndefined()
353
- })
354
-
355
- it('throws on closed db', () => {
356
- db.close()
357
- expect(() => db.pragma('journal_mode')).toThrow('not open')
358
- })
359
- })
360
-
361
- describe('Database.transaction', () => {
362
- let mock: SqlStorageLike & { _nativeDb: any }
363
- let db: Database
364
-
365
- beforeEach(() => {
366
- mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
367
- db = new Database(mock)
368
- db.exec('CREATE TABLE t (id INTEGER PRIMARY KEY)')
369
- })
370
-
371
- afterEach(() => {
372
- mock._nativeDb.close()
373
- })
374
-
375
- it('commits on success', () => {
376
- const txn = db.transaction(() => {
377
- db.prepare('INSERT INTO t VALUES (1)').run()
378
- db.prepare('INSERT INTO t VALUES (2)').run()
379
- })
380
-
381
- txn()
382
-
383
- const rows = db.prepare('SELECT * FROM t').all()
384
- expect(rows).toEqual([{ id: 1 }, { id: 2 }])
385
- })
386
-
387
- it('rolls back on error', () => {
388
- const txn = db.transaction(() => {
389
- db.prepare('INSERT INTO t VALUES (1)').run()
390
- throw new Error('boom')
391
- })
392
-
393
- expect(() => txn()).toThrow('boom')
394
-
395
- const rows = db.prepare('SELECT * FROM t').all()
396
- expect(rows).toEqual([])
397
- })
398
-
399
- it('inTransaction reflects state', () => {
400
- expect(db.inTransaction).toBe(false)
401
-
402
- db.transaction(() => {
403
- expect(db.inTransaction).toBe(true)
404
- })()
405
-
406
- expect(db.inTransaction).toBe(false)
407
- })
408
-
409
- it('inTransaction is false after rollback', () => {
410
- try {
411
- db.transaction(() => {
412
- throw new Error('fail')
413
- })()
414
- } catch {}
415
-
416
- expect(db.inTransaction).toBe(false)
417
- })
418
-
419
- it('has deferred/immediate/exclusive variants', () => {
420
- const txn = db.transaction(() => {
421
- db.prepare('INSERT INTO t VALUES (99)').run()
422
- })
423
-
424
- expect(typeof txn.deferred).toBe('function')
425
- expect(typeof txn.immediate).toBe('function')
426
- expect(typeof txn.exclusive).toBe('function')
427
- })
428
-
429
- it('immediate variant works', () => {
430
- const txn = db.transaction(() => {
431
- db.prepare('INSERT INTO t VALUES (42)').run()
432
- })
433
-
434
- txn.immediate()
435
-
436
- const rows = db.prepare('SELECT * FROM t').all()
437
- expect(rows).toEqual([{ id: 42 }])
438
- })
439
-
440
- it('nested transaction calls run fn without extra BEGIN', () => {
441
- const inner = db.transaction(() => {
442
- db.prepare('INSERT INTO t VALUES (2)').run()
443
- })
444
-
445
- const outer = db.transaction(() => {
446
- db.prepare('INSERT INTO t VALUES (1)').run()
447
- inner() // should NOT issue another BEGIN
448
- db.prepare('INSERT INTO t VALUES (3)').run()
449
- })
450
-
451
- outer()
452
-
453
- const rows = db.prepare('SELECT * FROM t ORDER BY id').all()
454
- expect(rows).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }])
455
- })
456
-
457
- it('passes arguments through to wrapped function', () => {
458
- const txn = db.transaction((id: unknown, val: unknown) => {
459
- db.prepare('INSERT INTO t VALUES (?)').run(id)
460
- return val
461
- })
462
-
463
- const result = txn(7, 'hello')
464
- expect(result).toBe('hello')
465
- expect(db.prepare('SELECT * FROM t').all()).toEqual([{ id: 7 }])
466
- })
467
- })
468
-
469
- describe('StatementRunner', () => {
470
- let mock: SqlStorageLike & { _nativeDb: any }
471
- let db: Database
472
- let runner: StatementRunner
473
-
474
- beforeEach(() => {
475
- mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
476
- db = new Database(mock)
477
- db.exec('CREATE TABLE foo (id INTEGER PRIMARY KEY, name TEXT)')
478
- runner = new StatementRunner(db)
479
- })
480
-
481
- afterEach(() => {
482
- mock._nativeDb.close()
483
- })
484
-
485
- it('run inserts data', () => {
486
- const result = runner.run('INSERT INTO foo (name) VALUES (?)', 'bar')
487
- expect(result).toHaveProperty('changes')
488
- expect(result.changes).toBe(1)
489
- })
490
-
491
- it('get returns single row', () => {
492
- runner.run('INSERT INTO foo (name) VALUES (?)', 'baz')
493
- const row = runner.get('SELECT * FROM foo WHERE name = ?', 'baz')
494
- expect(row).toEqual({ id: 1, name: 'baz' })
495
- })
496
-
497
- it('get returns undefined for no match', () => {
498
- const row = runner.get('SELECT * FROM foo WHERE name = ?', 'nope')
499
- expect(row).toBeUndefined()
500
- })
501
-
502
- it('all returns all rows', () => {
503
- runner.run('INSERT INTO foo (name) VALUES (?)', 'a')
504
- runner.run('INSERT INTO foo (name) VALUES (?)', 'b')
505
- const rows = runner.all('SELECT * FROM foo ORDER BY id')
506
- expect(rows).toEqual([
507
- { id: 1, name: 'a' },
508
- { id: 2, name: 'b' },
509
- ])
510
- })
511
-
512
- it('begin/commit transaction', () => {
513
- runner.begin()
514
- runner.run('INSERT INTO foo (name) VALUES (?)', 'x')
515
- runner.run('INSERT INTO foo (name) VALUES (?)', 'y')
516
- runner.commit()
517
-
518
- expect(runner.all('SELECT * FROM foo ORDER BY id')).toEqual([
519
- { id: 1, name: 'x' },
520
- { id: 2, name: 'y' },
521
- ])
522
- })
523
-
524
- it('begin/rollback discards changes', () => {
525
- runner.begin()
526
- runner.run('INSERT INTO foo (name) VALUES (?)', 'x')
527
- runner.rollback()
528
-
529
- expect(runner.all('SELECT * FROM foo')).toEqual([])
530
- })
531
-
532
- it('beginConcurrent maps to regular BEGIN', () => {
533
- runner.beginConcurrent()
534
- runner.run('INSERT INTO foo (name) VALUES (?)', 'concurrent')
535
- runner.commit()
536
-
537
- expect(runner.all('SELECT * FROM foo')).toEqual([{ id: 1, name: 'concurrent' }])
538
- })
539
-
540
- it('beginImmediate works', () => {
541
- runner.beginImmediate()
542
- runner.run('INSERT INTO foo (name) VALUES (?)', 'immediate')
543
- runner.commit()
544
-
545
- expect(runner.all('SELECT * FROM foo')).toEqual([{ id: 1, name: 'immediate' }])
546
- })
547
-
548
- it('caches prepared statements', () => {
549
- // run same SQL multiple times — statements should be reused
550
- runner.run('INSERT INTO foo (name) VALUES (?)', 'a')
551
- runner.run('INSERT INTO foo (name) VALUES (?)', 'b')
552
- runner.run('INSERT INTO foo (name) VALUES (?)', 'c')
553
-
554
- expect(runner.all('SELECT count(*) as c FROM foo')).toEqual([{ c: 3 }])
555
- })
556
- })
557
-
558
- describe('DO snapshot transactions', () => {
559
- let mock: SqlStorageLike & { _nativeDb: any; transactionSync: <T>(fn: () => T) => T }
560
- let live: Database
561
- let snapshot: Database
562
-
563
- beforeEach(() => {
564
- mock = createMockSqlStorage() as SqlStorageLike & {
565
- _nativeDb: any
566
- transactionSync: <T>(fn: () => T) => T
567
- }
568
- mock.transactionSync = (fn) => fn()
569
- live = new Database(mock)
570
- snapshot = new Database(mock)
571
- live.exec('CREATE TABLE todo (id TEXT PRIMARY KEY, title TEXT, _0_version TEXT)')
572
- live.prepare('INSERT INTO todo VALUES (?, ?, ?)').run('1', 'old', '01')
573
- })
574
-
575
- afterEach(() => {
576
- live.close()
577
- snapshot.close()
578
- mock._nativeDb.close()
579
- })
580
-
581
- it('keeps BEGIN CONCURRENT reads stable until rollback', () => {
582
- snapshot.prepare('BEGIN CONCURRENT').run()
583
- expect(snapshot.prepare('SELECT title FROM todo WHERE id = ?').get('1')).toEqual({
584
- title: 'old',
585
- })
586
-
587
- live
588
- .prepare('UPDATE todo SET title = ?, _0_version = ? WHERE id = ?')
589
- .run('new', '02', '1')
590
-
591
- expect(live.prepare('SELECT title FROM todo WHERE id = ?').get('1')).toEqual({
592
- title: 'new',
593
- })
594
- expect(snapshot.prepare('SELECT title FROM todo WHERE id = ?').get('1')).toEqual({
595
- title: 'old',
596
- })
597
-
598
- snapshot.prepare('ROLLBACK').run()
599
- expect(snapshot.prepare('SELECT title FROM todo WHERE id = ?').get('1')).toEqual({
600
- title: 'new',
601
- })
602
- })
603
-
604
- it('hides snapshot tables from sqlite catalog queries', () => {
605
- snapshot.prepare('BEGIN CONCURRENT').run()
606
-
607
- expect(
608
- live
609
- .prepare("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name")
610
- .all()
611
- ).toEqual([{ name: 'todo' }])
612
- })
613
-
614
- it('does not rewrite table names inside string literals during snapshots', () => {
615
- live.exec('CREATE TABLE log (id TEXT PRIMARY KEY, table_name TEXT, _0_version TEXT)')
616
- live.prepare('INSERT INTO log VALUES (?, ?, ?)').run('1', 'todo', '01')
617
- live.prepare('INSERT INTO log VALUES (?, ?, ?)').run('2', '"todo"', '02')
618
-
619
- snapshot.prepare('BEGIN CONCURRENT').run()
620
-
621
- expect(
622
- snapshot.prepare("SELECT table_name FROM log WHERE table_name = 'todo'").get()
623
- ).toEqual({ table_name: 'todo' })
624
- expect(
625
- snapshot.prepare('SELECT table_name FROM log WHERE table_name = ?').get('"todo"')
626
- ).toEqual({ table_name: '"todo"' })
627
- })
628
-
629
- it('cleans inactive snapshot tables when opening a database', () => {
630
- mock.exec('CREATE TABLE _orez_snapshot_1_todo AS SELECT * FROM todo')
631
- expect(
632
- mock
633
- .exec("SELECT name FROM sqlite_master WHERE name = '_orez_snapshot_1_todo'")
634
- .toArray()
635
- ).toEqual([{ name: '_orez_snapshot_1_todo' }])
636
-
637
- const reopened = new Database(mock)
638
- try {
639
- expect(
640
- mock
641
- .exec("SELECT name FROM sqlite_master WHERE name = '_orez_snapshot_1_todo'")
642
- .toArray()
643
- ).toEqual([])
644
- } finally {
645
- reopened.close()
646
- }
647
- })
648
-
649
- it('does not remove another open connection snapshot', () => {
650
- snapshot.prepare('BEGIN CONCURRENT').run()
651
- const snapshotTables = mock
652
- .exec("SELECT name FROM sqlite_master WHERE name LIKE '_orez_snapshot_%'")
653
- .toArray()
654
- expect(snapshotTables).toHaveLength(1)
655
-
656
- const other = new Database(mock)
657
- try {
658
- live
659
- .prepare('UPDATE todo SET title = ?, _0_version = ? WHERE id = ?')
660
- .run('new', '02', '1')
661
-
662
- expect(snapshot.prepare('SELECT title FROM todo WHERE id = ?').get('1')).toEqual({
663
- title: 'old',
664
- })
665
- } finally {
666
- other.close()
667
- }
668
- })
669
-
670
- it('persists BEGIN CONCURRENT writes for the zero-cache replica writer', () => {
671
- const globalObject = globalThis as any
672
- const previousRole = globalObject.__orez_zero_sqlite_role
673
- globalObject.__orez_zero_sqlite_role = 'replica-writer'
674
- let writer: Database
675
- try {
676
- writer = new Database(mock)
677
- } finally {
678
- if (previousRole === undefined) {
679
- delete globalObject.__orez_zero_sqlite_role
680
- } else {
681
- globalObject.__orez_zero_sqlite_role = previousRole
682
- }
683
- }
684
-
685
- writer.prepare('BEGIN CONCURRENT').run()
686
- writer.prepare('INSERT INTO todo VALUES (?, ?, ?)').run('2', 'writer row', '02')
687
- writer.prepare('COMMIT').run()
688
-
689
- expect(live.prepare('SELECT title FROM todo WHERE id = ?').get('2')).toEqual({
690
- title: 'writer row',
691
- })
692
- expect(
693
- live
694
- .prepare("SELECT name FROM sqlite_master WHERE name LIKE '_orez_snapshot_%'")
695
- .all()
696
- ).toEqual([])
697
- })
698
- })
699
-
700
- describe('StatementRunner: zero-cache replicator pattern', () => {
701
- let mock: SqlStorageLike & { _nativeDb: any }
702
- let db: Database
703
- let runner: StatementRunner
704
-
705
- beforeEach(() => {
706
- mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
707
- db = new Database(mock)
708
- db.exec(`
709
- CREATE TABLE issues (
710
- issueID INTEGER PRIMARY KEY,
711
- title TEXT,
712
- _0_version TEXT
713
- )
714
- `)
715
- runner = new StatementRunner(db)
716
- })
717
-
718
- afterEach(() => {
719
- mock._nativeDb.close()
720
- })
721
-
722
- it('simulates zero-cache batch processing', () => {
723
- // batch 1
724
- runner.begin()
725
- runner.run(
726
- 'INSERT INTO issues (issueID, title, _0_version) VALUES (?, ?, ?)',
727
- 1,
728
- 'bug',
729
- '01'
730
- )
731
- runner.run(
732
- 'INSERT INTO issues (issueID, title, _0_version) VALUES (?, ?, ?)',
733
- 2,
734
- 'feat',
735
- '01'
736
- )
737
- runner.commit()
738
-
739
- expect(runner.all('SELECT * FROM issues ORDER BY issueID')).toEqual([
740
- { issueID: 1, title: 'bug', _0_version: '01' },
741
- { issueID: 2, title: 'feat', _0_version: '01' },
742
- ])
743
-
744
- // batch 2
745
- runner.begin()
746
- runner.run(
747
- 'INSERT OR REPLACE INTO issues (issueID, title, _0_version) VALUES (?, ?, ?)',
748
- 1,
749
- 'bug fix',
750
- '02'
751
- )
752
- runner.commit()
753
-
754
- expect(runner.get('SELECT title FROM issues WHERE issueID = ?', 1)).toEqual({
755
- title: 'bug fix',
756
- })
757
- })
758
-
759
- it('simulates rollback on conflict', () => {
760
- runner.begin()
761
- runner.run(
762
- 'INSERT INTO issues (issueID, title, _0_version) VALUES (?, ?, ?)',
763
- 1,
764
- 'ok',
765
- '01'
766
- )
767
- runner.commit()
768
-
769
- runner.begin()
770
- try {
771
- runner.run(
772
- 'INSERT INTO issues (issueID, title, _0_version) VALUES (?, ?, ?)',
773
- 1,
774
- 'dupe',
775
- '02'
776
- )
777
- } catch {
778
- runner.rollback()
779
- }
780
-
781
- // original row should still be there
782
- expect(runner.get('SELECT title FROM issues WHERE issueID = ?', 1)).toEqual({
783
- title: 'ok',
784
- })
785
- })
786
- })