@livestore/sqlite-wasm 0.4.0-dev.22 → 0.4.0-dev.23

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 (79) hide show
  1. package/README.md +1 -1
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/FacadeVFS.d.ts +0 -1
  4. package/dist/FacadeVFS.d.ts.map +1 -1
  5. package/dist/FacadeVFS.js +9 -14
  6. package/dist/FacadeVFS.js.map +1 -1
  7. package/dist/browser/mod.d.ts +2 -2
  8. package/dist/browser/mod.d.ts.map +1 -1
  9. package/dist/browser/mod.js +2 -2
  10. package/dist/browser/mod.js.map +1 -1
  11. package/dist/browser/opfs/AccessHandlePoolVFS.d.ts.map +1 -1
  12. package/dist/browser/opfs/AccessHandlePoolVFS.js +15 -13
  13. package/dist/browser/opfs/AccessHandlePoolVFS.js.map +1 -1
  14. package/dist/browser/opfs/opfs-sah-pool.js +3 -3
  15. package/dist/browser/opfs/opfs-sah-pool.js.map +1 -1
  16. package/dist/cf/CloudflareDurableObjectVFS.d.ts +104 -0
  17. package/dist/cf/CloudflareDurableObjectVFS.d.ts.map +1 -0
  18. package/dist/cf/CloudflareDurableObjectVFS.js +281 -0
  19. package/dist/cf/CloudflareDurableObjectVFS.js.map +1 -0
  20. package/dist/cf/CloudflareWorkerVFS.d.ts.map +1 -1
  21. package/dist/cf/CloudflareWorkerVFS.js +29 -28
  22. package/dist/cf/CloudflareWorkerVFS.js.map +1 -1
  23. package/dist/cf/mod.d.ts +3 -4
  24. package/dist/cf/mod.d.ts.map +1 -1
  25. package/dist/cf/mod.js +4 -12
  26. package/dist/cf/mod.js.map +1 -1
  27. package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js +5 -4
  28. package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js.map +1 -1
  29. package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js +3 -3
  30. package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js.map +1 -1
  31. package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js +3 -3
  32. package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js.map +1 -1
  33. package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js +3 -3
  34. package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js.map +1 -1
  35. package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js +194 -179
  36. package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js.map +1 -1
  37. package/dist/in-memory-vfs.d.ts.map +1 -1
  38. package/dist/in-memory-vfs.js +0 -1
  39. package/dist/in-memory-vfs.js.map +1 -1
  40. package/dist/load-wasm/mod.node.js +1 -1
  41. package/dist/load-wasm/mod.node.js.map +1 -1
  42. package/dist/load-wasm/mod.workerd.d.ts.map +1 -1
  43. package/dist/load-wasm/mod.workerd.js.map +1 -1
  44. package/dist/make-sqlite-db.d.ts.map +1 -1
  45. package/dist/make-sqlite-db.js +16 -4
  46. package/dist/make-sqlite-db.js.map +1 -1
  47. package/dist/node/NodeFS.d.ts.map +1 -1
  48. package/dist/node/NodeFS.js +13 -13
  49. package/dist/node/NodeFS.js.map +1 -1
  50. package/package.json +54 -15
  51. package/src/FacadeVFS.ts +9 -14
  52. package/src/browser/mod.ts +1 -1
  53. package/src/browser/opfs/AccessHandlePoolVFS.ts +33 -25
  54. package/src/browser/opfs/opfs-sah-pool.ts +3 -3
  55. package/src/cf/CloudflareDurableObjectVFS.ts +325 -0
  56. package/src/cf/CloudflareWorkerVFS.ts +41 -39
  57. package/src/cf/README.md +3 -3
  58. package/src/cf/mod.ts +10 -15
  59. package/src/cf/test/README.md +55 -26
  60. package/src/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.ts +6 -4
  61. package/src/cf/test/async-storage/cloudflare-worker-vfs-core.test.ts +4 -3
  62. package/src/cf/test/async-storage/cloudflare-worker-vfs-integration.test.ts +4 -3
  63. package/src/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.ts +4 -3
  64. package/src/cf/test/sql/cloudflare-sql-vfs-core.test.ts +228 -197
  65. package/src/in-memory-vfs.ts +0 -1
  66. package/src/load-wasm/mod.node.ts +1 -1
  67. package/src/load-wasm/mod.workerd.ts +0 -1
  68. package/src/make-sqlite-db.ts +24 -4
  69. package/src/node/NodeFS.ts +24 -23
  70. package/dist/cf/BlockManager.d.ts +0 -61
  71. package/dist/cf/BlockManager.d.ts.map +0 -1
  72. package/dist/cf/BlockManager.js +0 -157
  73. package/dist/cf/BlockManager.js.map +0 -1
  74. package/dist/cf/CloudflareSqlVFS.d.ts +0 -51
  75. package/dist/cf/CloudflareSqlVFS.d.ts.map +0 -1
  76. package/dist/cf/CloudflareSqlVFS.js +0 -351
  77. package/dist/cf/CloudflareSqlVFS.js.map +0 -1
  78. package/src/cf/BlockManager.ts +0 -225
  79. package/src/cf/CloudflareSqlVFS.ts +0 -450
