@livestore/sqlite-wasm
Version:
174 lines • 6.06 kB
JavaScript
/// <reference types="node" />
/* eslint-disable prefer-arrow/prefer-arrow-functions */
import * as fs from 'node:fs';
import path from 'node:path';
import * as VFS from '@livestore/wa-sqlite/src/VFS.js';
import { FacadeVFS } from '../FacadeVFS.js';
export class NodeFS extends FacadeVFS {
mapIdToFile = new Map();
lastError = null;
directory;
constructor(name, sqlite3, directory) {
super(name, sqlite3);
this.directory = directory;
}
getFilename(fileId) {
const pathname = this.mapIdToFile.get(fileId)?.pathname;
return `NodeFS:${pathname}`;
}
jOpen(zName, fileId, flags, pOutFlags) {
try {
const pathname = zName ? path.resolve(this.directory, zName) : Math.random().toString(36).slice(2);
const file = { pathname, flags, fileHandle: null };
this.mapIdToFile.set(fileId, file);
const create = !!(flags & VFS.SQLITE_OPEN_CREATE);
const readwrite = !!(flags & VFS.SQLITE_OPEN_READWRITE);
// Convert SQLite flags to Node.js flags
let fsFlags = 'r';
if (create && readwrite) {
// Check if file exists first
const exists = fs.existsSync(pathname);
fsFlags = exists ? 'r+' : 'w+'; // Use r+ for existing files, w+ only for new files
}
else if (readwrite) {
fsFlags = 'r+'; // Open file for reading and writing
}
try {
file.fileHandle = fs.openSync(pathname, fsFlags);
pOutFlags.setInt32(0, flags, true);
return VFS.SQLITE_OK;
}
catch (err) {
if (err.code === 'ENOENT' && !create) {
return VFS.SQLITE_CANTOPEN;
}
throw err;
}
}
catch (e) {
this.lastError = e;
return VFS.SQLITE_CANTOPEN;
}
}
jRead(fileId, pData, iOffset) {
try {
const file = this.mapIdToFile.get(fileId);
if (!file?.fileHandle)
return VFS.SQLITE_IOERR_READ;
// const view = new DataView(pData.buffer, pData.byteOffset, pData.length)
// const bytesRead = fs.readSync(file.fileHandle, view, 0, pData.length, iOffset)
const bytesRead = fs.readSync(file.fileHandle, pData.subarray(), { position: iOffset });
if (bytesRead < pData.length) {
pData.fill(0, bytesRead);
return VFS.SQLITE_IOERR_SHORT_READ;
}
return VFS.SQLITE_OK;
}
catch (e) {
this.lastError = e;
return VFS.SQLITE_IOERR_READ;
}
}
jWrite(fileId, pData, iOffset) {
try {
const file = this.mapIdToFile.get(fileId);
if (!file?.fileHandle)
return VFS.SQLITE_IOERR_WRITE;
// const view = new DataView(pData.buffer, pData.byteOffset, pData.length)
// fs.writeSync(file.fileHandle, view, 0, pData.length, iOffset)
fs.writeSync(file.fileHandle, Buffer.from(pData.subarray()), 0, pData.length, iOffset);
return VFS.SQLITE_OK;
}
catch (e) {
this.lastError = e;
return VFS.SQLITE_IOERR_WRITE;
}
}
jClose(fileId) {
try {
const file = this.mapIdToFile.get(fileId);
if (!file)
return VFS.SQLITE_OK;
this.mapIdToFile.delete(fileId);
if (file.fileHandle !== null) {
fs.closeSync(file.fileHandle);
}
if (file.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) {
fs.unlinkSync(file.pathname);
}
return VFS.SQLITE_OK;
}
catch (e) {
this.lastError = e;
return VFS.SQLITE_IOERR_CLOSE;
}
}
jFileSize(fileId, pSize64) {
try {
const file = this.mapIdToFile.get(fileId);
if (!file?.fileHandle)
return VFS.SQLITE_IOERR_FSTAT;
const stats = fs.fstatSync(file.fileHandle);
pSize64.setBigInt64(0, BigInt(stats.size), true);
return VFS.SQLITE_OK;
}
catch (e) {
this.lastError = e;
return VFS.SQLITE_IOERR_FSTAT;
}
}
jTruncate(fileId, iSize) {
try {
const file = this.mapIdToFile.get(fileId);
if (!file?.fileHandle)
return VFS.SQLITE_IOERR_TRUNCATE;
fs.ftruncateSync(file.fileHandle, iSize);
return VFS.SQLITE_OK;
}
catch (e) {
this.lastError = e;
return VFS.SQLITE_IOERR_TRUNCATE;
}
}
jSync(fileId, _flags) {
try {
const file = this.mapIdToFile.get(fileId);
if (!file?.fileHandle)
return VFS.SQLITE_OK;
// TODO do this out of band (for now we disable it to speed up the node vfs)
// fs.fsyncSync(file.fileHandle)
return VFS.SQLITE_OK;
}
catch (e) {
this.lastError = e;
return VFS.SQLITE_IOERR_FSYNC;
}
}
jDelete(zName, _syncDir) {
try {
const pathname = path.resolve(this.directory, zName);
fs.unlinkSync(pathname);
return VFS.SQLITE_OK;
}
catch (e) {
this.lastError = e;
return VFS.SQLITE_IOERR_DELETE;
}
}
jAccess(zName, _flags, pResOut) {
try {
const pathname = path.resolve(this.directory, zName);
const exists = fs.existsSync(pathname);
pResOut.setInt32(0, exists ? 1 : 0, true);
return VFS.SQLITE_OK;
}
catch (e) {
this.lastError = e;
return VFS.SQLITE_IOERR_ACCESS;
}
}
deleteDb(fileName) {
fs.unlinkSync(path.join(this.directory, fileName));
}
}
//# sourceMappingURL=NodeFS.js.map