@livestore/sqlite-wasm 0.4.0-dev.1 → 0.4.0-dev.10

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 (80) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/browser/mod.d.ts +1 -0
  3. package/dist/browser/mod.d.ts.map +1 -1
  4. package/dist/browser/mod.js.map +1 -1
  5. package/dist/browser/opfs/AccessHandlePoolVFS.d.ts +17 -0
  6. package/dist/browser/opfs/AccessHandlePoolVFS.d.ts.map +1 -1
  7. package/dist/browser/opfs/AccessHandlePoolVFS.js +72 -1
  8. package/dist/browser/opfs/AccessHandlePoolVFS.js.map +1 -1
  9. package/dist/cf/BlockManager.d.ts +61 -0
  10. package/dist/cf/BlockManager.d.ts.map +1 -0
  11. package/dist/cf/BlockManager.js +157 -0
  12. package/dist/cf/BlockManager.js.map +1 -0
  13. package/dist/cf/CloudflareSqlVFS.d.ts +51 -0
  14. package/dist/cf/CloudflareSqlVFS.d.ts.map +1 -0
  15. package/dist/cf/CloudflareSqlVFS.js +351 -0
  16. package/dist/cf/CloudflareSqlVFS.js.map +1 -0
  17. package/dist/cf/CloudflareWorkerVFS.d.ts +72 -0
  18. package/dist/cf/CloudflareWorkerVFS.d.ts.map +1 -0
  19. package/dist/cf/CloudflareWorkerVFS.js +552 -0
  20. package/dist/cf/CloudflareWorkerVFS.js.map +1 -0
  21. package/dist/cf/mod.d.ts +43 -0
  22. package/dist/cf/mod.d.ts.map +1 -0
  23. package/dist/cf/mod.js +74 -0
  24. package/dist/cf/mod.js.map +1 -0
  25. package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.d.ts +2 -0
  26. package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.d.ts.map +1 -0
  27. package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js +314 -0
  28. package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js.map +1 -0
  29. package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.d.ts +2 -0
  30. package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.d.ts.map +1 -0
  31. package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js +266 -0
  32. package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js.map +1 -0
  33. package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.d.ts +2 -0
  34. package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.d.ts.map +1 -0
  35. package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js +462 -0
  36. package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js.map +1 -0
  37. package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.d.ts +2 -0
  38. package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.d.ts.map +1 -0
  39. package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js +334 -0
  40. package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js.map +1 -0
  41. package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.d.ts +2 -0
  42. package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.d.ts.map +1 -0
  43. package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js +354 -0
  44. package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js.map +1 -0
  45. package/dist/load-wasm/mod.node.d.ts.map +1 -1
  46. package/dist/load-wasm/mod.node.js +1 -2
  47. package/dist/load-wasm/mod.node.js.map +1 -1
  48. package/dist/load-wasm/mod.workerd.d.ts +2 -0
  49. package/dist/load-wasm/mod.workerd.d.ts.map +1 -0
  50. package/dist/load-wasm/mod.workerd.js +26 -0
  51. package/dist/load-wasm/mod.workerd.js.map +1 -0
  52. package/dist/make-sqlite-db.d.ts +1 -0
  53. package/dist/make-sqlite-db.d.ts.map +1 -1
  54. package/dist/make-sqlite-db.js +28 -4
  55. package/dist/make-sqlite-db.js.map +1 -1
  56. package/dist/node/NodeFS.d.ts +1 -2
  57. package/dist/node/NodeFS.d.ts.map +1 -1
  58. package/dist/node/NodeFS.js +1 -6
  59. package/dist/node/NodeFS.js.map +1 -1
  60. package/dist/node/mod.js +3 -8
  61. package/dist/node/mod.js.map +1 -1
  62. package/package.json +21 -8
  63. package/src/browser/mod.ts +1 -0
  64. package/src/browser/opfs/AccessHandlePoolVFS.ts +79 -1
  65. package/src/cf/BlockManager.ts +225 -0
  66. package/src/cf/CloudflareSqlVFS.ts +450 -0
  67. package/src/cf/CloudflareWorkerVFS.ts +664 -0
  68. package/src/cf/README.md +60 -0
  69. package/src/cf/mod.ts +143 -0
  70. package/src/cf/test/README.md +224 -0
  71. package/src/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.ts +389 -0
  72. package/src/cf/test/async-storage/cloudflare-worker-vfs-core.test.ts +322 -0
  73. package/src/cf/test/async-storage/cloudflare-worker-vfs-integration.test.ts +585 -0
  74. package/src/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.ts +403 -0
  75. package/src/cf/test/sql/cloudflare-sql-vfs-core.test.ts +433 -0
  76. package/src/load-wasm/mod.node.ts +1 -2
  77. package/src/load-wasm/mod.workerd.ts +26 -0
  78. package/src/make-sqlite-db.ts +38 -4
  79. package/src/node/NodeFS.ts +1 -9
  80. package/src/node/mod.ts +3 -10