@@ -9,6 +9,7 @@ import { SqliteDbHelper, SqliteError } from '@livestore/common'
9
9
  import { EventSequenceNumber } from '@livestore/common/schema'
10
10
  import type { SQLiteAPI } from '@livestore/wa-sqlite'
11
11
  import * as SqliteConstants from '@livestore/wa-sqlite/src/sqlite-constants.js'
12
+
12
13
  import { makeInMemoryDb } from './in-memory-vfs.ts'
13
14
 
14
15
  export const makeSqliteDb = <
@@ -53,7 +54,7 @@ export const makeSqliteDb = <
53
54
  try {
54
55
  sqlite3.step(stmt)
55
56
  } finally {
56
- if (options?.onRowsChanged) {
57
+ if (options?.onRowsChanged !== undefined) {
57
58
  options.onRowsChanged(sqlite3.changes(dbPointer))
58
59
  }
59
60
 
@@ -109,7 +110,7 @@ export const makeSqliteDb = <
109
110
  },
110
111
  finalize: () => {
111
112
  // Avoid double finalization which leads to a crash
112
- if (isFinalized) {
113
+ if (isFinalized === true) {
113
114
  return
114
115
  }
115
116
 
@@ -151,7 +152,7 @@ export const makeSqliteDb = <
151
152
  metadata.deleteDb()
152
153
  },
153
154
  close: () => {
154
- if (isClosed) {
155
+ if (isClosed === true) {
155
156
  return
156
157
  }
157
158
 
@@ -241,7 +242,26 @@ export const makeSqliteDb = <
241
242
  },
242
243
  apply: () => {
243
244
  try {
244
- sqlite3.changeset_apply(dbPointer, data)
245
+ sqlite3.changeset_apply(
246
+ dbPointer,
247
+ data,
248
+ null,
249
+ (eConflict) => {
250
+ // During rollback we apply inverted changesets to undo local events
251
+ // before replaying them on top of remote state. We want the undo to
252
+ // succeed unconditionally: if the row was already changed by another
253
+ // tab (DATA) or reinserted (CONFLICT), force-overwrite it. For
254
+ // NOTFOUND, CONSTRAINT, and FOREIGN_KEY the row is already gone or
255
+ // can't be replaced, so we skip.
256
+ if (
257
+ eConflict === SqliteConstants.SQLITE_CHANGESET_DATA ||
258
+ eConflict === SqliteConstants.SQLITE_CHANGESET_CONFLICT
259
+ ) {
260
+ return SqliteConstants.SQLITE_CHANGESET_REPLACE
261
+ }
262
+ return SqliteConstants.SQLITE_CHANGESET_OMIT
263
+ },
264
+ )
245
265
  // @ts-expect-error data should be garbage collected after use
246
266
  // biome-ignore lint/style/noParameterAssign: ...
247
267
  data = undefined
@@ -25,14 +25,15 @@ export class NodeFS extends FacadeVFS {
25
25
  this.directory = directory
26
26
  }
27
27
 
28
- getFilename(fileId: number): string {
28
+ override getFilename(fileId: number): string {
29
29
  const pathname = this.mapIdToFile.get(fileId)?.pathname
30
30
  return `NodeFS:${pathname}`
31
31
  }
32
32
 
33
- jOpen(zName: string | null, fileId: number, flags: number, pOutFlags: DataView): number {
33
+ override jOpen(zName: string | null, fileId: number, flags: number, pOutFlags: DataView): number {
34
34
  try {
35
- const pathname = zName ? path.resolve(this.directory, zName) : Math.random().toString(36).slice(2)
35
+ const pathname =
36
+ zName !== null && zName !== '' ? path.resolve(this.directory, zName) : Math.random().toString(36).slice(2)
36
37
  const file: NodeFsFile = { pathname, flags, fileHandle: null }
37
38
  this.mapIdToFile.set(fileId, file)
38
39
 
@@ -41,11 +42,11 @@ export class NodeFS extends FacadeVFS {
41
42
 
42
43
  // Convert SQLite flags to Node.js flags
43
44
  let fsFlags = 'r'
44
- if (create && readwrite) {
45
+ if (create === true && readwrite === true) {
45
46
  // Check if file exists first
46
47
  const exists = fs.existsSync(pathname)
47
- fsFlags = exists ? 'r+' : 'w+' // Use r+ for existing files, w+ only for new files
48
- } else if (readwrite) {
48
+ fsFlags = exists === true ? 'r+' : 'w+' // Use r+ for existing files, w+ only for new files
49
+ } else if (readwrite === true) {
49
50
  fsFlags = 'r+' // Open file for reading and writing
50
51
  }
51
52
 
@@ -54,7 +55,7 @@ export class NodeFS extends FacadeVFS {
54
55
  pOutFlags.setInt32(0, flags, true)
55
56
  return VFS.SQLITE_OK
56
57
  } catch (err: any) {
57
- if (err.code === 'ENOENT' && !create) {
58
+ if (err.code === 'ENOENT' && create == null) {
58
59
  return VFS.SQLITE_CANTOPEN
59
60
  }
60
61
  throw err
@@ -65,10 +66,10 @@ export class NodeFS extends FacadeVFS {
65
66
  }
66
67
  }
67
68
 
68
- jRead(fileId: number, pData: Uint8Array<ArrayBuffer>, iOffset: number): number {
69
+ override jRead(fileId: number, pData: Uint8Array<ArrayBuffer>, iOffset: number): number {
69
70
  try {
70
71
  const file = this.mapIdToFile.get(fileId)
71
- if (!file?.fileHandle) return VFS.SQLITE_IOERR_READ
72
+ if (file?.fileHandle == null) return VFS.SQLITE_IOERR_READ
72
73
 
73
74
  // const view = new DataView(pData.buffer, pData.byteOffset, pData.length)
74
75
  // const bytesRead = fs.readSync(file.fileHandle, view, 0, pData.length, iOffset)
@@ -85,10 +86,10 @@ export class NodeFS extends FacadeVFS {
85
86
  }
86
87
  }
87
88
 
88
- jWrite(fileId: number, pData: Uint8Array<ArrayBuffer>, iOffset: number): number {
89
+ override jWrite(fileId: number, pData: Uint8Array<ArrayBuffer>, iOffset: number): number {
89
90
  try {
90
91
  const file = this.mapIdToFile.get(fileId)
91
- if (!file?.fileHandle) return VFS.SQLITE_IOERR_WRITE
92
+ if (file?.fileHandle == null) return VFS.SQLITE_IOERR_WRITE
92
93
 
93
94
  // const view = new DataView(pData.buffer, pData.byteOffset, pData.length)
94
95
  // fs.writeSync(file.fileHandle, view, 0, pData.length, iOffset)
@@ -100,17 +101,17 @@ export class NodeFS extends FacadeVFS {
100
101
  }
101
102
  }
102
103
 
103
- jClose(fileId: number): number {
104
+ override jClose(fileId: number): number {
104
105
  try {
105
106
  const file = this.mapIdToFile.get(fileId)
106
- if (!file) return VFS.SQLITE_OK
107
+ if (file == null) return VFS.SQLITE_OK
107
108
 
108
109
  this.mapIdToFile.delete(fileId)
109
110
  if (file.fileHandle !== null) {
110
111
  fs.closeSync(file.fileHandle)
111
112
  }
112
113
 
113
- if (file.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) {
114
+ if ((file.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) !== 0) {
114
115
  fs.unlinkSync(file.pathname)
115
116
  }
116
117
  return VFS.SQLITE_OK
@@ -120,10 +121,10 @@ export class NodeFS extends FacadeVFS {
120
121
  }
121
122
  }
122
123
 
123
- jFileSize(fileId: number, pSize64: DataView): number {
124
+ override jFileSize(fileId: number, pSize64: DataView): number {
124
125
  try {
125
126
  const file = this.mapIdToFile.get(fileId)
126
- if (!file?.fileHandle) return VFS.SQLITE_IOERR_FSTAT
127
+ if (file?.fileHandle == null) return VFS.SQLITE_IOERR_FSTAT
127
128
 
128
129
  const stats = fs.fstatSync(file.fileHandle)
129
130
  pSize64.setBigInt64(0, BigInt(stats.size), true)
@@ -134,10 +135,10 @@ export class NodeFS extends FacadeVFS {
134
135
  }
135
136
  }
136
137
 
137
- jTruncate(fileId: number, iSize: number): number {
138
+ override jTruncate(fileId: number, iSize: number): number {
138
139
  try {
139
140
  const file = this.mapIdToFile.get(fileId)
140
- if (!file?.fileHandle) return VFS.SQLITE_IOERR_TRUNCATE
141
+ if (file?.fileHandle == null) return VFS.SQLITE_IOERR_TRUNCATE
141
142
 
142
143
  fs.ftruncateSync(file.fileHandle, iSize)
143
144
  return VFS.SQLITE_OK
@@ -147,10 +148,10 @@ export class NodeFS extends FacadeVFS {
147
148
  }
148
149
  }
149
150
 
150
- jSync(fileId: number, _flags: number): number {
151
+ override jSync(fileId: number, _flags: number): number {
151
152
  try {
152
153
  const file = this.mapIdToFile.get(fileId)
153
- if (!file?.fileHandle) return VFS.SQLITE_OK
154
+ if (file?.fileHandle == null) return VFS.SQLITE_OK
154
155
 
155
156
  // TODO do this out of band (for now we disable it to speed up the node vfs)
156
157
  // fs.fsyncSync(file.fileHandle)
@@ -161,7 +162,7 @@ export class NodeFS extends FacadeVFS {
161
162
  }
162
163
  }
163
164
 
164
- jDelete(zName: string, _syncDir: number): number {
165
+ override jDelete(zName: string, _syncDir: number): number {
165
166
  try {
166
167
  const pathname = path.resolve(this.directory, zName)
167
168
  fs.unlinkSync(pathname)
@@ -172,11 +173,11 @@ export class NodeFS extends FacadeVFS {
172
173
  }
173
174
  }
174
175
 
175
- jAccess(zName: string, _flags: number, pResOut: DataView): number {
176
+ override jAccess(zName: string, _flags: number, pResOut: DataView): number {
176
177
  try {
177
178
  const pathname = path.resolve(this.directory, zName)
178
179
  const exists = fs.existsSync(pathname)
179
- pResOut.setInt32(0, exists ? 1 : 0, true)
180
+ pResOut.setInt32(0, exists === true ? 1 : 0, true)
180
181
  return VFS.SQLITE_OK
181
182
  } catch (e: any) {
182
183
  this.lastError = e
@@ -1,61 +0,0 @@
1
- import type { CfTypes } from '@livestore/common-cf';
2
- export interface BlockRange {
3
- startBlock: number;
4
- endBlock: number;
5
- startOffset: number;
6
- endOffset: number;
7
- }
8
- export interface BlockData {
9
- blockId: number;
10
- data: Uint8Array;
11
- }
12
- /**
13
- * BlockManager handles the conversion between file operations and block-based storage
14
- * for the CloudflareSqlVFS. It manages fixed-size blocks stored in SQL tables.
15
- */
16
- export declare class BlockManager {
17
- private readonly blockSize;
18
- constructor(blockSize?: number);
19
- /**
20
- * Calculate which blocks are needed for a given file operation
21
- */
22
- calculateBlockRange(offset: number, length: number): BlockRange;
23
- /**
24
- * Read blocks from SQL storage and return as a Map
25
- */
26
- readBlocks(sql: CfTypes.SqlStorage, filePath: string, blockIds: number[]): Map<number, Uint8Array>;
27
- /**
28
- * Write blocks to SQL storage using exec for now (prepared statements later)
29
- */
30
- writeBlocks(sql: CfTypes.SqlStorage, filePath: string, blocks: Map<number, Uint8Array>): void;
31
- /**
32
- * Delete blocks at or after the specified block ID (used for truncation)
33
- */
34
- deleteBlocksAfter(sql: CfTypes.SqlStorage, filePath: string, startBlockId: number): void;
35
- /**
36
- * Split write data into blocks, handling partial blocks at boundaries
37
- */
38
- splitIntoBlocks(data: Uint8Array, offset: number): Map<number, {
39
- blockId: number;
40
- blockOffset: number;
41
- data: Uint8Array;
42
- }>;
43
- /**
44
- * Assemble read data from blocks into a continuous buffer
45
- */
46
- assembleBlocks(blocks: Map<number, Uint8Array>, range: BlockRange, requestedLength: number): Uint8Array;
47
- /**
48
- * Handle partial block writes by reading existing block, modifying, and returning complete block
49
- */
50
- mergePartialBlock(sql: CfTypes.SqlStorage, filePath: string, blockId: number, blockOffset: number, newData: Uint8Array): Uint8Array;
51
- /**
52
- * Get statistics about block usage for a file
53
- */
54
- getBlockStats(sql: CfTypes.SqlStorage, filePath: string): {
55
- totalBlocks: number;
56
- storedBlocks: number;
57
- totalBytes: number;
58
- };
59
- getBlockSize(): number;
60
- }
61
- //# sourceMappingURL=BlockManager.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BlockManager.d.ts","sourceRoot":"","sources":["../../src/cf/BlockManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAEnD,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,UAAU,CAAA;CACjB;AAED;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;gBAEtB,SAAS,GAAE,MAAkB;IAIzC;;OAEG;IACH,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU;IAc/D;;OAEG;IACH,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC;IAyBlG;;OAEG;IACH,WAAW,CAAC,GAAG,EAAE,OAAO,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI;IAe7F;;OAEG;IACH,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAIxF;;OAEG;IACH,eAAe,CACb,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,GACb,GAAG,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,CAAC;IAyB1E;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,GAAG,UAAU;IA6BvG;;OAEG;IACH,iBAAiB,CACf,GAAG,EAAE,OAAO,CAAC,UAAU,EACvB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,UAAU,GAClB,UAAU;IAab;;OAEG;IACH,aAAa,CACX,GAAG,EAAE,OAAO,CAAC,UAAU,EACvB,QAAQ,EAAE,MAAM,GACf;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE;IAmCpE,YAAY,IAAI,MAAM;CAGvB"}
@@ -1,157 +0,0 @@
1
- /**
2
- * BlockManager handles the conversion between file operations and block-based storage
3
- * for the CloudflareSqlVFS. It manages fixed-size blocks stored in SQL tables.
4
- */
5
- export class BlockManager {
6
- blockSize;
7
- constructor(blockSize = 64 * 1024) {
8
- this.blockSize = blockSize;
9
- }
10
- /**
11
- * Calculate which blocks are needed for a given file operation
12
- */
13
- calculateBlockRange(offset, length) {
14
- const startBlock = Math.floor(offset / this.blockSize);
15
- const endBlock = Math.floor((offset + length - 1) / this.blockSize);
16
- const startOffset = offset % this.blockSize;
17
- const endOffset = ((offset + length - 1) % this.blockSize) + 1;
18
- return {
19
- startBlock,
20
- endBlock,
21
- startOffset,
22
- endOffset,
23
- };
24
- }
25
- /**
26
- * Read blocks from SQL storage and return as a Map
27
- */
28
- readBlocks(sql, filePath, blockIds) {
29
- const blocks = new Map();
30
- if (blockIds.length === 0) {
31
- return blocks;
32
- }
33
- // Build IN clause for efficient querying
34
- const placeholders = blockIds.map(() => '?').join(',');
35
- const query = `
36
- SELECT block_id, block_data
37
- FROM vfs_blocks
38
- WHERE file_path = ? AND block_id IN (${placeholders})
39
- ORDER BY block_id
40
- `;
41
- const cursor = sql.exec(query, filePath, ...blockIds);
42
- for (const row of cursor) {
43
- blocks.set(row.block_id, new Uint8Array(row.block_data));
44
- }
45
- return blocks;
46
- }
47
- /**
48
- * Write blocks to SQL storage using exec for now (prepared statements later)
49
- */
50
- writeBlocks(sql, filePath, blocks) {
51
- if (blocks.size === 0) {
52
- return;
53
- }
54
- for (const [blockId, data] of blocks) {
55
- sql.exec('INSERT OR REPLACE INTO vfs_blocks (file_path, block_id, block_data) VALUES (?, ?, ?)', filePath, blockId, data);
56
- }
57
- }
58
- /**
59
- * Delete blocks at or after the specified block ID (used for truncation)
60
- */
61
- deleteBlocksAfter(sql, filePath, startBlockId) {
62
- sql.exec('DELETE FROM vfs_blocks WHERE file_path = ? AND block_id >= ?', filePath, startBlockId);
63
- }
64
- /**
65
- * Split write data into blocks, handling partial blocks at boundaries
66
- */
67
- splitIntoBlocks(data, offset) {
68
- const blocks = new Map();
69
- let remainingData = data;
70
- let currentOffset = offset;
71
- while (remainingData.length > 0) {
72
- const blockId = Math.floor(currentOffset / this.blockSize);
73
- const blockOffset = currentOffset % this.blockSize;
74
- const bytesToWrite = Math.min(remainingData.length, this.blockSize - blockOffset);
75
- const blockData = remainingData.slice(0, bytesToWrite);
76
- blocks.set(blockId, {
77
- blockId,
78
- blockOffset,
79
- data: blockData,
80
- });
81
- remainingData = remainingData.slice(bytesToWrite);
82
- currentOffset += bytesToWrite;
83
- }
84
- return blocks;
85
- }
86
- /**
87
- * Assemble read data from blocks into a continuous buffer
88
- */
89
- assembleBlocks(blocks, range, requestedLength) {
90
- const result = new Uint8Array(requestedLength);
91
- let resultOffset = 0;
92
- for (let blockId = range.startBlock; blockId <= range.endBlock; blockId++) {
93
- const blockData = blocks.get(blockId);
94
- if (!blockData) {
95
- // Block not found - fill with zeros (sparse file behavior)
96
- const zeroLength = Math.min(this.blockSize, requestedLength - resultOffset);
97
- // result is already zero-filled by default
98
- resultOffset += zeroLength;
99
- continue;
100
- }
101
- // Calculate the slice of this block we need
102
- const blockStartOffset = blockId === range.startBlock ? range.startOffset : 0;
103
- const blockEndOffset = blockId === range.endBlock ? range.endOffset : blockData.length;
104
- const sliceLength = blockEndOffset - blockStartOffset;
105
- if (sliceLength > 0) {
106
- const slice = blockData.slice(blockStartOffset, blockEndOffset);
107
- result.set(slice, resultOffset);
108
- resultOffset += sliceLength;
109
- }
110
- }
111
- return result;
112
- }
113
- /**
114
- * Handle partial block writes by reading existing block, modifying, and returning complete block
115
- */
116
- mergePartialBlock(sql, filePath, blockId, blockOffset, newData) {
117
- // Read existing block data if it exists
118
- const existingBlocks = this.readBlocks(sql, filePath, [blockId]);
119
- const existingBlock = existingBlocks.get(blockId) || new Uint8Array(this.blockSize);
120
- // Create a new block with the merged data
121
- const mergedBlock = new Uint8Array(this.blockSize);
122
- mergedBlock.set(existingBlock);
123
- mergedBlock.set(newData, blockOffset);
124
- return mergedBlock;
125
- }
126
- /**
127
- * Get statistics about block usage for a file
128
- */
129
- getBlockStats(sql, filePath) {
130
- const blockStatsCursor = sql.exec(`SELECT
131
- COUNT(*) as stored_blocks,
132
- COALESCE(SUM(LENGTH(block_data)), 0) as total_bytes
133
- FROM vfs_blocks
134
- WHERE file_path = ?`, filePath);
135
- const result = blockStatsCursor.one();
136
- // Get file size to calculate theoretical total blocks
137
- const fileSizeCursor = sql.exec('SELECT file_size FROM vfs_files WHERE file_path = ?', filePath);
138
- let fileSize = 0;
139
- try {
140
- const fileSizeResult = fileSizeCursor.one();
141
- fileSize = fileSizeResult.file_size;
142
- }
143
- catch {
144
- // File doesn't exist
145
- }
146
- const totalBlocks = Math.ceil(fileSize / this.blockSize);
147
- return {
148
- totalBlocks,
149
- storedBlocks: result.stored_blocks,
150
- totalBytes: result.total_bytes,
151
- };
152
- }
153
- getBlockSize() {
154
- return this.blockSize;
155
- }
156
- }
157
- //# sourceMappingURL=BlockManager.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BlockManager.js","sourceRoot":"","sources":["../../src/cf/BlockManager.ts"],"names":[],"mappings":"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"}
@@ -1,51 +0,0 @@
1
- import type { CfTypes } from '@livestore/common-cf';
2
- import { FacadeVFS } from '../FacadeVFS.ts';
3
- export interface SqlVfsOptions {
4
- maxFiles?: number;
5
- blockSize?: number;
6
- }
7
- /**
8
- * VFS implementation using Cloudflare Durable Object SQL storage as the backend.
9
- * This provides a synchronous VFS interface by leveraging SQL's synchronous API.
10
- *
11
- * Storage Strategy:
12
- * - Files are stored as blocks in SQL tables for efficient I/O
13
- * - File metadata stored in vfs_files table
14
- * - File data stored as fixed-size blocks in vfs_blocks table
15
- * - Synchronous operations via SQL's synchronous API
16
- *
17
- * Key advantages over async VFS:
18
- * - No async/await complexity
19
- * - Native SQL ACID properties
20
- * - Efficient range queries for file operations
21
- * - Built-in consistency and durability
22
- */
23
- export declare class CloudflareSqlVFS extends FacadeVFS {
24
- #private;
25
- log: null;
26
- static create(name: string, sql: CfTypes.SqlStorage, module: any, options?: SqlVfsOptions): Promise<CloudflareSqlVFS>;
27
- constructor(name: string, sql: CfTypes.SqlStorage, module: any, options?: SqlVfsOptions);
28
- /**
29
- * Initialize the VFS by setting up SQL schema
30
- */
31
- isReady(): Promise<boolean>;
32
- jOpen(path: string, fileId: number, flags: number, pOutFlags: DataView): number;
33
- jClose(fileId: number): number;
34
- jRead(fileId: number, buffer: Uint8Array, offset: number): number;
35
- jWrite(fileId: number, data: Uint8Array, offset: number): number;
36
- jTruncate(fileId: number, size: number): number;
37
- jSync(fileId: number, _flags: number): number;
38
- jFileSize(fileId: number, pSize64: DataView): number;
39
- jDelete(path: string, _syncDir: number): number;
40
- jAccess(path: string, _flags: number, pResOut: DataView): number;
41
- jSectorSize(_fileId: number): number;
42
- jDeviceCharacteristics(_fileId: number): number;
43
- getStats(): {
44
- activeFiles: number;
45
- openFiles: number;
46
- maxFiles: number;
47
- blockSize: number;
48
- totalStoredBytes: number;
49
- };
50
- }
51
- //# sourceMappingURL=CloudflareSqlVFS.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CloudflareSqlVFS.d.ts","sourceRoot":"","sources":["../../src/cf/CloudflareSqlVFS.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AA6B3C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,gBAAiB,SAAQ,SAAS;;IAC7C,GAAG,OAAO;WAUG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAE,aAAkB;gBAMvF,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAE,aAAkB;IAO3F;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IA2FjC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,GAAG,MAAM;IAqD/E,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAK9B,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAwBjE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAgDhE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAuC/C,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAS7C,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,MAAM;IAepD,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAU/C,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,MAAM;IAWhE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAIpC,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IA+B/C,QAAQ,IAAI;QACV,WAAW,EAAE,MAAM,CAAA;QACnB,SAAS,EAAE,MAAM,CAAA;QACjB,QAAQ,EAAE,MAAM,CAAA;QAChB,SAAS,EAAE,MAAM,CAAA;QACjB,gBAAgB,EAAE,MAAM,CAAA;KACzB;CAwBF"}