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.
Files changed (157) hide show
  1. package/dist/cf-do/worker.d.ts +3 -0
  2. package/dist/cf-do/worker.d.ts.map +1 -1
  3. package/dist/cf-do/worker.js +37 -15
  4. package/dist/cf-do/worker.js.map +1 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +8 -0
  7. package/dist/index.js.map +1 -1
  8. package/package.json +3 -4
  9. package/src/admin/admin-data.test.ts +0 -348
  10. package/src/admin/http-proxy.ts +0 -252
  11. package/src/admin/log-store.ts +0 -192
  12. package/src/admin/server.ts +0 -471
  13. package/src/admin/ui.ts +0 -1322
  14. package/src/bench/proxy-throughput.bench.ts +0 -343
  15. package/src/bench/serial-mutations.bench.ts +0 -270
  16. package/src/browser.ts +0 -203
  17. package/src/cf-do/.wrangler/cache/cf.json +0 -1
  18. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
  19. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
  20. package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
  21. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0ffaabee41a60e04dd0eb7db3073f0a40139e6a97ccd26823967acb652b89a7b.sqlite +0 -0
  22. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
  23. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
  24. package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
  25. package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-insertion-facade.js +0 -11
  26. package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-loader.entry.ts +0 -134
  27. package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-insertion-facade.js +0 -11
  28. package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-loader.entry.ts +0 -134
  29. package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js +0 -1059
  30. package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js.map +0 -8
  31. package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js +0 -1059
  32. package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js.map +0 -8
  33. package/src/cf-do/ARCHITECTURE.md +0 -93
  34. package/src/cf-do/CHAT_E2E.md +0 -213
  35. package/src/cf-do/watermark.test.ts +0 -103
  36. package/src/cf-do/watermark.ts +0 -118
  37. package/src/cf-do/worker.ts +0 -1041
  38. package/src/cf-do/wrangler.toml +0 -11
  39. package/src/cf-pglite/README.md +0 -19
  40. package/src/change-tracking.ts +0 -25
  41. package/src/child-process.test.ts +0 -147
  42. package/src/child-process.ts +0 -90
  43. package/src/cli-entry.ts +0 -72
  44. package/src/cli.test.ts +0 -40
  45. package/src/cli.ts +0 -1214
  46. package/src/config.ts +0 -150
  47. package/src/do-sql-tracking.test.ts +0 -19
  48. package/src/do-sql-tracking.ts +0 -19
  49. package/src/index.ts +0 -1215
  50. package/src/integration/integration.test.ts +0 -517
  51. package/src/integration/native-binary.guard.test.ts +0 -13
  52. package/src/integration/native-startup.test.ts +0 -44
  53. package/src/integration/replication-latency.test.ts +0 -428
  54. package/src/integration/restore-live-stress.test.ts +0 -433
  55. package/src/integration/restore-reset.test.ts +0 -400
  56. package/src/integration/restore.test.ts +0 -274
  57. package/src/integration/test-permissions.ts +0 -147
  58. package/src/load-config.ts +0 -46
  59. package/src/log.ts +0 -96
  60. package/src/mutex.ts +0 -47
  61. package/src/pg-proxy-browser.singledb.test.ts +0 -233
  62. package/src/pg-proxy-browser.ts +0 -2022
  63. package/src/pg-proxy-do-backend.test.ts +0 -3890
  64. package/src/pg-proxy-do-backend.ts +0 -7191
  65. package/src/pg-proxy.ts +0 -1087
  66. package/src/pg-sqlite-compiler/README.md +0 -53
  67. package/src/pg-sqlite-compiler/catalog/seed.ts +0 -524
  68. package/src/pg-sqlite-compiler/fixtures/pgsqlite/arithmetic.json +0 -307
  69. package/src/pg-sqlite-compiler/fixtures/pgsqlite/array.json +0 -377
  70. package/src/pg-sqlite-compiler/fixtures/pgsqlite/cast.json +0 -12
  71. package/src/pg-sqlite-compiler/fixtures/pgsqlite/catalog.json +0 -447
  72. package/src/pg-sqlite-compiler/fixtures/pgsqlite/create-table.json +0 -32
  73. package/src/pg-sqlite-compiler/fixtures/pgsqlite/datetime.json +0 -397
  74. package/src/pg-sqlite-compiler/fixtures/pgsqlite/enum.json +0 -337
  75. package/src/pg-sqlite-compiler/fixtures/pgsqlite/insert.json +0 -337
  76. package/src/pg-sqlite-compiler/fixtures/pgsqlite/json.json +0 -537
  77. package/src/pg-sqlite-compiler/fixtures/pgsqlite/misc.json +0 -1837
  78. package/src/pg-sqlite-compiler/index.ts +0 -73
  79. package/src/pg-sqlite-compiler/integration.test.ts +0 -136
  80. package/src/pg-sqlite-compiler/passes/ast-utils.ts +0 -113
  81. package/src/pg-sqlite-compiler/passes/catalog.ts +0 -65
  82. package/src/pg-sqlite-compiler/passes/datetime.ts +0 -74
  83. package/src/pg-sqlite-compiler/passes/index.ts +0 -49
  84. package/src/pg-sqlite-compiler/passes/types.ts +0 -156
  85. package/src/pg-sqlite-compiler/smoke.test.ts +0 -69
  86. package/src/pg-sqlite-compiler/test/catalog.test.ts +0 -171
  87. package/src/pg-sqlite-compiler/test/corpus.test.ts +0 -161
  88. package/src/pg-sqlite-compiler/test/datetime.oracle.test.ts +0 -102
  89. package/src/pg-sqlite-compiler/test/oracle.ts +0 -237
  90. package/src/pg-sqlite-compiler/test/types.test.ts +0 -109
  91. package/src/pg-sqlite-compiler/types.ts +0 -63
  92. package/src/pglite-ipc.test.ts +0 -116
  93. package/src/pglite-ipc.ts +0 -266
  94. package/src/pglite-manager.ts +0 -557
  95. package/src/pglite-web-proxy.test.ts +0 -57
  96. package/src/pglite-web-proxy.ts +0 -221
  97. package/src/pglite-web-worker.ts +0 -152
  98. package/src/pglite-worker-thread.ts +0 -253
  99. package/src/port.ts +0 -25
  100. package/src/process-title.ts +0 -9
  101. package/src/recovery.ts +0 -155
  102. package/src/replication/change-tracker.test.ts +0 -357
  103. package/src/replication/change-tracker.ts +0 -279
  104. package/src/replication/handler.test.ts +0 -511
  105. package/src/replication/handler.ts +0 -1190
  106. package/src/replication/pgoutput-encoder.test.ts +0 -697
  107. package/src/replication/pgoutput-encoder.ts +0 -373
  108. package/src/replication/tcp-replication.test.ts +0 -876
  109. package/src/replication/zero-compat.test.ts +0 -1150
  110. package/src/restore-stress.test.ts +0 -188
  111. package/src/s3-local.ts +0 -203
  112. package/src/shim/hooks.mjs +0 -120
  113. package/src/shim/register.mjs +0 -4
  114. package/src/sqlite-mode/apply-mode.ts +0 -224
  115. package/src/sqlite-mode/index.ts +0 -15
  116. package/src/sqlite-mode/native-binary.ts +0 -89
  117. package/src/sqlite-mode/package-resolve.ts +0 -17
  118. package/src/sqlite-mode/resolve-mode.ts +0 -80
  119. package/src/sqlite-mode/shim-template.ts +0 -159
  120. package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
  121. package/src/sqlite-mode/types.ts +0 -30
  122. package/src/vite-plugin.ts +0 -67
  123. package/src/wasm-sqlite.test.ts +0 -537
  124. package/src/worker/browser-admin.ts +0 -52
  125. package/src/worker/browser-build-config.test.ts +0 -71
  126. package/src/worker/browser-build-config.ts +0 -109
  127. package/src/worker/browser-embed-admin.test.ts +0 -75
  128. package/src/worker/browser-embed.ts +0 -345
  129. package/src/worker/cf-patches.ts +0 -384
  130. package/src/worker/embed-integration.test.ts +0 -321
  131. package/src/worker/index.ts +0 -138
  132. package/src/worker/shims/fastify.test.ts +0 -255
  133. package/src/worker/shims/fastify.ts +0 -306
  134. package/src/worker/shims/http-service.test.ts +0 -355
  135. package/src/worker/shims/http-service.ts +0 -293
  136. package/src/worker/shims/node-stub.ts +0 -290
  137. package/src/worker/shims/oxfmt.ts +0 -3
  138. package/src/worker/shims/postgres-browser.ts +0 -59
  139. package/src/worker/shims/postgres-socket.test.ts +0 -576
  140. package/src/worker/shims/postgres-socket.ts +0 -310
  141. package/src/worker/shims/postgres.test.ts +0 -364
  142. package/src/worker/shims/postgres.ts +0 -1454
  143. package/src/worker/shims/sqlite-browser.test.ts +0 -233
  144. package/src/worker/shims/sqlite-browser.ts +0 -175
  145. package/src/worker/shims/sqlite.test.ts +0 -786
  146. package/src/worker/shims/sqlite.ts +0 -978
  147. package/src/worker/shims/stream-browser.ts +0 -15
  148. package/src/worker/shims/ws-browser.test.ts +0 -205
  149. package/src/worker/shims/ws-browser.ts +0 -248
  150. package/src/worker/shims/ws.test.ts +0 -288
  151. package/src/worker/shims/ws.ts +0 -467
  152. package/src/worker/shims/zero-process-env.ts +0 -11
  153. package/src/worker/types.ts +0 -75
  154. package/src/worker/worker-integration.test.ts +0 -223
  155. package/src/worker/worker.test.ts +0 -136
  156. package/src/worker/zero-cache-embed-cf.ts +0 -463
  157. 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
- })