@livestore/sqlite-wasm 0.4.0-dev.22 → 0.4.0-dev.24
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/README.md +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/FacadeVFS.d.ts +0 -1
- package/dist/FacadeVFS.d.ts.map +1 -1
- package/dist/FacadeVFS.js +9 -14
- package/dist/FacadeVFS.js.map +1 -1
- package/dist/browser/mod.d.ts +2 -2
- package/dist/browser/mod.d.ts.map +1 -1
- package/dist/browser/mod.js +2 -2
- package/dist/browser/mod.js.map +1 -1
- package/dist/browser/opfs/AccessHandlePoolVFS.d.ts.map +1 -1
- package/dist/browser/opfs/AccessHandlePoolVFS.js +15 -13
- package/dist/browser/opfs/AccessHandlePoolVFS.js.map +1 -1
- package/dist/browser/opfs/opfs-sah-pool.js +3 -3
- package/dist/browser/opfs/opfs-sah-pool.js.map +1 -1
- package/dist/cf/CloudflareDurableObjectVFS.d.ts +104 -0
- package/dist/cf/CloudflareDurableObjectVFS.d.ts.map +1 -0
- package/dist/cf/CloudflareDurableObjectVFS.js +281 -0
- package/dist/cf/CloudflareDurableObjectVFS.js.map +1 -0
- package/dist/cf/CloudflareWorkerVFS.d.ts.map +1 -1
- package/dist/cf/CloudflareWorkerVFS.js +29 -28
- package/dist/cf/CloudflareWorkerVFS.js.map +1 -1
- package/dist/cf/mod.d.ts +3 -4
- package/dist/cf/mod.d.ts.map +1 -1
- package/dist/cf/mod.js +4 -12
- package/dist/cf/mod.js.map +1 -1
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js +5 -4
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.js.map +1 -1
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js +3 -3
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-core.test.js.map +1 -1
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js +3 -3
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-integration.test.js.map +1 -1
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js +3 -3
- package/dist/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.js.map +1 -1
- package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js +194 -179
- package/dist/cf/test/sql/cloudflare-sql-vfs-core.test.js.map +1 -1
- package/dist/in-memory-vfs.d.ts.map +1 -1
- package/dist/in-memory-vfs.js +0 -1
- package/dist/in-memory-vfs.js.map +1 -1
- package/dist/load-wasm/mod.node.js +1 -1
- package/dist/load-wasm/mod.node.js.map +1 -1
- package/dist/load-wasm/mod.workerd.d.ts.map +1 -1
- package/dist/load-wasm/mod.workerd.js.map +1 -1
- package/dist/make-sqlite-db.d.ts.map +1 -1
- package/dist/make-sqlite-db.js +16 -4
- package/dist/make-sqlite-db.js.map +1 -1
- package/dist/node/NodeFS.d.ts.map +1 -1
- package/dist/node/NodeFS.js +13 -13
- package/dist/node/NodeFS.js.map +1 -1
- package/package.json +54 -15
- package/src/FacadeVFS.ts +9 -14
- package/src/browser/mod.ts +1 -1
- package/src/browser/opfs/AccessHandlePoolVFS.ts +33 -25
- package/src/browser/opfs/opfs-sah-pool.ts +3 -3
- package/src/cf/CloudflareDurableObjectVFS.ts +325 -0
- package/src/cf/CloudflareWorkerVFS.ts +41 -39
- package/src/cf/README.md +3 -3
- package/src/cf/mod.ts +10 -15
- package/src/cf/test/README.md +55 -26
- package/src/cf/test/async-storage/cloudflare-worker-vfs-advanced.test.ts +6 -4
- package/src/cf/test/async-storage/cloudflare-worker-vfs-core.test.ts +4 -3
- package/src/cf/test/async-storage/cloudflare-worker-vfs-integration.test.ts +4 -3
- package/src/cf/test/async-storage/cloudflare-worker-vfs-reliability.test.ts +4 -3
- package/src/cf/test/sql/cloudflare-sql-vfs-core.test.ts +228 -197
- package/src/in-memory-vfs.ts +0 -1
- package/src/load-wasm/mod.node.ts +1 -1
- package/src/load-wasm/mod.workerd.ts +0 -1
- package/src/make-sqlite-db.ts +24 -4
- package/src/node/NodeFS.ts +24 -23
- package/dist/cf/BlockManager.d.ts +0 -61
- package/dist/cf/BlockManager.d.ts.map +0 -1
- package/dist/cf/BlockManager.js +0 -157
- package/dist/cf/BlockManager.js.map +0 -1
- package/dist/cf/CloudflareSqlVFS.d.ts +0 -51
- package/dist/cf/CloudflareSqlVFS.d.ts.map +0 -1
- package/dist/cf/CloudflareSqlVFS.js +0 -351
- package/dist/cf/CloudflareSqlVFS.js.map +0 -1
- package/src/cf/BlockManager.ts +0 -225
- package/src/cf/CloudflareSqlVFS.ts +0 -450
package/src/make-sqlite-db.ts
CHANGED
|
@@ -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(
|
|
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
|
package/src/node/NodeFS.ts
CHANGED
|
@@ -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 =
|
|
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' &&
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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"}
|
package/dist/cf/BlockManager.js
DELETED
|
@@ -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"}
|