@livestore/sqlite-wasm 0.0.0-snapshot-1d99fea7d2ce2c7a5d9ed0a3752f8a7bda6bc3db

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 (69) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/FacadeVFS.d.ts +243 -0
  3. package/dist/FacadeVFS.d.ts.map +1 -0
  4. package/dist/FacadeVFS.js +474 -0
  5. package/dist/FacadeVFS.js.map +1 -0
  6. package/dist/browser/mod.d.ts +44 -0
  7. package/dist/browser/mod.d.ts.map +1 -0
  8. package/dist/browser/mod.js +51 -0
  9. package/dist/browser/mod.js.map +1 -0
  10. package/dist/browser/opfs/AccessHandlePoolVFS.d.ts +47 -0
  11. package/dist/browser/opfs/AccessHandlePoolVFS.d.ts.map +1 -0
  12. package/dist/browser/opfs/AccessHandlePoolVFS.js +355 -0
  13. package/dist/browser/opfs/AccessHandlePoolVFS.js.map +1 -0
  14. package/dist/browser/opfs/index.d.ts +12 -0
  15. package/dist/browser/opfs/index.d.ts.map +1 -0
  16. package/dist/browser/opfs/index.js +19 -0
  17. package/dist/browser/opfs/index.js.map +1 -0
  18. package/dist/browser/opfs/opfs-sah-pool.d.ts +3 -0
  19. package/dist/browser/opfs/opfs-sah-pool.d.ts.map +1 -0
  20. package/dist/browser/opfs/opfs-sah-pool.js +55 -0
  21. package/dist/browser/opfs/opfs-sah-pool.js.map +1 -0
  22. package/dist/in-memory-vfs.d.ts +7 -0
  23. package/dist/in-memory-vfs.d.ts.map +1 -0
  24. package/dist/in-memory-vfs.js +15 -0
  25. package/dist/in-memory-vfs.js.map +1 -0
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +2 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/index_.d.ts +3 -0
  31. package/dist/index_.d.ts.map +1 -0
  32. package/dist/index_.js +3 -0
  33. package/dist/index_.js.map +1 -0
  34. package/dist/load-wasm/mod.browser.d.ts +2 -0
  35. package/dist/load-wasm/mod.browser.d.ts.map +1 -0
  36. package/dist/load-wasm/mod.browser.js +12 -0
  37. package/dist/load-wasm/mod.browser.js.map +1 -0
  38. package/dist/load-wasm/mod.node.d.ts +2 -0
  39. package/dist/load-wasm/mod.node.d.ts.map +1 -0
  40. package/dist/load-wasm/mod.node.js +13 -0
  41. package/dist/load-wasm/mod.node.js.map +1 -0
  42. package/dist/make-sqlite-db.d.ts +11 -0
  43. package/dist/make-sqlite-db.d.ts.map +1 -0
  44. package/dist/make-sqlite-db.js +181 -0
  45. package/dist/make-sqlite-db.js.map +1 -0
  46. package/dist/node/NodeFS.d.ts +20 -0
  47. package/dist/node/NodeFS.d.ts.map +1 -0
  48. package/dist/node/NodeFS.js +174 -0
  49. package/dist/node/NodeFS.js.map +1 -0
  50. package/dist/node/mod.d.ts +41 -0
  51. package/dist/node/mod.d.ts.map +1 -0
  52. package/dist/node/mod.js +61 -0
  53. package/dist/node/mod.js.map +1 -0
  54. package/package.json +38 -0
  55. package/src/FacadeVFS.ts +510 -0
  56. package/src/ambient.d.ts +18 -0
  57. package/src/browser/mod.ts +109 -0
  58. package/src/browser/opfs/AccessHandlePoolVFS.ts +404 -0
  59. package/src/browser/opfs/index.ts +35 -0
  60. package/src/browser/opfs/opfs-sah-pool.ts +68 -0
  61. package/src/in-memory-vfs.ts +20 -0
  62. package/src/index.ts +1 -0
  63. package/src/index_.ts +2 -0
  64. package/src/load-wasm/mod.browser.ts +12 -0
  65. package/src/load-wasm/mod.node.ts +13 -0
  66. package/src/make-sqlite-db.ts +220 -0
  67. package/src/node/NodeFS.ts +190 -0
  68. package/src/node/mod.ts +132 -0
  69. package/tsconfig.json +10 -0
