@livestore/sqlite-wasm 0.4.0-dev.3 → 0.4.0-dev.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser/mod.d.ts +1 -0
- package/dist/browser/mod.d.ts.map +1 -1
- package/dist/browser/mod.js.map +1 -1
- package/dist/cf/BlockManager.d.ts +61 -0
- package/dist/cf/BlockManager.d.ts.map +1 -0
- package/dist/cf/BlockManager.js +157 -0
- package/dist/cf/BlockManager.js.map +1 -0
- package/dist/cf/CloudflareSqlVFS.d.ts +51 -0
- package/dist/cf/CloudflareSqlVFS.d.ts.map +1 -0
- package/dist/cf/CloudflareSqlVFS.js +351 -0
- package/dist/cf/CloudflareSqlVFS.js.map +1 -0
- package/dist/cf/CloudflareWorkerVFS.d.ts +72 -0
- package/dist/cf/CloudflareWorkerVFS.d.ts.map +1 -0
- package/dist/cf/CloudflareWorkerVFS.js +552 -0
- package/dist/cf/CloudflareWorkerVFS.js.map +1 -0
- package/dist/cf/mod.d.ts +43 -0
- package/dist/cf/mod.d.ts.map +1 -0
- package/dist/cf/mod.js +74 -0
- package/dist/cf/mod.js.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.d.ts +2 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.d.ts.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js +314 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.d.ts +2 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.d.ts.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js +266 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.d.ts +2 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.d.ts.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js +444 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.d.ts +2 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.d.ts.map +1 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js +334 -0
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js.map +1 -0
- package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.d.ts +2 -0
- package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.d.ts.map +1 -0
- package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js +354 -0
- package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js.map +1 -0
- package/dist/load-wasm/mod.node.d.ts.map +1 -1
- package/dist/load-wasm/mod.node.js +1 -2
- package/dist/load-wasm/mod.node.js.map +1 -1
- package/dist/load-wasm/mod.workerd.d.ts +2 -0
- package/dist/load-wasm/mod.workerd.d.ts.map +1 -0
- package/dist/load-wasm/mod.workerd.js +26 -0
- package/dist/load-wasm/mod.workerd.js.map +1 -0
- package/dist/make-sqlite-db.d.ts +1 -0
- package/dist/make-sqlite-db.d.ts.map +1 -1
- package/dist/make-sqlite-db.js.map +1 -1
- package/dist/node/NodeFS.d.ts +1 -2
- package/dist/node/NodeFS.d.ts.map +1 -1
- package/dist/node/NodeFS.js +1 -6
- package/dist/node/NodeFS.js.map +1 -1
- package/dist/node/mod.js +3 -8
- package/dist/node/mod.js.map +1 -1
- package/package.json +20 -7
- package/src/browser/mod.ts +1 -0
- package/src/cf/BlockManager.ts +225 -0
- package/src/cf/CloudflareSqlVFS.ts +450 -0
- package/src/cf/CloudflareWorkerVFS.ts +664 -0
- package/src/cf/README.md +60 -0
- package/src/cf/mod.ts +143 -0
- package/src/cf/test/README.md +224 -0
- package/src/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.ts +389 -0
- package/src/cf/test/async-storage/cloudflare-worker-vfs-core.test.ts +322 -0
- package/src/cf/test/async-storage/cloudflare-worker-vfs-integration.test.ts +567 -0
- package/src/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.ts +403 -0
- package/src/cf/test/sql/cloudflare-sql-vfs-core.test.ts +433 -0
- package/src/load-wasm/mod.node.ts +1 -2
- package/src/load-wasm/mod.workerd.ts +26 -0
- package/src/make-sqlite-db.ts +1 -0
- package/src/node/NodeFS.ts +1 -9
- package/src/node/mod.ts +3 -10
|
@@ -0,0 +1,389 @@
|
|
|
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 - Advanced Features', () => {
|
|
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-advanced-vfs', mockStorage, {})
|
|
63
|
+
await vfs.isReady()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
describe('Large File Chunking', () => {
|
|
67
|
+
it('should handle large files with proper chunking', async () => {
|
|
68
|
+
const path = '/test/large-file.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 5 chunks worth of data
|
|
76
|
+
const chunkSize = 64 * 1024
|
|
77
|
+
const numChunks = 5
|
|
78
|
+
const totalSize = chunkSize * numChunks
|
|
79
|
+
const largeData = new Uint8Array(totalSize)
|
|
80
|
+
|
|
81
|
+
// Fill with pattern for verification
|
|
82
|
+
for (let i = 0; i < totalSize; i++) {
|
|
83
|
+
largeData[i] = (i * 7) % 256
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
expect(vfs.jWrite(fileId, largeData, 0)).toBe(VFS.SQLITE_OK)
|
|
87
|
+
|
|
88
|
+
// Verify file size
|
|
89
|
+
const pSize64 = new DataView(new ArrayBuffer(8))
|
|
90
|
+
expect(vfs.jFileSize(fileId, pSize64)).toBe(VFS.SQLITE_OK)
|
|
91
|
+
expect(pSize64.getBigInt64(0, true)).toBe(BigInt(totalSize))
|
|
92
|
+
|
|
93
|
+
// Read back in chunks to verify chunking works correctly
|
|
94
|
+
for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
|
|
95
|
+
const chunkData = new Uint8Array(chunkSize)
|
|
96
|
+
const offset = chunkIdx * chunkSize
|
|
97
|
+
|
|
98
|
+
expect(vfs.jRead(fileId, chunkData, offset)).toBe(VFS.SQLITE_OK)
|
|
99
|
+
expect(chunkData).toEqual(largeData.slice(offset, offset + chunkSize))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should handle cross-chunk boundary operations', async () => {
|
|
106
|
+
const path = '/test/cross-chunk.db'
|
|
107
|
+
const fileId = 1
|
|
108
|
+
const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
|
|
109
|
+
const pOutFlags = new DataView(new ArrayBuffer(4))
|
|
110
|
+
|
|
111
|
+
vfs.jOpen(path, fileId, flags, pOutFlags)
|
|
112
|
+
|
|
113
|
+
const chunkSize = 64 * 1024
|
|
114
|
+
|
|
115
|
+
// Write data spanning chunk boundaries
|
|
116
|
+
const spanData = new Uint8Array(chunkSize + 1000)
|
|
117
|
+
spanData.fill(0xaa)
|
|
118
|
+
const spanOffset = chunkSize - 500
|
|
119
|
+
|
|
120
|
+
expect(vfs.jWrite(fileId, spanData, spanOffset)).toBe(VFS.SQLITE_OK)
|
|
121
|
+
|
|
122
|
+
// Read back cross-boundary data
|
|
123
|
+
const readData = new Uint8Array(spanData.length)
|
|
124
|
+
expect(vfs.jRead(fileId, readData, spanOffset)).toBe(VFS.SQLITE_OK)
|
|
125
|
+
expect(readData).toEqual(spanData)
|
|
126
|
+
|
|
127
|
+
expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe('Cache Management', () => {
|
|
132
|
+
it('should handle cache operations correctly', async () => {
|
|
133
|
+
const path = '/test/cache-test.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
|
+
vfs.jOpen(path, fileId, flags, pOutFlags)
|
|
139
|
+
|
|
140
|
+
// Write data to populate cache
|
|
141
|
+
const testData = new TextEncoder().encode('Cache test data')
|
|
142
|
+
expect(vfs.jWrite(fileId, testData, 0)).toBe(VFS.SQLITE_OK)
|
|
143
|
+
|
|
144
|
+
// Multiple reads should use cache
|
|
145
|
+
for (let i = 0; i < 5; i++) {
|
|
146
|
+
const readData = new Uint8Array(testData.length)
|
|
147
|
+
expect(vfs.jRead(fileId, readData, 0)).toBe(VFS.SQLITE_OK)
|
|
148
|
+
expect(readData).toEqual(testData)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check cache statistics
|
|
152
|
+
const stats = vfs.getStats()
|
|
153
|
+
expect(stats.cachedChunks).toBeGreaterThan(0)
|
|
154
|
+
expect(stats.chunkSize).toBe(64 * 1024)
|
|
155
|
+
|
|
156
|
+
expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should handle large files that exceed cache capacity', async () => {
|
|
160
|
+
const path = '/test/cache-overflow.db'
|
|
161
|
+
const fileId = 1
|
|
162
|
+
const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
|
|
163
|
+
const pOutFlags = new DataView(new ArrayBuffer(4))
|
|
164
|
+
|
|
165
|
+
vfs.jOpen(path, fileId, flags, pOutFlags)
|
|
166
|
+
|
|
167
|
+
// Write many chunks to potentially exceed cache
|
|
168
|
+
const chunkSize = 64 * 1024
|
|
169
|
+
const numChunks = 20 // Likely to exceed typical cache size
|
|
170
|
+
|
|
171
|
+
for (let i = 0; i < numChunks; i++) {
|
|
172
|
+
const chunkData = new Uint8Array(chunkSize)
|
|
173
|
+
chunkData.fill(i % 256)
|
|
174
|
+
const offset = i * chunkSize
|
|
175
|
+
|
|
176
|
+
expect(vfs.jWrite(fileId, chunkData, offset)).toBe(VFS.SQLITE_OK)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Verify all chunks can still be read correctly
|
|
180
|
+
for (let i = 0; i < numChunks; i++) {
|
|
181
|
+
const readData = new Uint8Array(chunkSize)
|
|
182
|
+
const offset = i * chunkSize
|
|
183
|
+
|
|
184
|
+
const readResult = vfs.jRead(fileId, readData, offset)
|
|
185
|
+
|
|
186
|
+
if (readResult === VFS.SQLITE_OK) {
|
|
187
|
+
const expectedData = new Uint8Array(chunkSize)
|
|
188
|
+
expectedData.fill(i % 256)
|
|
189
|
+
expect(readData).toEqual(expectedData)
|
|
190
|
+
} else {
|
|
191
|
+
// Cache miss is acceptable for this test - we're testing cache pressure
|
|
192
|
+
expect(readResult).toBe(VFS.SQLITE_IOERR_SHORT_READ)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
describe('SQLite File Types', () => {
|
|
201
|
+
it('should handle main database files', async () => {
|
|
202
|
+
const path = '/test/main.db'
|
|
203
|
+
const fileId = 1
|
|
204
|
+
const flags = VFS.SQLITE_OPEN_MAIN_DB | VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
|
|
205
|
+
const pOutFlags = new DataView(new ArrayBuffer(4))
|
|
206
|
+
|
|
207
|
+
expect(vfs.jOpen(path, fileId, flags, pOutFlags)).toBe(VFS.SQLITE_OK)
|
|
208
|
+
expect(pOutFlags.getUint32(0, true)).toBe(flags)
|
|
209
|
+
|
|
210
|
+
// Write typical SQLite header
|
|
211
|
+
const header = new TextEncoder().encode('SQLite format 3\0')
|
|
212
|
+
expect(vfs.jWrite(fileId, header, 0)).toBe(VFS.SQLITE_OK)
|
|
213
|
+
|
|
214
|
+
// Read back header
|
|
215
|
+
const readHeader = new Uint8Array(header.length)
|
|
216
|
+
expect(vfs.jRead(fileId, readHeader, 0)).toBe(VFS.SQLITE_OK)
|
|
217
|
+
expect(readHeader).toEqual(header)
|
|
218
|
+
|
|
219
|
+
expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('should handle WAL files', async () => {
|
|
223
|
+
const path = '/test/main.db-wal'
|
|
224
|
+
const fileId = 2
|
|
225
|
+
const flags = VFS.SQLITE_OPEN_WAL | VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
|
|
226
|
+
const pOutFlags = new DataView(new ArrayBuffer(4))
|
|
227
|
+
|
|
228
|
+
expect(vfs.jOpen(path, fileId, flags, pOutFlags)).toBe(VFS.SQLITE_OK)
|
|
229
|
+
|
|
230
|
+
// Write WAL data
|
|
231
|
+
const walData = new Uint8Array(1000)
|
|
232
|
+
walData.fill(0xee)
|
|
233
|
+
expect(vfs.jWrite(fileId, walData, 0)).toBe(VFS.SQLITE_OK)
|
|
234
|
+
|
|
235
|
+
// Verify WAL data
|
|
236
|
+
const readWalData = new Uint8Array(walData.length)
|
|
237
|
+
expect(vfs.jRead(fileId, readWalData, 0)).toBe(VFS.SQLITE_OK)
|
|
238
|
+
expect(readWalData).toEqual(walData)
|
|
239
|
+
|
|
240
|
+
expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('should handle journal files', async () => {
|
|
244
|
+
const path = '/test/main.db-journal'
|
|
245
|
+
const fileId = 3
|
|
246
|
+
const flags = VFS.SQLITE_OPEN_MAIN_JOURNAL | VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
|
|
247
|
+
const pOutFlags = new DataView(new ArrayBuffer(4))
|
|
248
|
+
|
|
249
|
+
expect(vfs.jOpen(path, fileId, flags, pOutFlags)).toBe(VFS.SQLITE_OK)
|
|
250
|
+
|
|
251
|
+
// Write journal data
|
|
252
|
+
const journalData = new Uint8Array(500)
|
|
253
|
+
journalData.fill(0xff)
|
|
254
|
+
expect(vfs.jWrite(fileId, journalData, 0)).toBe(VFS.SQLITE_OK)
|
|
255
|
+
|
|
256
|
+
// Verify journal data
|
|
257
|
+
const readJournalData = new Uint8Array(journalData.length)
|
|
258
|
+
expect(vfs.jRead(fileId, readJournalData, 0)).toBe(VFS.SQLITE_OK)
|
|
259
|
+
expect(readJournalData).toEqual(journalData)
|
|
260
|
+
|
|
261
|
+
expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
it('should handle temporary files', async () => {
|
|
265
|
+
const path = '/test/temp.db'
|
|
266
|
+
const fileId = 4
|
|
267
|
+
const flags = VFS.SQLITE_OPEN_TEMP_DB | VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
|
|
268
|
+
const pOutFlags = new DataView(new ArrayBuffer(4))
|
|
269
|
+
|
|
270
|
+
expect(vfs.jOpen(path, fileId, flags, pOutFlags)).toBe(VFS.SQLITE_OK)
|
|
271
|
+
|
|
272
|
+
// Write temporary data
|
|
273
|
+
const tempData = new TextEncoder().encode('Temporary database content')
|
|
274
|
+
expect(vfs.jWrite(fileId, tempData, 0)).toBe(VFS.SQLITE_OK)
|
|
275
|
+
|
|
276
|
+
// Read temporary data
|
|
277
|
+
const readTempData = new Uint8Array(tempData.length)
|
|
278
|
+
expect(vfs.jRead(fileId, readTempData, 0)).toBe(VFS.SQLITE_OK)
|
|
279
|
+
expect(readTempData).toEqual(tempData)
|
|
280
|
+
|
|
281
|
+
expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
|
|
282
|
+
})
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
describe('Advanced Operations', () => {
|
|
286
|
+
it('should handle multiple files with different types simultaneously', async () => {
|
|
287
|
+
const files = [
|
|
288
|
+
{
|
|
289
|
+
path: '/test/multi-main.db',
|
|
290
|
+
id: 1,
|
|
291
|
+
flags: VFS.SQLITE_OPEN_MAIN_DB | VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE,
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
path: '/test/multi-main.db-wal',
|
|
295
|
+
id: 2,
|
|
296
|
+
flags: VFS.SQLITE_OPEN_WAL | VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE,
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
path: '/test/multi-main.db-journal',
|
|
300
|
+
id: 3,
|
|
301
|
+
flags: VFS.SQLITE_OPEN_MAIN_JOURNAL | VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE,
|
|
302
|
+
},
|
|
303
|
+
]
|
|
304
|
+
|
|
305
|
+
const pOutFlags = new DataView(new ArrayBuffer(4))
|
|
306
|
+
|
|
307
|
+
// Open all files
|
|
308
|
+
for (const file of files) {
|
|
309
|
+
expect(vfs.jOpen(file.path, file.id, file.flags, pOutFlags)).toBe(VFS.SQLITE_OK)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Write different data to each file
|
|
313
|
+
for (let i = 0; i < files.length; i++) {
|
|
314
|
+
const data = new TextEncoder().encode(`File ${i} data`)
|
|
315
|
+
expect(vfs.jWrite(files[i]?.id ?? 0, data, 0)).toBe(VFS.SQLITE_OK)
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Verify each file has correct data
|
|
319
|
+
for (let i = 0; i < files.length; i++) {
|
|
320
|
+
const expected = new TextEncoder().encode(`File ${i} data`)
|
|
321
|
+
const actual = new Uint8Array(expected.length)
|
|
322
|
+
expect(vfs.jRead(files[i]?.id ?? 0, actual, 0)).toBe(VFS.SQLITE_OK)
|
|
323
|
+
expect(actual).toEqual(expected)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Close all files
|
|
327
|
+
for (const file of files) {
|
|
328
|
+
expect(vfs.jClose(file.id)).toBe(VFS.SQLITE_OK)
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
it('should handle file truncation with chunking', async () => {
|
|
333
|
+
const path = '/test/truncate-chunks.db'
|
|
334
|
+
const fileId = 1
|
|
335
|
+
const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
|
|
336
|
+
const pOutFlags = new DataView(new ArrayBuffer(4))
|
|
337
|
+
|
|
338
|
+
vfs.jOpen(path, fileId, flags, pOutFlags)
|
|
339
|
+
|
|
340
|
+
const chunkSize = 64 * 1024
|
|
341
|
+
|
|
342
|
+
// Write data spanning 3 chunks
|
|
343
|
+
const largeData = new Uint8Array(chunkSize * 3)
|
|
344
|
+
largeData.fill(0xdd)
|
|
345
|
+
expect(vfs.jWrite(fileId, largeData, 0)).toBe(VFS.SQLITE_OK)
|
|
346
|
+
|
|
347
|
+
// Truncate to 1.5 chunks
|
|
348
|
+
const truncateSize = chunkSize + chunkSize / 2
|
|
349
|
+
expect(vfs.jTruncate(fileId, truncateSize)).toBe(VFS.SQLITE_OK)
|
|
350
|
+
|
|
351
|
+
// Verify new size
|
|
352
|
+
const pSize64 = new DataView(new ArrayBuffer(8))
|
|
353
|
+
expect(vfs.jFileSize(fileId, pSize64)).toBe(VFS.SQLITE_OK)
|
|
354
|
+
expect(pSize64.getBigInt64(0, true)).toBe(BigInt(truncateSize))
|
|
355
|
+
|
|
356
|
+
// Verify data integrity after truncation
|
|
357
|
+
const readData = new Uint8Array(truncateSize)
|
|
358
|
+
expect(vfs.jRead(fileId, readData, 0)).toBe(VFS.SQLITE_OK)
|
|
359
|
+
expect(readData).toEqual(largeData.slice(0, truncateSize))
|
|
360
|
+
|
|
361
|
+
expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
it('should handle sync operations with proper flush behavior', async () => {
|
|
365
|
+
const path = '/test/sync-flush.db'
|
|
366
|
+
const fileId = 1
|
|
367
|
+
const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
|
|
368
|
+
const pOutFlags = new DataView(new ArrayBuffer(4))
|
|
369
|
+
|
|
370
|
+
vfs.jOpen(path, fileId, flags, pOutFlags)
|
|
371
|
+
|
|
372
|
+
// Write data
|
|
373
|
+
const testData = new TextEncoder().encode('Sync test data')
|
|
374
|
+
expect(vfs.jWrite(fileId, testData, 0)).toBe(VFS.SQLITE_OK)
|
|
375
|
+
|
|
376
|
+
// Test different sync modes
|
|
377
|
+
expect(vfs.jSync(fileId, VFS.SQLITE_SYNC_NORMAL)).toBe(VFS.SQLITE_OK)
|
|
378
|
+
expect(vfs.jSync(fileId, VFS.SQLITE_SYNC_FULL)).toBe(VFS.SQLITE_OK)
|
|
379
|
+
expect(vfs.jSync(fileId, VFS.SQLITE_SYNC_DATAONLY)).toBe(VFS.SQLITE_OK)
|
|
380
|
+
|
|
381
|
+
// Verify data is still accessible after sync
|
|
382
|
+
const readData = new Uint8Array(testData.length)
|
|
383
|
+
expect(vfs.jRead(fileId, readData, 0)).toBe(VFS.SQLITE_OK)
|
|
384
|
+
expect(readData).toEqual(testData)
|
|
385
|
+
|
|
386
|
+
expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
|
|
387
|
+
})
|
|
388
|
+
})
|
|
389
|
+
})
|