@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,60 @@
1
+ # CF SQLite WASM
2
+
3
+ ## VFS SQL Schema for Cloudflare Durable Object SQL Storage
4
+
5
+ This schema implements a block-based file storage system on top of SQLite
6
+
7
+ ## Schema
8
+
9
+ ```sql
10
+
11
+ -- File metadata table
12
+ -- Stores information about each virtual file in the VFS
13
+ CREATE TABLE IF NOT EXISTS vfs_files (
14
+ file_path TEXT PRIMARY KEY,
15
+ file_size INTEGER NOT NULL DEFAULT 0,
16
+ flags INTEGER NOT NULL DEFAULT 0,
17
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
18
+ modified_at INTEGER NOT NULL DEFAULT (unixepoch())
19
+ );
20
+
21
+ -- Block-based file data storage
22
+ -- Files are split into fixed-size blocks for efficient I/O operations
23
+ CREATE TABLE IF NOT EXISTS vfs_blocks (
24
+ file_path TEXT NOT NULL,
25
+ block_id INTEGER NOT NULL,
26
+ block_data BLOB NOT NULL,
27
+ created_at INTEGER NOT NULL DEFAULT (unixepoch()),
28
+ PRIMARY KEY (file_path, block_id),
29
+ FOREIGN KEY (file_path) REFERENCES vfs_files(file_path) ON DELETE CASCADE
30
+ );
31
+
32
+ -- Index for efficient block range queries
33
+ -- Essential for performance when reading/writing sequential blocks
34
+ CREATE INDEX IF NOT EXISTS idx_vfs_blocks_range ON vfs_blocks(file_path, block_id);
35
+
36
+ -- Index for file metadata queries
37
+ CREATE INDEX IF NOT EXISTS idx_vfs_files_modified ON vfs_files(modified_at);
38
+
39
+ -- Trigger to update modified_at timestamp when file size changes
40
+ CREATE TRIGGER IF NOT EXISTS trg_vfs_files_update_modified
41
+ AFTER UPDATE OF file_size ON vfs_files
42
+ BEGIN
43
+ UPDATE vfs_files SET modified_at = unixepoch() WHERE file_path = NEW.file_path;
44
+ END;
45
+
46
+ -- View for file statistics (useful for debugging and monitoring)
47
+ CREATE VIEW IF NOT EXISTS vfs_file_stats AS
48
+ SELECT
49
+ f.file_path,
50
+ f.file_size,
51
+ f.flags,
52
+ f.created_at,
53
+ f.modified_at,
54
+ COUNT(b.block_id) as block_count,
55
+ COALESCE(SUM(LENGTH(b.block_data)), 0) as stored_bytes,
56
+ ROUND(CAST(COALESCE(SUM(LENGTH(b.block_data)), 0) AS REAL) / NULLIF(f.file_size, 0) * 100, 2) as compression_ratio
57
+ FROM vfs_files f
58
+ LEFT JOIN vfs_blocks b ON f.file_path = b.file_path
59
+ GROUP BY f.file_path, f.file_size, f.flags, f.created_at, f.modified_at;
60
+ ```
package/src/cf/mod.ts ADDED
@@ -0,0 +1,143 @@
1
+ // import path from 'node:path'
2
+
3
+ import { type MakeSqliteDb, type PersistenceInfo, type SqliteDb, UnexpectedError } from '@livestore/common'
4
+ import type { CfTypes } from '@livestore/common-cf'
5
+ import { Effect } from '@livestore/utils/effect'
6
+ import type * as WaSqlite from '@livestore/wa-sqlite'
7
+ import type { MemoryVFS } from '@livestore/wa-sqlite/src/examples/MemoryVFS.js'
8
+ import { makeInMemoryDb } from '../in-memory-vfs.ts'
9
+ import { makeSqliteDb } from '../make-sqlite-db.ts'
10
+ import { CloudflareSqlVFS } from './CloudflareSqlVFS.ts'
11
+
12
+ export { BlockManager } from './BlockManager.ts'
13
+ export { CloudflareSqlVFS } from './CloudflareSqlVFS.ts'
14
+ export { CloudflareWorkerVFS } from './CloudflareWorkerVFS.ts'
15
+
16
+ export type CloudflareDatabaseMetadataInMemory = {
17
+ _tag: 'in-memory'
18
+ vfs: MemoryVFS
19
+ dbPointer: number
20
+ persistenceInfo: PersistenceInfo
21
+ deleteDb: () => void
22
+ configureDb: (db: SqliteDb) => void
23
+ }
24
+
25
+ export type CloudflareDatabaseMetadataFs = {
26
+ _tag: 'storage'
27
+ // vfs: CloudflareWorkerVFS
28
+ vfs: CloudflareSqlVFS
29
+ dbPointer: number
30
+ persistenceInfo: PersistenceInfo
31
+ deleteDb: () => void
32
+ configureDb: (db: SqliteDb) => void
33
+ }
34
+
35
+ export type CloudflareDatabaseMetadata = CloudflareDatabaseMetadataInMemory | CloudflareDatabaseMetadataFs
36
+
37
+ export type CloudflareDatabaseInputInMemory = {
38
+ _tag: 'in-memory'
39
+ configureDb?: (db: SqliteDb) => void
40
+ }
41
+
42
+ export type CloudflareDatabaseInputFs = {
43
+ _tag: 'storage'
44
+ // directory: string
45
+ fileName: string
46
+ configureDb?: (db: SqliteDb) => void
47
+ storage: CfTypes.DurableObjectStorage
48
+ }
49
+
50
+ export type CloudflareDatabaseInput = CloudflareDatabaseInputInMemory | CloudflareDatabaseInputFs
51
+
52
+ export type MakeCloudflareSqliteDb = MakeSqliteDb<
53
+ { dbPointer: number; persistenceInfo: PersistenceInfo },
54
+ CloudflareDatabaseInput,
55
+ CloudflareDatabaseMetadata
56
+ >
57
+
58
+ export const sqliteDbFactory =
59
+ ({ sqlite3 }: { sqlite3: SQLiteAPI }): MakeCloudflareSqliteDb =>
60
+ (input) =>
61
+ Effect.gen(function* () {
62
+ if (input._tag === 'in-memory') {
63
+ const { dbPointer, vfs } = makeInMemoryDb(sqlite3)
64
+ return makeSqliteDb<CloudflareDatabaseMetadataInMemory>({
65
+ sqlite3,
66
+ metadata: {
67
+ _tag: 'in-memory',
68
+ vfs,
69
+ dbPointer,
70
+ persistenceInfo: { fileName: ':memory:' },
71
+ deleteDb: () => {},
72
+ configureDb: input.configureDb ?? (() => {}),
73
+ },
74
+ }) as any
75
+ }
76
+
77
+ const { dbPointer, vfs } = yield* makeCloudflareFsDb({
78
+ sqlite3,
79
+ fileName: input.fileName,
80
+ // directory: input.directory,
81
+ storage: input.storage,
82
+ })
83
+
84
+ // const filePath = path.join(input.directory, input.fileName)
85
+ // const filePath = `${input.directory}/${input.fileName}`
86
+
87
+ return makeSqliteDb<CloudflareDatabaseMetadataFs>({
88
+ sqlite3,
89
+ metadata: {
90
+ _tag: 'storage',
91
+ vfs,
92
+ dbPointer,
93
+ persistenceInfo: { fileName: input.fileName },
94
+ // deleteDb: () => vfs.deleteDb(filePath),
95
+ // TODO: implement deleteDb
96
+ deleteDb: () => {},
97
+ configureDb: input.configureDb ?? (() => {}),
98
+ },
99
+ })
100
+ })
101
+
102
+ const makeCloudflareFsDb = ({
103
+ sqlite3,
104
+ fileName,
105
+ // directory,
106
+ storage,
107
+ }: {
108
+ sqlite3: WaSqlite.SQLiteAPI
109
+ fileName: string
110
+ // directory: string
111
+ storage: CfTypes.DurableObjectStorage
112
+ }) =>
113
+ Effect.gen(function* () {
114
+ // NOTE to keep the filePath short, we use the directory name in the vfs name
115
+ // If this is becoming a problem, we can use a hashed version of the directory name
116
+ const vfsName = `cf-do-sqlite-${fileName}`
117
+ if (sqlite3.vfs_registered.has(vfsName) === false) {
118
+ // TODO refactor with Effect FileSystem instead of using `node:fs` directly inside of CloudflareWorkerVFS
119
+ // const nodeFsVfs = new CloudflareWorkerVFS(vfsName, storage, (sqlite3 as any).module)
120
+ const nodeFsVfs = new CloudflareSqlVFS(vfsName, storage.sql, (sqlite3 as any).module)
121
+
122
+ // Initialize the VFS schema before registering it
123
+ const isReady = yield* Effect.promise(() => nodeFsVfs.isReady())
124
+ if (!isReady) {
125
+ throw new Error(`Failed to initialize CloudflareSqlVFS for ${vfsName}`)
126
+ }
127
+
128
+ // @ts-expect-error TODO fix types
129
+ sqlite3.vfs_register(nodeFsVfs, false)
130
+ }
131
+
132
+ // yield* fs.makeDirectory(directory, { recursive: true })
133
+
134
+ const FILE_NAME_MAX_LENGTH = 56
135
+ if (fileName.length > FILE_NAME_MAX_LENGTH) {
136
+ throw new Error(`File name ${fileName} is too long. Maximum length is ${FILE_NAME_MAX_LENGTH} characters.`)
137
+ }
138
+
139
+ // NOTE SQLite will return a "disk I/O error" if the file path is too long.
140
+ const dbPointer = sqlite3.open_v2Sync(fileName, undefined, vfsName)
141
+
142
+ return { dbPointer, vfs: {} as UNUSED<'only needed in web adapter currently and should longer-term be removed'> }
143
+ }).pipe(UnexpectedError.mapToUnexpectedError)
@@ -0,0 +1,224 @@
1
+ # Cloudflare SQLite WASM Test Suite
2
+
3
+ This directory contains comprehensive tests for both SQLite WASM VFS implementations on Cloudflare Workers.
4
+
5
+ ## Directory Structure
6
+
7
+ ### `/sql/` - SQL Storage Tests
8
+ Tests for the CloudflareSqlVFS implementation, which uses Cloudflare's DurableObject SQL API for storage.
9
+
10
+ - **`cloudflare-sql-vfs-core.test.ts`** - Core SQL VFS functionality tests
11
+
12
+ ### `/async-storage/` - Async Storage Tests
13
+ Tests for the CloudflareWorkerVFS implementation, which uses DurableObjectStorage (key-value) for file storage.
14
+
15
+ - **`cloudflare-worker-vfs-core.test.ts`** - Basic VFS operations (open, read, write, close, sync)
16
+ - **`cloudflare-worker-vfs-advanced.test.ts`** - Large file chunking and advanced features
17
+ - **`cloudflare-worker-vfs-reliability.test.ts`** - Error recovery and reliability testing
18
+ - **`cloudflare-worker-vfs-integration.test.ts`** - End-to-end integration tests
19
+
20
+ ## Test Architecture
21
+
22
+ ### Testing Framework
23
+ - **Vitest** with **@cloudflare/vitest-pool-workers** for Workers runtime testing
24
+ - **Isolated Storage** - Each test gets fresh storage instances
25
+ - **Real Runtime** - Tests run in the actual Cloudflare Workers runtime environment
26
+
27
+ ## VFS Implementation Comparison
28
+
29
+ ### SQL Storage VFS (`CloudflareSqlVFS`)
30
+ - **Backend**: Cloudflare DurableObject SQL API
31
+ - **Storage Model**: Relational tables with blocks and metadata
32
+ - **Advantages**: ACID transactions, complex queries, relational integrity
33
+ - **Use Case**: Applications requiring complex data relationships
34
+
35
+ ### Async Storage VFS (`CloudflareWorkerVFS`)
36
+ - **Backend**: DurableObjectStorage (key-value)
37
+ - **Storage Model**: 64 KiB chunks with LRU caching
38
+ - **Advantages**: Simple API, optimized for large files, better caching
39
+ - **Use Case**: High-performance file operations, large databases
40
+
41
+ ## Key Design Decisions Tested
42
+
43
+ ### SQL Storage Approach
44
+ - **Block-based Storage**: Files stored as blocks in SQL tables
45
+ - **Metadata Management**: File metadata in dedicated tables
46
+ - **Transaction Safety**: ACID compliance for data integrity
47
+
48
+ ### Async Storage Approach
49
+ - **64 KiB Chunking Strategy**: Optimized for SQLite I/O patterns
50
+ - **Synchronous Interface with Async Backend**: Aggressive caching + background sync
51
+ - **Memory Management**: LRU cache for chunks, complete metadata cache
52
+ - **Error Handling**: SQLite-compatible error codes
53
+
54
+ ## Running Tests
55
+
56
+ ### Prerequisites
57
+ 1. Install dependencies:
58
+ ```bash
59
+ pnpm install
60
+ ```
61
+
62
+ 2. Ensure Wrangler configuration is correct:
63
+ ```bash
64
+ # Check wrangler.toml exists and has correct Durable Object bindings
65
+ ```
66
+
67
+ ### Test Commands
68
+
69
+ ```bash
70
+ # Run all tests
71
+ pnpm test
72
+
73
+ # Run tests in watch mode
74
+ pnpm test:watch
75
+
76
+ # Run tests with UI
77
+ pnpm test:ui
78
+
79
+ # Run specific test directory
80
+ pnpm test src/cf/ # All cf tests (SQL + async storage)
81
+ pnpm test src/cf/test/sql/ # SQL storage tests only
82
+ pnpm test src/cf/test/async-storage/ # Async storage tests only
83
+
84
+ # Run specific test file
85
+ pnpm test cloudflare-sql-vfs-core.test.ts
86
+
87
+ # Run with coverage
88
+ pnpm test --coverage
89
+ ```
90
+
91
+ ### Test Environment
92
+
93
+ - **Runtime**: Cloudflare Workers (via workerd)
94
+ - **Storage**: Isolated per-test storage instances
95
+
96
+ ## Test Structure
97
+
98
+ ### SQL Storage Test Pattern
99
+ ```typescript
100
+ import { describe, it, expect, beforeEach } from 'vitest'
101
+ import { type Cf, CloudflareSqlVFS } from '../../mod.ts'
102
+
103
+ describe('SQL VFS Test Suite', () => {
104
+ let vfs: CloudflareSqlVFS
105
+ let mockSql: Cf.SqlStorage
106
+ let queryLog: string[]
107
+
108
+ beforeEach(async () => {
109
+ // Setup mock SQL storage
110
+ mockSql = {
111
+ exec: (query: string, ...bindings: any[]) => {
112
+ // Mock SQL implementation
113
+ }
114
+ }
115
+
116
+ vfs = new CloudflareSqlVFS('test-vfs', mockSql, {})
117
+ await vfs.isReady()
118
+ })
119
+
120
+ // Tests...
121
+ })
122
+ ```
123
+
124
+ ### Async Storage Test Pattern
125
+ ```typescript
126
+ import { describe, it, expect, beforeEach } from 'vitest'
127
+ import { type Cf, CloudflareWorkerVFS } from '../../mod.ts'
128
+
129
+ describe('Async Storage VFS Test Suite', () => {
130
+ let vfs: CloudflareWorkerVFS
131
+ let mockStorage: DurableObjectStorage
132
+
133
+ beforeEach(async () => {
134
+ // Setup mock DurableObjectStorage
135
+ mockStorage = {
136
+ get: async (key) => { /* mock implementation */ },
137
+ put: async (key, value) => { /* mock implementation */ },
138
+ delete: async (key) => { /* mock implementation */ },
139
+ // ... other methods
140
+ }
141
+
142
+ vfs = new CloudflareWorkerVFS('test-vfs', mockStorage, {})
143
+ await vfs.isReady()
144
+ })
145
+
146
+ // Tests...
147
+ })
148
+ ```
149
+
150
+ ## Performance Benchmarks
151
+
152
+ ### Metrics Tracked
153
+ - **Throughput**: Operations per second
154
+ - **Latency**: Response times for various operations
155
+ - **Memory Usage**: Cache effectiveness and memory consumption
156
+ - **Storage Efficiency**: Data compression and chunking effectiveness
157
+
158
+ ### Benchmark Comparisons
159
+ - SQL vs Async Storage performance characteristics
160
+ - Memory usage patterns between implementations
161
+ - Scalability under different workloads
162
+
163
+ ## Test Data Cleanup
164
+
165
+ ### Automatic Cleanup
166
+ - **Isolated Storage**: Each test gets fresh storage instances
167
+ - **Temporary Files**: Cleaned up automatically
168
+ - **Cache Management**: Memory caches cleared between tests
169
+
170
+ ## Debugging Tests
171
+
172
+ ### Logging
173
+ - Use `console.log` for debugging (visible in test output)
174
+ - VFS statistics available via `vfs.getStats()`
175
+ - Storage operations logged in development mode
176
+
177
+ ### Common Issues
178
+ 1. **SQL Schema Issues**: Ensure proper table creation and constraints
179
+ 2. **Cache Misses**: Verify proper preloading in VFS initialization
180
+ 3. **Async/Sync Mismatch**: Check async operations are properly handled
181
+ 4. **Storage Limits**: Verify chunk sizes and storage capacity
182
+
183
+ ### Test Debugging
184
+ ```typescript
185
+ // SQL VFS debugging
186
+ console.log('Query log:', queryLog)
187
+ const stats = vfs.getStats()
188
+ console.log('VFS Stats:', stats)
189
+
190
+ // Async Storage VFS debugging
191
+ const metadata = await storage.get('file:/test/file.db:meta')
192
+ console.log('File metadata:', metadata)
193
+ ```
194
+
195
+ ## Future Enhancements
196
+
197
+ ### Planned Test Additions
198
+ 1. **Performance Comparisons**: Direct SQL vs Async Storage benchmarks
199
+ 2. **Migration Tests**: Converting between storage backends
200
+ 3. **Stress Tests**: High-load scenarios for both implementations
201
+ 4. **Real SQLite Integration**: Tests with actual SQLite WASM
202
+
203
+ ### Implementation Improvements
204
+ 1. **Hybrid Approach**: Combining SQL and async storage benefits
205
+ 2. **Compression Testing**: Data compression effectiveness
206
+ 3. **Caching Strategies**: Cross-implementation cache optimization
207
+
208
+ ## Contributing
209
+
210
+ When adding new tests:
211
+ 1. Choose the appropriate location:
212
+ - SQL storage tests: `/src/cf/test/sql/`
213
+ - Async storage tests: `/src/cf/test/async-storage/`
214
+ 2. Follow the existing test structure for that implementation
215
+ 3. Use descriptive test names indicating the VFS type
216
+ 4. Include both positive and negative test cases
217
+ 5. Update this documentation
218
+
219
+ ### Test Guidelines
220
+ - **Isolation**: Each test should be independent
221
+ - **Implementation Specific**: Tests should target specific VFS features
222
+ - **Assertions**: Use meaningful assertions with clear error messages
223
+ - **Coverage**: Aim for comprehensive coverage of edge cases
224
+ - **Performance**: Consider performance implications of each approach