@@ -0,0 +1,510 @@
1
+ // Based on https://github.com/rhashimoto/wa-sqlite/blob/master/src/FacadeVFS.js
2
+
3
+ /* eslint-disable unicorn/prefer-code-point */
4
+ /* eslint-disable @typescript-eslint/no-unused-vars */
5
+ /* eslint-disable prefer-arrow/prefer-arrow-functions */
6
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
7
+ // @ts-nocheck
8
+ import * as VFS from '@livestore/wa-sqlite/src/VFS.js'
9
+
10
+ const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor
11
+
12
+ export class FacadeVFS extends VFS.Base {
13
+ /**
14
+ * @param {string} name
15
+ * @param {object} module
16
+ */
17
+ constructor(name, module) {
18
+ super(name, module)
19
+ }
20
+
21
+ /**
22
+ * Override to indicate which methods are asynchronous.
23
+ * @param {string} methodName
24
+ * @returns {boolean}
25
+ */
26
+ hasAsyncMethod(methodName) {
27
+ // The input argument is a string like "xOpen", so convert to "jOpen".
28
+ // Then check if the method exists and is async.
29
+ const jMethodName = `j${methodName.slice(1)}`
30
+ return this[jMethodName] instanceof AsyncFunction
31
+ }
32
+
33
+ /**
34
+ * Return the filename for a file id for use by mixins.
35
+ * @param {number} pFile
36
+ * @returns {string}
37
+ */
38
+ getFilename(pFile) {
39
+ throw new Error('unimplemented')
40
+ }
41
+
42
+ /**
43
+ * @param {string?} filename
44
+ * @param {number} pFile
45
+ * @param {number} flags
46
+ * @param {DataView} pOutFlags
47
+ * @returns {number|Promise<number>}
48
+ */
49
+ jOpen(filename, pFile, flags, pOutFlags): number {
50
+ return VFS.SQLITE_CANTOPEN
51
+ }
52
+
53
+ /**
54
+ * @param {string} filename
55
+ * @param {number} syncDir
56
+ * @returns {number|Promise<number>}
57
+ */
58
+ jDelete(filename, syncDir): number {
59
+ return VFS.SQLITE_OK
60
+ }
61
+
62
+ /**
63
+ * @param {string} filename
64
+ * @param {number} flags
65
+ * @param {DataView} pResOut
66
+ * @returns {number|Promise<number>}
67
+ */
68
+ jAccess(filename, flags, pResOut): number {
69
+ return VFS.SQLITE_OK
70
+ }
71
+
72
+ /**
73
+ * @param {string} filename
74
+ * @param {Uint8Array} zOut
75
+ * @returns {number|Promise<number>}
76
+ */
77
+ jFullPathname(filename, zOut): number {
78
+ // Copy the filename to the output buffer.
79
+ const { read, written } = new TextEncoder().encodeInto(filename, zOut)
80
+ if (read < filename.length) return VFS.SQLITE_IOERR
81
+ if (written >= zOut.length) return VFS.SQLITE_IOERR
82
+ zOut[written] = 0
83
+ return VFS.SQLITE_OK
84
+ }
85
+
86
+ /**
87
+ * @param {Uint8Array} zBuf
88
+ * @returns {number|Promise<number>}
89
+ */
90
+ jGetLastError(zBuf) {
91
+ return VFS.SQLITE_OK
92
+ }
93
+
94
+ /**
95
+ * @param {number} pFile
96
+ * @returns {number|Promise<number>}
97
+ */
98
+ jClose(pFile): number {
99
+ return VFS.SQLITE_OK
100
+ }
101
+
102
+ /**
103
+ * @param {number} pFile
104
+ * @param {Uint8Array} pData
105
+ * @param {number} iOffset
106
+ * @returns {number|Promise<number>}
107
+ */
108
+ jRead(pFile, pData, iOffset): number {
109
+ pData.fill(0)
110
+ return VFS.SQLITE_IOERR_SHORT_READ
111
+ }
112
+
113
+ /**
114
+ * @param {number} pFile
115
+ * @param {Uint8Array} pData
116
+ * @param {number} iOffset
117
+ * @returns {number|Promise<number>}
118
+ */
119
+ jWrite(pFile, pData, iOffset): number {
120
+ return VFS.SQLITE_IOERR_WRITE
121
+ }
122
+
123
+ /**
124
+ * @param {number} pFile
125
+ * @param {number} size
126
+ * @returns {number|Promise<number>}
127
+ */
128
+ jTruncate(pFile, size): number {
129
+ return VFS.SQLITE_OK
130
+ }
131
+
132
+ /**
133
+ * @param {number} pFile
134
+ * @param {number} flags
135
+ * @returns {number|Promise<number>}
136
+ */
137
+ jSync(pFile, flags): number {
138
+ return VFS.SQLITE_OK
139
+ }
140
+
141
+ /**
142
+ * @param {number} pFile
143
+ * @param {DataView} pSize
144
+ * @returns {number|Promise<number>}
145
+ */
146
+ jFileSize(pFile, pSize): number {
147
+ return VFS.SQLITE_OK
148
+ }
149
+
150
+ /**
151
+ * @param {number} pFile
152
+ * @param {number} lockType
153
+ * @returns {number|Promise<number>}
154
+ */
155
+ jLock(pFile, lockType): number {
156
+ return VFS.SQLITE_OK
157
+ }
158
+
159
+ /**
160
+ * @param {number} pFile
161
+ * @param {number} lockType
162
+ * @returns {number|Promise<number>}
163
+ */
164
+ jUnlock(pFile, lockType): number {
165
+ return VFS.SQLITE_OK
166
+ }
167
+
168
+ /**
169
+ * @param {number} pFile
170
+ * @param {DataView} pResOut
171
+ * @returns {number|Promise<number>}
172
+ */
173
+ jCheckReservedLock(pFile, pResOut): number {
174
+ pResOut.setInt32(0, 0, true)
175
+ return VFS.SQLITE_OK
176
+ }
177
+
178
+ /**
179
+ * @param {number} pFile
180
+ * @param {number} op
181
+ * @param {DataView} pArg
182
+ * @returns {number|Promise<number>}
183
+ */
184
+ jFileControl(pFile, op, pArg): number {
185
+ return VFS.SQLITE_NOTFOUND
186
+ }
187
+
188
+ /**
189
+ * @param {number} pFile
190
+ * @returns {number|Promise<number>}
191
+ */
192
+ jSectorSize(pFile): number {
193
+ return super.xSectorSize(pFile)
194
+ }
195
+
196
+ /**
197
+ * @param {number} pFile
198
+ * @returns {number|Promise<number>}
199
+ */
200
+ jDeviceCharacteristics(pFile): number {
201
+ return 0
202
+ }
203
+
204
+ /**
205
+ * @param {number} pVfs
206
+ * @param {number} zName
207
+ * @param {number} pFile
208
+ * @param {number} flags
209
+ * @param {number} pOutFlags
210
+ * @returns {number|Promise<number>}
211
+ */
212
+ xOpen(pVfs, zName, pFile, flags, pOutFlags): number {
213
+ const filename = this.#decodeFilename(zName, flags)
214
+ const pOutFlagsView = this.#makeTypedDataView('Int32', pOutFlags)
215
+ this['log']?.('jOpen', filename, pFile, '0x' + flags.toString(16))
216
+ return this.jOpen(filename, pFile, flags, pOutFlagsView)
217
+ }
218
+
219
+ /**
220
+ * @param {number} pVfs
221
+ * @param {number} zName
222
+ * @param {number} syncDir
223
+ * @returns {number|Promise<number>}
224
+ */
225
+ xDelete(pVfs, zName, syncDir) {
226
+ const filename = this._module.UTF8ToString(zName)
227
+ this['log']?.('jDelete', filename, syncDir)
228
+ return this.jDelete(filename, syncDir)
229
+ }
230
+
231
+ /**
232
+ * @param {number} pVfs
233
+ * @param {number} zName
234
+ * @param {number} flags
235
+ * @param {number} pResOut
236
+ * @returns {number|Promise<number>}
237
+ */
238
+ xAccess(pVfs, zName, flags, pResOut) {
239
+ const filename = this._module.UTF8ToString(zName)
240
+ const pResOutView = this.#makeTypedDataView('Int32', pResOut)
241
+ this['log']?.('jAccess', filename, flags)
242
+ return this.jAccess(filename, flags, pResOutView)
243
+ }
244
+
245
+ /**
246
+ * @param {number} pVfs
247
+ * @param {number} zName
248
+ * @param {number} nOut
249
+ * @param {number} zOut
250
+ * @returns {number|Promise<number>}
251
+ */
252
+ xFullPathname(pVfs, zName, nOut, zOut) {
253
+ const filename = this._module.UTF8ToString(zName)
254
+ const zOutArray = this._module.HEAPU8.subarray(zOut, zOut + nOut)
255
+ this['log']?.('jFullPathname', filename, nOut)
256
+ return this.jFullPathname(filename, zOutArray)
257
+ }
258
+
259
+ /**
260
+ * @param {number} pVfs
261
+ * @param {number} nBuf
262
+ * @param {number} zBuf
263
+ * @returns {number|Promise<number>}
264
+ */
265
+ xGetLastError(pVfs, nBuf, zBuf) {
266
+ const zBufArray = this._module.HEAPU8.subarray(zBuf, zBuf + nBuf)
267
+ this['log']?.('jGetLastError', nBuf)
268
+ return this.jGetLastError(zBufArray)
269
+ }
270
+
271
+ /**
272
+ * @param {number} pFile
273
+ * @returns {number|Promise<number>}
274
+ */
275
+ xClose(pFile) {
276
+ this['log']?.('jClose', pFile)
277
+ return this.jClose(pFile)
278
+ }
279
+
280
+ /**
281
+ * @param {number} pFile
282
+ * @param {number} pData
283
+ * @param {number} iAmt
284
+ * @param {number} iOffsetLo
285
+ * @param {number} iOffsetHi
286
+ * @returns {number|Promise<number>}
287
+ */
288
+ xRead(pFile, pData, iAmt, iOffsetLo, iOffsetHi) {
289
+ const pDataArray = this.#makeDataArray(pData, iAmt)
290
+ const iOffset = delegalize(iOffsetLo, iOffsetHi)
291
+ this['log']?.('jRead', pFile, iAmt, iOffset)
292
+ return this.jRead(pFile, pDataArray, iOffset)
293
+ }
294
+
295
+ /**
296
+ * @param {number} pFile
297
+ * @param {number} pData
298
+ * @param {number} iAmt
299
+ * @param {number} iOffsetLo
300
+ * @param {number} iOffsetHi
301
+ * @returns {number|Promise<number>}
302
+ */
303
+ xWrite(pFile, pData, iAmt, iOffsetLo, iOffsetHi) {
304
+ const pDataArray = this.#makeDataArray(pData, iAmt)
305
+ const iOffset = delegalize(iOffsetLo, iOffsetHi)
306
+ this['log']?.('jWrite', pFile, pDataArray, iOffset)
307
+ return this.jWrite(pFile, pDataArray, iOffset)
308
+ }
309
+
310
+ /**
311
+ * @param {number} pFile
312
+ * @param {number} sizeLo
313
+ * @param {number} sizeHi
314
+ * @returns {number|Promise<number>}
315
+ */
316
+ xTruncate(pFile, sizeLo, sizeHi) {
317
+ const size = delegalize(sizeLo, sizeHi)
318
+ this['log']?.('jTruncate', pFile, size)
319
+ return this.jTruncate(pFile, size)
320
+ }
321
+
322
+ /**
323
+ * @param {number} pFile
324
+ * @param {number} flags
325
+ * @returns {number|Promise<number>}
326
+ */
327
+ xSync(pFile, flags) {
328
+ this['log']?.('jSync', pFile, flags)
329
+ return this.jSync(pFile, flags)
330
+ }
331
+
332
+ /**
333
+ *
334
+ * @param {number} pFile
335
+ * @param {number} pSize
336
+ * @returns {number|Promise<number>}
337
+ */
338
+ xFileSize(pFile, pSize) {
339
+ const pSizeView = this.#makeTypedDataView('BigInt64', pSize)
340
+ this['log']?.('jFileSize', pFile)
341
+ return this.jFileSize(pFile, pSizeView)
342
+ }
343
+
344
+ /**
345
+ * @param {number} pFile
346
+ * @param {number} lockType
347
+ * @returns {number|Promise<number>}
348
+ */
349
+ xLock(pFile, lockType) {
350
+ this['log']?.('jLock', pFile, lockType)
351
+ return this.jLock(pFile, lockType)
352
+ }
353
+
354
+ /**
355
+ * @param {number} pFile
356
+ * @param {number} lockType
357
+ * @returns {number|Promise<number>}
358
+ */
359
+ xUnlock(pFile, lockType) {
360
+ this['log']?.('jUnlock', pFile, lockType)
361
+ return this.jUnlock(pFile, lockType)
362
+ }
363
+
364
+ /**
365
+ * @param {number} pFile
366
+ * @param {number} pResOut
367
+ * @returns {number|Promise<number>}
368
+ */
369
+ xCheckReservedLock(pFile, pResOut) {
370
+ const pResOutView = this.#makeTypedDataView('Int32', pResOut)
371
+ this['log']?.('jCheckReservedLock', pFile)
372
+ return this.jCheckReservedLock(pFile, pResOutView)
373
+ }
374
+
375
+ /**
376
+ * @param {number} pFile
377
+ * @param {number} op
378
+ * @param {number} pArg
379
+ * @returns {number|Promise<number>}
380
+ */
381
+ xFileControl(pFile, op, pArg) {
382
+ const pArgView = new DataView(this._module.HEAPU8.buffer, this._module.HEAPU8.byteOffset + pArg)
383
+ this['log']?.('jFileControl', pFile, op, pArgView)
384
+ return this.jFileControl(pFile, op, pArgView)
385
+ }
386
+
387
+ /**
388
+ * @param {number} pFile
389
+ * @returns {number|Promise<number>}
390
+ */
391
+ xSectorSize(pFile) {
392
+ this['log']?.('jSectorSize', pFile)
393
+ return this.jSectorSize(pFile)
394
+ }
395
+
396
+ /**
397
+ * @param {number} pFile
398
+ * @returns {number|Promise<number>}
399
+ */
400
+ xDeviceCharacteristics(pFile) {
401
+ this['log']?.('jDeviceCharacteristics', pFile)
402
+ return this.jDeviceCharacteristics(pFile)
403
+ }
404
+
405
+ /**
406
+ * Wrapped DataView for pointer arguments.
407
+ * Pointers to a single value are passed using DataView. A Proxy
408
+ * wrapper prevents use of incorrect type or endianness.
409
+ * @param {'Int32'|'BigInt64'} type
410
+ * @param {number} byteOffset
411
+ * @returns {DataView}
412
+ */
413
+ #makeTypedDataView(type, byteOffset) {
414
+ const byteLength = type === 'Int32' ? 4 : 8
415
+ const getter = `get${type}`
416
+ const setter = `set${type}`
417
+ const makeDataView = () =>
418
+ new DataView(this._module.HEAPU8.buffer, this._module.HEAPU8.byteOffset + byteOffset, byteLength)
419
+ let dataView = makeDataView()
420
+ return new Proxy(dataView, {
421
+ get(_, prop) {
422
+ if (dataView.buffer.byteLength === 0) {
423
+ // WebAssembly memory resize detached the buffer.
424
+ dataView = makeDataView()
425
+ }
426
+ if (prop === getter) {
427
+ return function (byteOffset, littleEndian) {
428
+ if (!littleEndian) throw new Error('must be little endian')
429
+ return dataView[prop](byteOffset, littleEndian)
430
+ }
431
+ }
432
+ if (prop === setter) {
433
+ return function (byteOffset, value, littleEndian) {
434
+ if (!littleEndian) throw new Error('must be little endian')
435
+ return dataView[prop](byteOffset, value, littleEndian)
436
+ }
437
+ }
438
+ if (typeof prop === 'string' && /^(get)|(set)/.test(prop)) {
439
+ throw new Error('invalid type')
440
+ }
441
+ const result = dataView[prop]
442
+ return typeof result === 'function' ? result.bind(dataView) : result
443
+ },
444
+ })
445
+ }
446
+
447
+ /**
448
+ * @param {number} byteOffset
449
+ * @param {number} byteLength
450
+ */
451
+ #makeDataArray(byteOffset, byteLength) {
452
+ let target = this._module.HEAPU8.subarray(byteOffset, byteOffset + byteLength)
453
+ return new Proxy(target, {
454
+ get: (_, prop, receiver) => {
455
+ if (target.buffer.byteLength === 0) {
456
+ // WebAssembly memory resize detached the buffer.
457
+ target = this._module.HEAPU8.subarray(byteOffset, byteOffset + byteLength)
458
+ }
459
+ const result = target[prop]
460
+ return typeof result === 'function' ? result.bind(target) : result
461
+ },
462
+ })
463
+ }
464
+
465
+ #decodeFilename(zName, flags) {
466
+ if (flags & VFS.SQLITE_OPEN_URI) {
467
+ // The first null-terminated string is the URI path. Subsequent
468
+ // strings are query parameter keys and values.
469
+ // https://www.sqlite.org/c3ref/open.html#urifilenamesinsqlite3open
470
+ let pName = zName
471
+ let state = 1
472
+ const charCodes = []
473
+ while (state) {
474
+ const charCode = this._module.HEAPU8[pName++]
475
+ if (charCode) {
476
+ charCodes.push(charCode)
477
+ } else {
478
+ if (!this._module.HEAPU8[pName]) state = null
479
+ switch (state) {
480
+ case 1: {
481
+ // path
482
+ charCodes.push('?'.charCodeAt(0))
483
+ state = 2
484
+ break
485
+ }
486
+ case 2: {
487
+ // key
488
+ charCodes.push('='.charCodeAt(0))
489
+ state = 3
490
+ break
491
+ }
492
+ case 3: {
493
+ // value
494
+ charCodes.push('&'.charCodeAt(0))
495
+ state = 2
496
+ break
497
+ }
498
+ }
499
+ }
500
+ }
501
+ return new TextDecoder().decode(new Uint8Array(charCodes))
502
+ }
503
+ return zName ? this._module.UTF8ToString(zName) : null
504
+ }
505
+ }
506
+ // Emscripten "legalizes" 64-bit integer arguments by passing them as
507
+ // two 32-bit signed integers.
508
+ function delegalize(lo32, hi32) {
509
+ return hi32 * 0x1_00_00_00_00 + lo32 + (lo32 < 0 ? 2 ** 32 : 0)
510
+ }
@@ -0,0 +1,18 @@
1
+ // https://developer.mozilla.org/en-US/docs/Web/API/FileSystemSyncAccessHandle
2
+ interface FileSystemSyncAccessHandle {
3
+ close: () => void
4
+ flush: () => Promise<void>
5
+ getSize: () => number
6
+ read: (buffer: Uint8Array | Uint32Array, options?: FileSystemReadWriteOptions) => number
7
+ truncate: (newSize: number) => void
8
+ write: (buffer: Uint8Array | Uint32Array, options?: FileSystemReadWriteOptions) => number
9
+ seek: (offset: number) => void
10
+ }
11
+
12
+ interface FileSystemReadWriteOptions {
13
+ at?: number
14
+ }
15
+
16
+ interface FileSystemFileHandle {
17
+ createSyncAccessHandle: () => Promise<FileSystemSyncAccessHandle>
18
+ }
@@ -0,0 +1,109 @@
1
+ import type { MakeSqliteDb, PersistenceInfo, SqliteDb } from '@livestore/common'
2
+ import { Effect, Hash } from '@livestore/utils/effect'
3
+ import type { MemoryVFS } from '@livestore/wa-sqlite/src/examples/MemoryVFS.js'
4
+
5
+ import { makeInMemoryDb } from '../in-memory-vfs.js'
6
+ import { makeSqliteDb } from '../make-sqlite-db.js'
7
+ import type { AccessHandlePoolVFS } from './opfs/AccessHandlePoolVFS.js'
8
+ import { makeOpfsDb } from './opfs/index.js'
9
+
10
+ export * from './opfs/opfs-sah-pool.js'
11
+
12
+ export type WebDatabaseMetadataInMemory = {
13
+ _tag: 'in-memory'
14
+ vfs: MemoryVFS
15
+ dbPointer: number
16
+ persistenceInfo: PersistenceInfo
17
+ deleteDb: () => void
18
+ configureDb: (db: SqliteDb) => void
19
+ }
20
+
21
+ export type WebDatabaseMetadataOpfs = {
22
+ _tag: 'opfs'
23
+ vfs: AccessHandlePoolVFS
24
+ dbPointer: number
25
+ persistenceInfo: PersistenceInfo<{
26
+ opfsDirectory: string
27
+ /** Actual filename used by OPFS */
28
+ opfsFileName: string
29
+ }>
30
+ deleteDb: () => void
31
+ configureDb: (db: SqliteDb) => void
32
+ }
33
+
34
+ export type WebDatabaseMetadata = WebDatabaseMetadataInMemory | WebDatabaseMetadataOpfs
35
+
36
+ export type WebDatabaseInputInMemory = {
37
+ _tag: 'in-memory'
38
+ configureDb?: (db: SqliteDb) => void
39
+ }
40
+
41
+ export type WebDatabaseInputOpfs = {
42
+ _tag: 'opfs'
43
+ /** Filename of the database file (only used when exporting/downloading the database) */
44
+ fileName: string
45
+ opfsDirectory: string
46
+ configureDb?: (db: SqliteDb) => void
47
+ }
48
+
49
+ export type WebDatabaseInput = WebDatabaseInputInMemory | WebDatabaseInputOpfs
50
+
51
+ export const sqliteDbFactory =
52
+ ({
53
+ sqlite3,
54
+ }: {
55
+ sqlite3: SQLiteAPI
56
+ }): MakeSqliteDb<{ dbPointer: number; persistenceInfo: PersistenceInfo }, WebDatabaseInput, WebDatabaseMetadata> =>
57
+ (input: WebDatabaseInput) =>
58
+ Effect.gen(function* () {
59
+ if (input._tag === 'in-memory') {
60
+ const { dbPointer, vfs } = makeInMemoryDb(sqlite3)
61
+ return makeSqliteDb<WebDatabaseMetadataInMemory>({
62
+ sqlite3,
63
+ metadata: {
64
+ _tag: 'in-memory',
65
+ vfs,
66
+ dbPointer,
67
+ deleteDb: () => {},
68
+ configureDb: input.configureDb ?? (() => {}),
69
+ persistenceInfo: {
70
+ fileName: ':memory:',
71
+ },
72
+ },
73
+ }) as any
74
+ }
75
+
76
+ // TODO figure out the actual max length
77
+ const MAX_DB_FILENAME_LENGTH = 60
78
+
79
+ let dbFilename = input.fileName
80
+
81
+ if (input.fileName.length > MAX_DB_FILENAME_LENGTH) {
82
+ yield* Effect.logWarning(
83
+ `dbFilename too long: '${input.fileName}'. Max ${MAX_DB_FILENAME_LENGTH} chars, got ${input.fileName.length}. Hashing...`,
84
+ )
85
+ dbFilename = `hash-${Hash.string(input.fileName)}.db`
86
+ }
87
+
88
+ const { dbPointer, vfs } = yield* makeOpfsDb({
89
+ sqlite3,
90
+ directory: input.opfsDirectory,
91
+ fileName: dbFilename,
92
+ })
93
+
94
+ return makeSqliteDb<WebDatabaseMetadataOpfs>({
95
+ sqlite3,
96
+ metadata: {
97
+ _tag: 'opfs',
98
+ vfs,
99
+ dbPointer,
100
+ deleteDb: () => vfs.resetAccessHandle(input.fileName),
101
+ configureDb: input.configureDb ?? (() => {}),
102
+ persistenceInfo: {
103
+ fileName: dbFilename,
104
+ opfsDirectory: input.opfsDirectory,
105
+ opfsFileName: vfs.getOpfsFileName(dbFilename),
106
+ },
107
+ },
108
+ })
109
+ })