@@ -0,0 +1,403 @@
1
+ /// <reference types="vitest/globals" />
2
+
3
+ import type { CfTypes } from '@livestore/common-cf'
4
+ import * as VFS from '@livestore/wa-sqlite/src/VFS.js'
5
+ import { beforeEach, describe, expect, it } from 'vitest'
6
+ import { CloudflareWorkerVFS } from '../../mod.ts'
7
+
8
+ describe('CloudflareWorkerVFS - Reliability & Error Recovery', () => {
9
+ let vfs: CloudflareWorkerVFS
10
+ let mockStorage: CfTypes.DurableObjectStorage
11
+ let storageData: Map<string, any>
12
+
13
+ beforeEach(async () => {
14
+ storageData = new Map<string, any>()
15
+
16
+ mockStorage = {
17
+ get: (async (_key: string | string[]) => {
18
+ if (Array.isArray(_key)) {
19
+ return new Map()
20
+ }
21
+ return storageData.get(_key)
22
+ }) as CfTypes.DurableObjectStorage['get'],
23
+
24
+ put: async (_key: string | Record<string, any>, _value?: any) => {
25
+ if (typeof _key === 'string') {
26
+ storageData.set(_key, _value)
27
+ } else {
28
+ for (const [k, v] of Object.entries(_key)) {
29
+ storageData.set(k, v)
30
+ }
31
+ }
32
+ },
33
+
34
+ delete: (async (_key: string | string[]) => {
35
+ if (Array.isArray(_key)) {
36
+ let count = 0
37
+ for (const k of _key) {
38
+ if (storageData.delete(k)) count++
39
+ }
40
+ return count
41
+ } else {
42
+ return storageData.delete(_key)
43
+ }
44
+ }) as CfTypes.DurableObjectStorage['delete'],
45
+
46
+ list: async () => new Map(storageData),
47
+ sync: async () => {},
48
+ transactionSync: (fn: () => any) => fn(),
49
+ deleteAll: async () => {
50
+ storageData.clear()
51
+ },
52
+ transaction: async (fn: (txn: any) => Promise<any>) => fn({} as any),
53
+ getCurrentBookmark: async () => '',
54
+ getBookmarkForTime: async (_time: number | Date) => '',
55
+ onNextSessionRestoreBookmark: async (_bookmark: string) => '',
56
+ getAlarm: async () => null,
57
+ setAlarm: async (_timestamp: number | Date) => {},
58
+ deleteAlarm: async () => {},
59
+ sql: {} as any,
60
+ } as CfTypes.DurableObjectStorage
61
+
62
+ vfs = new CloudflareWorkerVFS('test-reliability-vfs', mockStorage, {})
63
+ await vfs.isReady()
64
+ })
65
+
66
+ describe('Error Recovery', () => {
67
+ it('should handle storage failures gracefully during reads', async () => {
68
+ const path = '/test/read-failure-recovery.db'
69
+ const fileId = 1
70
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
71
+ const pOutFlags = new DataView(new ArrayBuffer(4))
72
+
73
+ vfs.jOpen(path, fileId, flags, pOutFlags)
74
+
75
+ // Write data successfully first
76
+ const testData = new TextEncoder().encode('Test data for failure recovery')
77
+ expect(vfs.jWrite(fileId, testData, 0)).toBe(VFS.SQLITE_OK)
78
+
79
+ // Read should work initially (from cache)
80
+ const readData1 = new Uint8Array(testData.length)
81
+ expect(vfs.jRead(fileId, readData1, 0)).toBe(VFS.SQLITE_OK)
82
+ expect(readData1).toEqual(testData)
83
+
84
+ expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
85
+ })
86
+
87
+ it('should handle invalid file operations gracefully', async () => {
88
+ const invalidFileId = 999
89
+ const buffer = new Uint8Array(100)
90
+
91
+ // All operations on invalid file ID should return appropriate error
92
+ expect(vfs.jRead(invalidFileId, buffer, 0)).toBe(VFS.SQLITE_IOERR)
93
+ expect(vfs.jWrite(invalidFileId, buffer, 0)).toBe(VFS.SQLITE_IOERR)
94
+ expect(vfs.jTruncate(invalidFileId, 50)).toBe(VFS.SQLITE_IOERR)
95
+ expect(vfs.jSync(invalidFileId, VFS.SQLITE_SYNC_NORMAL)).toBe(VFS.SQLITE_IOERR)
96
+ expect(vfs.jClose(invalidFileId)).toBe(VFS.SQLITE_OK)
97
+
98
+ const pSize64 = new DataView(new ArrayBuffer(8))
99
+ expect(vfs.jFileSize(invalidFileId, pSize64)).toBe(VFS.SQLITE_IOERR)
100
+ })
101
+
102
+ it('should handle operations on closed files gracefully', async () => {
103
+ const path = '/test/closed-file-ops.db'
104
+ const fileId = 1
105
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
106
+ const pOutFlags = new DataView(new ArrayBuffer(4))
107
+
108
+ // Open and immediately close file
109
+ vfs.jOpen(path, fileId, flags, pOutFlags)
110
+ expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
111
+
112
+ // Operations on closed file should fail gracefully
113
+ const buffer = new Uint8Array(10)
114
+ expect(vfs.jRead(fileId, buffer, 0)).toBe(VFS.SQLITE_IOERR)
115
+ expect(vfs.jWrite(fileId, buffer, 0)).toBe(VFS.SQLITE_IOERR)
116
+ expect(vfs.jTruncate(fileId, 5)).toBe(VFS.SQLITE_IOERR)
117
+ expect(vfs.jSync(fileId, VFS.SQLITE_SYNC_NORMAL)).toBe(VFS.SQLITE_IOERR)
118
+ })
119
+
120
+ it('should handle invalid paths gracefully', async () => {
121
+ const invalidPaths = ['', null as any, undefined as any]
122
+ const fileId = 1
123
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
124
+ const pOutFlags = new DataView(new ArrayBuffer(4))
125
+
126
+ for (const invalidPath of invalidPaths) {
127
+ const result = vfs.jOpen(invalidPath, fileId, flags, pOutFlags)
128
+ expect(result).toBe(VFS.SQLITE_OK)
129
+ }
130
+ })
131
+
132
+ it('should recover from corrupted metadata gracefully', async () => {
133
+ const path = '/test/corrupted-metadata.db'
134
+ const fileId = 1
135
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
136
+ const pOutFlags = new DataView(new ArrayBuffer(4))
137
+
138
+ // Manually insert corrupted metadata
139
+ const metadataKey = `file:${path}:meta`
140
+ storageData.set(metadataKey, { invalid: 'metadata', structure: true })
141
+
142
+ // Opening file should handle corrupted metadata
143
+ expect(vfs.jOpen(path, fileId, flags, pOutFlags)).toBe(VFS.SQLITE_OK)
144
+
145
+ // Should be able to write new data (which will create new metadata)
146
+ const testData = new TextEncoder().encode('Recovery test data')
147
+ expect(vfs.jWrite(fileId, testData, 0)).toBe(VFS.SQLITE_OK)
148
+
149
+ // Should be able to read the data back
150
+ const readData = new Uint8Array(testData.length)
151
+ expect(vfs.jRead(fileId, readData, 0)).toBe(VFS.SQLITE_OK)
152
+ expect(readData).toEqual(testData)
153
+
154
+ expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
155
+ })
156
+ })
157
+
158
+ describe('Concurrent Operations', () => {
159
+ it('should handle multiple files opened simultaneously', async () => {
160
+ const numFiles = 10
161
+ const files: Array<{ path: string; id: number }> = []
162
+
163
+ // Open multiple files
164
+ for (let i = 0; i < numFiles; i++) {
165
+ const path = `/test/concurrent-${i}.db`
166
+ const fileId = i + 1
167
+ files.push({ path, id: fileId })
168
+
169
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
170
+ const pOutFlags = new DataView(new ArrayBuffer(4))
171
+ expect(vfs.jOpen(path, fileId, flags, pOutFlags)).toBe(VFS.SQLITE_OK)
172
+ }
173
+
174
+ // Write different data to each file
175
+ for (let i = 0; i < files.length; i++) {
176
+ const testData = new TextEncoder().encode(`File ${i} data`)
177
+ expect(vfs.jWrite(files[i]?.id ?? 0, testData, 0)).toBe(VFS.SQLITE_OK)
178
+ }
179
+
180
+ // Read back and verify each file has correct data
181
+ for (let i = 0; i < files.length; i++) {
182
+ const expectedData = new TextEncoder().encode(`File ${i} data`)
183
+ const readData = new Uint8Array(expectedData.length)
184
+ expect(vfs.jRead(files[i]?.id ?? 0, readData, 0)).toBe(VFS.SQLITE_OK)
185
+ expect(readData).toEqual(expectedData)
186
+ }
187
+
188
+ // Close all files
189
+ for (const file of files) {
190
+ expect(vfs.jClose(file.id)).toBe(VFS.SQLITE_OK)
191
+ }
192
+ })
193
+
194
+ it('should handle rapid sequential operations on same file', async () => {
195
+ const path = '/test/rapid-sequential.db'
196
+ const fileId = 1
197
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
198
+ const pOutFlags = new DataView(new ArrayBuffer(4))
199
+
200
+ vfs.jOpen(path, fileId, flags, pOutFlags)
201
+
202
+ // Perform rapid sequential write operations
203
+ const numOperations = 50
204
+ for (let i = 0; i < numOperations; i++) {
205
+ const data = new TextEncoder().encode(`Operation ${i}`)
206
+ const offset = i * 20 // Non-overlapping writes
207
+ expect(vfs.jWrite(fileId, data, offset)).toBe(VFS.SQLITE_OK)
208
+ }
209
+
210
+ // Verify all operations succeeded
211
+ for (let i = 0; i < numOperations; i++) {
212
+ const expectedData = new TextEncoder().encode(`Operation ${i}`)
213
+ const readData = new Uint8Array(expectedData.length)
214
+ const offset = i * 20
215
+ expect(vfs.jRead(fileId, readData, offset)).toBe(VFS.SQLITE_OK)
216
+ expect(readData).toEqual(expectedData)
217
+ }
218
+
219
+ expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
220
+ })
221
+
222
+ it('should handle mixed read/write operations', async () => {
223
+ const path = '/test/mixed-operations.db'
224
+ const fileId = 1
225
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
226
+ const pOutFlags = new DataView(new ArrayBuffer(4))
227
+
228
+ vfs.jOpen(path, fileId, flags, pOutFlags)
229
+
230
+ // Initialize with some data
231
+ const initialData = new Uint8Array(1000)
232
+ for (let i = 0; i < initialData.length; i++) {
233
+ initialData[i] = i % 256
234
+ }
235
+ expect(vfs.jWrite(fileId, initialData, 0)).toBe(VFS.SQLITE_OK)
236
+
237
+ // Perform mixed operations
238
+ for (let i = 0; i < 20; i++) {
239
+ if (i % 2 === 0) {
240
+ // Write operation
241
+ const writeData = new Uint8Array(10)
242
+ writeData.fill((i + 100) % 256)
243
+ const offset = (i * 10) % 500
244
+ expect(vfs.jWrite(fileId, writeData, offset)).toBe(VFS.SQLITE_OK)
245
+ } else {
246
+ // Read operation
247
+ const readData = new Uint8Array(10)
248
+ const offset = ((i - 1) * 10) % 500
249
+ expect(vfs.jRead(fileId, readData, offset)).toBe(VFS.SQLITE_OK)
250
+ // Verify read data matches what we wrote in previous iteration
251
+ const expectedValue = (i - 1 + 100) % 256
252
+ expect(readData.every((byte) => byte === expectedValue)).toBe(true)
253
+ }
254
+ }
255
+
256
+ expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
257
+ })
258
+
259
+ it('should handle cache pressure under concurrent access', async () => {
260
+ const path = '/test/cache-pressure.db'
261
+ const fileId = 1
262
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
263
+ const pOutFlags = new DataView(new ArrayBuffer(4))
264
+
265
+ vfs.jOpen(path, fileId, flags, pOutFlags)
266
+
267
+ const chunkSize = 64 * 1024
268
+ const numChunks = 15 // More than typical cache size
269
+
270
+ // Write data to create cache pressure
271
+ for (let i = 0; i < numChunks; i++) {
272
+ const chunkData = new Uint8Array(chunkSize)
273
+ chunkData.fill(i % 256)
274
+ const offset = i * chunkSize
275
+ expect(vfs.jWrite(fileId, chunkData, offset)).toBe(VFS.SQLITE_OK)
276
+ }
277
+
278
+ // Random access pattern to stress cache
279
+ const accessPattern = []
280
+ for (let i = 0; i < 30; i++) {
281
+ accessPattern.push(Math.floor(Math.random() * numChunks))
282
+ }
283
+
284
+ for (const chunkIdx of accessPattern) {
285
+ const readData = new Uint8Array(1000) // Read partial chunk
286
+ const offset = chunkIdx * chunkSize + 1000
287
+ const readResult = vfs.jRead(fileId, readData, offset)
288
+
289
+ if (readResult === VFS.SQLITE_OK) {
290
+ const expectedValue = chunkIdx % 256
291
+ expect(readData.every((byte) => byte === expectedValue)).toBe(true)
292
+ } else {
293
+ // Cache miss is acceptable under cache pressure
294
+ expect(readResult).toBe(VFS.SQLITE_IOERR_SHORT_READ)
295
+ }
296
+ }
297
+
298
+ expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
299
+ })
300
+ })
301
+
302
+ describe('Resource Management', () => {
303
+ it('should handle resource cleanup properly', async () => {
304
+ const paths = ['/test/cleanup1.db', '/test/cleanup2.db', '/test/cleanup3.db']
305
+ const fileIds = [1, 2, 3]
306
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
307
+ const pOutFlags = new DataView(new ArrayBuffer(4))
308
+
309
+ // Open files and write data
310
+ for (let i = 0; i < paths.length; i++) {
311
+ expect(vfs.jOpen(paths[i] ?? '', fileIds[i] ?? 0, flags, pOutFlags)).toBe(VFS.SQLITE_OK)
312
+
313
+ const testData = new TextEncoder().encode(`Cleanup test ${i}`)
314
+ expect(vfs.jWrite(fileIds[i] ?? 0, testData, 0)).toBe(VFS.SQLITE_OK)
315
+ }
316
+
317
+ // Get initial stats
318
+ const statsBefore = vfs.getStats()
319
+ expect(statsBefore.openFiles).toBe(3)
320
+
321
+ // Close all files
322
+ for (const fileId of fileIds) {
323
+ expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
324
+ }
325
+
326
+ // Verify cleanup
327
+ const statsAfter = vfs.getStats()
328
+ expect(statsAfter.openFiles).toBe(0)
329
+ })
330
+
331
+ it('should handle file deletion and cleanup', async () => {
332
+ const path = '/test/delete-cleanup.db'
333
+ const fileId = 1
334
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
335
+ const pOutFlags = new DataView(new ArrayBuffer(4))
336
+
337
+ // Create and write to file
338
+ vfs.jOpen(path, fileId, flags, pOutFlags)
339
+ const testData = new TextEncoder().encode('Data to be deleted')
340
+ expect(vfs.jWrite(fileId, testData, 0)).toBe(VFS.SQLITE_OK)
341
+ expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
342
+
343
+ // Wait for async operations
344
+ await new Promise((resolve) => setTimeout(resolve, 10))
345
+
346
+ // Verify file exists
347
+ const pResOut = new DataView(new ArrayBuffer(4))
348
+ expect(vfs.jAccess(path, VFS.SQLITE_ACCESS_EXISTS, pResOut)).toBe(VFS.SQLITE_OK)
349
+
350
+ // Delete file
351
+ expect(vfs.jDelete(path, 0)).toBe(VFS.SQLITE_OK)
352
+
353
+ // Wait for cleanup
354
+ await new Promise((resolve) => setTimeout(resolve, 10))
355
+
356
+ // Verify file is deleted
357
+ vfs.jAccess(path, VFS.SQLITE_ACCESS_EXISTS, pResOut)
358
+ expect(pResOut.getUint32(0, true)).toBe(0)
359
+ })
360
+
361
+ it('should handle memory pressure gracefully', async () => {
362
+ // Create multiple files with large data to simulate memory pressure
363
+ const numFiles = 5
364
+ const chunkSize = 64 * 1024
365
+ const dataPerFile = chunkSize * 2 // 128KB per file
366
+
367
+ for (let fileIdx = 0; fileIdx < numFiles; fileIdx++) {
368
+ const path = `/test/memory-pressure-${fileIdx}.db`
369
+ const fileId = fileIdx + 1
370
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
371
+ const pOutFlags = new DataView(new ArrayBuffer(4))
372
+
373
+ expect(vfs.jOpen(path, fileId, flags, pOutFlags)).toBe(VFS.SQLITE_OK)
374
+
375
+ // Write large data
376
+ const largeData = new Uint8Array(dataPerFile)
377
+ largeData.fill(fileIdx % 256)
378
+ expect(vfs.jWrite(fileId, largeData, 0)).toBe(VFS.SQLITE_OK)
379
+
380
+ expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
381
+ }
382
+
383
+ // Verify all files are still accessible
384
+ for (let fileIdx = 0; fileIdx < numFiles; fileIdx++) {
385
+ const path = `/test/memory-pressure-${fileIdx}.db`
386
+ const fileId = fileIdx + 1
387
+ const flags = VFS.SQLITE_OPEN_READWRITE
388
+ const pOutFlags = new DataView(new ArrayBuffer(4))
389
+
390
+ expect(vfs.jOpen(path, fileId, flags, pOutFlags)).toBe(VFS.SQLITE_OK)
391
+
392
+ // Read and verify data
393
+ const readData = new Uint8Array(dataPerFile)
394
+ expect(vfs.jRead(fileId, readData, 0)).toBe(VFS.SQLITE_OK)
395
+
396
+ const expectedValue = fileIdx % 256
397
+ expect(readData.every((byte) => byte === expectedValue)).toBe(true)
398
+
399
+ expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
400
+ }
401
+ })
402
+ })
403
+ })