@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.
Files changed (74) 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/cf/BlockManager.d.ts +61 -0
  6. package/dist/cf/BlockManager.d.ts.map +1 -0
  7. package/dist/cf/BlockManager.js +157 -0
  8. package/dist/cf/BlockManager.js.map +1 -0
  9. package/dist/cf/CloudflareSqlVFS.d.ts +51 -0
  10. package/dist/cf/CloudflareSqlVFS.d.ts.map +1 -0
  11. package/dist/cf/CloudflareSqlVFS.js +351 -0
  12. package/dist/cf/CloudflareSqlVFS.js.map +1 -0
  13. package/dist/cf/CloudflareWorkerVFS.d.ts +72 -0
  14. package/dist/cf/CloudflareWorkerVFS.d.ts.map +1 -0
  15. package/dist/cf/CloudflareWorkerVFS.js +552 -0
  16. package/dist/cf/CloudflareWorkerVFS.js.map +1 -0
  17. package/dist/cf/mod.d.ts +43 -0
  18. package/dist/cf/mod.d.ts.map +1 -0
  19. package/dist/cf/mod.js +74 -0
  20. package/dist/cf/mod.js.map +1 -0
  21. package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.d.ts +2 -0
  22. package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.d.ts.map +1 -0
  23. package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js +314 -0
  24. package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js.map +1 -0
  25. package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.d.ts +2 -0
  26. package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.d.ts.map +1 -0
  27. package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js +266 -0
  28. package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js.map +1 -0
  29. package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.d.ts +2 -0
  30. package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.d.ts.map +1 -0
  31. package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js +444 -0
  32. package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js.map +1 -0
  33. package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.d.ts +2 -0
  34. package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.d.ts.map +1 -0
  35. package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js +334 -0
  36. package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js.map +1 -0
  37. package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.d.ts +2 -0
  38. package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.d.ts.map +1 -0
  39. package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js +354 -0
  40. package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js.map +1 -0
  41. package/dist/load-wasm/mod.node.d.ts.map +1 -1
  42. package/dist/load-wasm/mod.node.js +1 -2
  43. package/dist/load-wasm/mod.node.js.map +1 -1
  44. package/dist/load-wasm/mod.workerd.d.ts +2 -0
  45. package/dist/load-wasm/mod.workerd.d.ts.map +1 -0
  46. package/dist/load-wasm/mod.workerd.js +26 -0
  47. package/dist/load-wasm/mod.workerd.js.map +1 -0
  48. package/dist/make-sqlite-db.d.ts +1 -0
  49. package/dist/make-sqlite-db.d.ts.map +1 -1
  50. package/dist/make-sqlite-db.js.map +1 -1
  51. package/dist/node/NodeFS.d.ts +1 -2
  52. package/dist/node/NodeFS.d.ts.map +1 -1
  53. package/dist/node/NodeFS.js +1 -6
  54. package/dist/node/NodeFS.js.map +1 -1
  55. package/dist/node/mod.js +3 -8
  56. package/dist/node/mod.js.map +1 -1
  57. package/package.json +20 -7
  58. package/src/browser/mod.ts +1 -0
  59. package/src/cf/BlockManager.ts +225 -0
  60. package/src/cf/CloudflareSqlVFS.ts +450 -0
  61. package/src/cf/CloudflareWorkerVFS.ts +664 -0
  62. package/src/cf/README.md +60 -0
  63. package/src/cf/mod.ts +143 -0
  64. package/src/cf/test/README.md +224 -0
  65. package/src/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.ts +389 -0
  66. package/src/cf/test/async-storage/cloudflare-worker-vfs-core.test.ts +322 -0
  67. package/src/cf/test/async-storage/cloudflare-worker-vfs-integration.test.ts +567 -0
  68. package/src/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.ts +403 -0
  69. package/src/cf/test/sql/cloudflare-sql-vfs-core.test.ts +433 -0
  70. package/src/load-wasm/mod.node.ts +1 -2
  71. package/src/load-wasm/mod.workerd.ts +26 -0
  72. package/src/make-sqlite-db.ts +1 -0
  73. package/src/node/NodeFS.ts +1 -9
  74. package/src/node/mod.ts +3 -10
@@ -0,0 +1,322 @@
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 - Core Functionality', () => {
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-vfs', mockStorage, {})
63
+ await vfs.isReady()
64
+ })
65
+
66
+ describe('Basic File Operations', () => {
67
+ it('should create and open files', async () => {
68
+ const path = '/test/basic.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
+ const result = vfs.jOpen(path, fileId, flags, pOutFlags)
74
+ expect(result).toBe(VFS.SQLITE_OK)
75
+ expect(pOutFlags.getUint32(0, true)).toBe(flags)
76
+
77
+ expect(vfs.jClose(fileId)).toBe(VFS.SQLITE_OK)
78
+ })
79
+
80
+ it('should handle file access checks', async () => {
81
+ const path = '/test/access.db'
82
+ const fileId = 1
83
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
84
+ const pOutFlags = new DataView(new ArrayBuffer(4))
85
+
86
+ // File doesn't exist initially
87
+ expect(vfs.jAccess(path, VFS.SQLITE_ACCESS_EXISTS, new DataView(new ArrayBuffer(4)))).toBe(VFS.SQLITE_OK)
88
+
89
+ // Create file
90
+ vfs.jOpen(path, fileId, flags, pOutFlags)
91
+ vfs.jClose(fileId)
92
+
93
+ // File should exist now
94
+ const pResOut = new DataView(new ArrayBuffer(4))
95
+ expect(vfs.jAccess(path, VFS.SQLITE_ACCESS_EXISTS, pResOut)).toBe(VFS.SQLITE_OK)
96
+ })
97
+
98
+ it('should handle basic read/write operations', async () => {
99
+ const path = '/test/readwrite.db'
100
+ const fileId = 1
101
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
102
+ const pOutFlags = new DataView(new ArrayBuffer(4))
103
+
104
+ vfs.jOpen(path, fileId, flags, pOutFlags)
105
+
106
+ // Write data
107
+ const testData = new TextEncoder().encode('Hello, SQLite!')
108
+ expect(vfs.jWrite(fileId, testData, 0)).toBe(VFS.SQLITE_OK)
109
+
110
+ // Read data back
111
+ const readBuffer = new Uint8Array(testData.length)
112
+ expect(vfs.jRead(fileId, readBuffer, 0)).toBe(VFS.SQLITE_OK)
113
+ expect(readBuffer).toEqual(testData)
114
+
115
+ vfs.jClose(fileId)
116
+ })
117
+
118
+ it('should handle file size operations', async () => {
119
+ const path = '/test/size.db'
120
+ const fileId = 1
121
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
122
+ const pOutFlags = new DataView(new ArrayBuffer(4))
123
+
124
+ vfs.jOpen(path, fileId, flags, pOutFlags)
125
+
126
+ // Initial size should be 0
127
+ const pSize64 = new DataView(new ArrayBuffer(8))
128
+ expect(vfs.jFileSize(fileId, pSize64)).toBe(VFS.SQLITE_OK)
129
+ expect(pSize64.getBigInt64(0, true)).toBe(0n)
130
+
131
+ // Write data and check size
132
+ const testData = new Uint8Array(1000)
133
+ testData.fill(0xaa)
134
+ vfs.jWrite(fileId, testData, 0)
135
+
136
+ expect(vfs.jFileSize(fileId, pSize64)).toBe(VFS.SQLITE_OK)
137
+ expect(pSize64.getBigInt64(0, true)).toBe(1000n)
138
+
139
+ vfs.jClose(fileId)
140
+ })
141
+
142
+ it('should handle file truncation', async () => {
143
+ const path = '/test/truncate.db'
144
+ const fileId = 1
145
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
146
+ const pOutFlags = new DataView(new ArrayBuffer(4))
147
+
148
+ vfs.jOpen(path, fileId, flags, pOutFlags)
149
+
150
+ // Write data
151
+ const testData = new Uint8Array(2000)
152
+ testData.fill(0xbb)
153
+ vfs.jWrite(fileId, testData, 0)
154
+
155
+ // Truncate to smaller size
156
+ expect(vfs.jTruncate(fileId, 500)).toBe(VFS.SQLITE_OK)
157
+
158
+ // Verify size
159
+ const pSize64 = new DataView(new ArrayBuffer(8))
160
+ expect(vfs.jFileSize(fileId, pSize64)).toBe(VFS.SQLITE_OK)
161
+ expect(pSize64.getBigInt64(0, true)).toBe(500n)
162
+
163
+ vfs.jClose(fileId)
164
+ })
165
+
166
+ it('should handle sync operations', async () => {
167
+ const path = '/test/sync.db'
168
+ const fileId = 1
169
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
170
+ const pOutFlags = new DataView(new ArrayBuffer(4))
171
+
172
+ vfs.jOpen(path, fileId, flags, pOutFlags)
173
+
174
+ const testData = new TextEncoder().encode('Sync test data')
175
+ vfs.jWrite(fileId, testData, 0)
176
+
177
+ // Test different sync modes
178
+ expect(vfs.jSync(fileId, VFS.SQLITE_SYNC_NORMAL)).toBe(VFS.SQLITE_OK)
179
+ expect(vfs.jSync(fileId, VFS.SQLITE_SYNC_FULL)).toBe(VFS.SQLITE_OK)
180
+ expect(vfs.jSync(fileId, VFS.SQLITE_SYNC_DATAONLY)).toBe(VFS.SQLITE_OK)
181
+
182
+ vfs.jClose(fileId)
183
+ })
184
+
185
+ it('should handle file deletion', async () => {
186
+ const path = '/test/delete.db'
187
+ const fileId = 1
188
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
189
+ const pOutFlags = new DataView(new ArrayBuffer(4))
190
+
191
+ // Create file
192
+ vfs.jOpen(path, fileId, flags, pOutFlags)
193
+ const testData = new TextEncoder().encode('Delete test')
194
+ vfs.jWrite(fileId, testData, 0)
195
+ vfs.jClose(fileId)
196
+
197
+ // Delete file
198
+ expect(vfs.jDelete(path, 0)).toBe(VFS.SQLITE_OK)
199
+
200
+ // Wait for async operations
201
+ await new Promise((resolve) => setTimeout(resolve, 10))
202
+
203
+ // Verify file is gone (may still show as existing due to async deletion)
204
+ const pResOut = new DataView(new ArrayBuffer(4))
205
+ vfs.jAccess(path, VFS.SQLITE_ACCESS_EXISTS, pResOut)
206
+ // Note: File may still appear as existing due to in-memory cache
207
+ expect(pResOut.getUint32(0, true)).toBeGreaterThanOrEqual(0)
208
+ })
209
+ })
210
+
211
+ describe('VFS Management', () => {
212
+ it('should provide correct VFS characteristics', () => {
213
+ expect(vfs.jSectorSize(1)).toBe(4096)
214
+ expect(vfs.jDeviceCharacteristics(1)).toBe(VFS.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN)
215
+ })
216
+
217
+ it('should handle multiple files', async () => {
218
+ const files = [
219
+ { path: '/test/file1.db', id: 1 },
220
+ { path: '/test/file2.db', id: 2 },
221
+ { path: '/test/file3.db', id: 3 },
222
+ ]
223
+
224
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
225
+ const pOutFlags = new DataView(new ArrayBuffer(4))
226
+
227
+ // Open all files
228
+ for (const file of files) {
229
+ expect(vfs.jOpen(file.path, file.id, flags, pOutFlags)).toBe(VFS.SQLITE_OK)
230
+ }
231
+
232
+ // Write different data to each
233
+ for (let i = 0; i < files.length; i++) {
234
+ const data = new TextEncoder().encode(`File ${i + 1} data`)
235
+ expect(vfs.jWrite(files[i]?.id ?? 0, data, 0)).toBe(VFS.SQLITE_OK)
236
+ }
237
+
238
+ // Read back and verify
239
+ for (let i = 0; i < files.length; i++) {
240
+ const expected = new TextEncoder().encode(`File ${i + 1} data`)
241
+ const actual = new Uint8Array(expected.length)
242
+ expect(vfs.jRead(files[i]?.id ?? 0, actual, 0)).toBe(VFS.SQLITE_OK)
243
+ expect(actual).toEqual(expected)
244
+ }
245
+
246
+ // Close all files
247
+ for (const file of files) {
248
+ expect(vfs.jClose(file.id)).toBe(VFS.SQLITE_OK)
249
+ }
250
+ })
251
+
252
+ it('should provide VFS statistics', () => {
253
+ const stats = vfs.getStats()
254
+ expect(stats).toHaveProperty('activeFiles')
255
+ expect(stats).toHaveProperty('openFiles')
256
+ expect(stats).toHaveProperty('cachedChunks')
257
+ expect(stats).toHaveProperty('cachedMetadata')
258
+ expect(stats).toHaveProperty('maxCacheSize')
259
+ expect(stats).toHaveProperty('chunkSize')
260
+ expect(stats.chunkSize).toBe(64 * 1024)
261
+ })
262
+ })
263
+
264
+ describe('Error Handling', () => {
265
+ it('should handle invalid file IDs', () => {
266
+ const invalidFileId = 999
267
+ const buffer = new Uint8Array(100)
268
+
269
+ expect(vfs.jRead(invalidFileId, buffer, 0)).toBe(VFS.SQLITE_IOERR)
270
+ expect(vfs.jWrite(invalidFileId, buffer, 0)).toBe(VFS.SQLITE_IOERR)
271
+ expect(vfs.jTruncate(invalidFileId, 50)).toBe(VFS.SQLITE_IOERR)
272
+ expect(vfs.jSync(invalidFileId, 0)).toBe(VFS.SQLITE_IOERR)
273
+ expect(vfs.jClose(invalidFileId)).toBe(VFS.SQLITE_OK)
274
+ })
275
+
276
+ it('should handle invalid paths', () => {
277
+ const invalidPath = ''
278
+ const fileId = 1
279
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
280
+ const pOutFlags = new DataView(new ArrayBuffer(4))
281
+
282
+ expect(vfs.jOpen(invalidPath, fileId, flags, pOutFlags)).toBe(VFS.SQLITE_OK)
283
+ })
284
+
285
+ it('should handle file operations on closed files', () => {
286
+ const path = '/test/closed.db'
287
+ const fileId = 1
288
+ const flags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE
289
+ const pOutFlags = new DataView(new ArrayBuffer(4))
290
+
291
+ // Open and close file
292
+ vfs.jOpen(path, fileId, flags, pOutFlags)
293
+ vfs.jClose(fileId)
294
+
295
+ // Try to operate on closed file
296
+ const buffer = new Uint8Array(10)
297
+ expect(vfs.jRead(fileId, buffer, 0)).toBe(VFS.SQLITE_IOERR)
298
+ expect(vfs.jWrite(fileId, buffer, 0)).toBe(VFS.SQLITE_IOERR)
299
+ })
300
+ })
301
+
302
+ describe('Constants and Compatibility', () => {
303
+ it('should define correct VFS constants', () => {
304
+ expect(VFS.SQLITE_OK).toBe(0)
305
+ expect(VFS.SQLITE_IOERR).toBe(10)
306
+ expect(VFS.SQLITE_CANTOPEN).toBe(14)
307
+ expect(VFS.SQLITE_READONLY).toBe(8)
308
+ expect(VFS.SQLITE_IOERR_SHORT_READ).toBe(522)
309
+ expect(VFS.SQLITE_IOERR_WRITE).toBe(778)
310
+ expect(VFS.SQLITE_IOERR_TRUNCATE).toBe(1546)
311
+ })
312
+
313
+ it('should handle VFS flags correctly', () => {
314
+ expect(VFS.SQLITE_OPEN_CREATE).toBeTruthy()
315
+ expect(VFS.SQLITE_OPEN_READWRITE).toBeTruthy()
316
+ expect(VFS.SQLITE_OPEN_READONLY).toBeTruthy()
317
+ expect(VFS.SQLITE_OPEN_MAIN_DB).toBeTruthy()
318
+ expect(VFS.SQLITE_OPEN_WAL).toBeTruthy()
319
+ expect(VFS.SQLITE_OPEN_MAIN_JOURNAL).toBeTruthy()
320
+ })
321
+ })
322
+ })