fs: get rid of localforage and use generic interface

This commit is contained in:
Abdullah Atta
2023-09-20 11:25:06 +05:00
committed by Abdullah Atta
parent ec6fc6839e
commit c88bc722e1
6 changed files with 47 additions and 45 deletions

View File

@@ -17,24 +17,15 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import localforage from "localforage";
import FileHandle from "./src/filehandle";
import { IStreamableFS } from "./src/interfaces";
import { IFileStorage, IStreamableFS } from "./src/interfaces";
import { File } from "./src/types";
export class StreamableFS implements IStreamableFS {
private storage: LocalForage;
/**
* @param db name of the indexeddb database
*/
constructor(db: string) {
this.storage = localforage.createInstance({
storeName: "streamable-fs",
name: db,
driver: [localforage.INDEXEDDB]
});
}
constructor(private readonly storage: IFileStorage) {}
async createFile(
filename: string,
@@ -43,23 +34,24 @@ export class StreamableFS implements IStreamableFS {
): Promise<FileHandle> {
if (await this.exists(filename)) throw new Error("File already exists.");
const file: File = await this.storage.setItem<File>(filename, {
const file: File = {
filename,
size,
type,
chunks: 0
});
};
await this.storage.setMetadata(filename, file);
return new FileHandle(this.storage, file);
}
async readFile(filename: string): Promise<FileHandle | undefined> {
const file = await this.storage.getItem<File>(filename);
const file = await this.storage.getMetadata(filename);
if (!file) return undefined;
return new FileHandle(this.storage, file);
}
async exists(filename: string): Promise<boolean> {
const file = await this.storage.getItem<File>(filename);
const file = await this.storage.getMetadata(filename);
return !!file;
}

View File

@@ -9,8 +9,7 @@
"version": "1.0.0",
"license": "GPL-3.0-or-later",
"dependencies": {
"@notesnook/crypto": "file:../crypto",
"localforage": "^1.10.0"
"@notesnook/crypto": "file:../crypto"
},
"devDependencies": {
"@types/localforage": "^0.0.34"
@@ -42,12 +41,14 @@
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
"dev": true
},
"node_modules/lie": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
"integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
"dev": true,
"dependencies": {
"immediate": "~3.0.5"
}
@@ -56,6 +57,7 @@
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
"integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
"dev": true,
"dependencies": {
"lie": "3.1.1"
}
@@ -80,12 +82,14 @@
"immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
"dev": true
},
"lie": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
"integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==",
"dev": true,
"requires": {
"immediate": "~3.0.5"
}
@@ -94,6 +98,7 @@
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz",
"integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==",
"dev": true,
"requires": {
"lie": "3.1.1"
}

View File

@@ -10,8 +10,7 @@
"author": "",
"license": "GPL-3.0-or-later",
"dependencies": {
"@notesnook/crypto": "file:../crypto",
"localforage": "^1.10.0"
"@notesnook/crypto": "file:../crypto"
},
"devDependencies": {
"@types/localforage": "^0.0.34"

View File

@@ -18,16 +18,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import FileStreamSource from "./filestreamsource";
import { IFileStorage } from "./interfaces";
import { File } from "./types";
export default class FileHandle {
private storage: LocalForage;
public file: File;
constructor(storage: LocalForage, file: File) {
this.file = file;
this.storage = storage;
}
constructor(private readonly storage: IFileStorage, readonly file: File) {}
get readable() {
return new ReadableStream(new FileStreamSource(this.storage, this.file));
@@ -38,12 +33,15 @@ export default class FileHandle {
write: async (chunk, controller) => {
if (controller.signal.aborted) return;
await this.storage.setItem(this.getChunkKey(this.file.chunks++), chunk);
await this.storage.setItem(this.file.filename, this.file);
await this.storage.writeChunk(
this.getChunkKey(this.file.chunks++),
chunk
);
await this.storage.setMetadata(this.file.filename, this.file);
},
abort: async () => {
for (let i = 0; i < this.file.chunks; ++i) {
await this.storage.removeItem(this.getChunkKey(i));
await this.storage.deleteChunk(this.getChunkKey(i));
}
}
});
@@ -52,14 +50,14 @@ export default class FileHandle {
async addAdditionalData<T>(key: string, value: T) {
this.file.additionalData = this.file.additionalData || {};
this.file.additionalData[key] = value;
await this.storage.setItem(this.file.filename, this.file);
await this.storage.setMetadata(this.file.filename, this.file);
}
async delete() {
for (let i = 0; i < this.file.chunks; ++i) {
await this.storage.removeItem(this.getChunkKey(i));
await this.storage.deleteChunk(this.getChunkKey(i));
}
await this.storage.removeItem(this.file.filename);
await this.storage.deleteMetadata(this.file.filename);
}
private getChunkKey(offset: number): string {
@@ -67,10 +65,8 @@ export default class FileHandle {
}
async readChunk(offset: number): Promise<Uint8Array | null> {
const array = await this.storage.getItem<Uint8Array>(
this.getChunkKey(offset)
);
return array;
const array = await this.storage.readChunk(this.getChunkKey(offset));
return array || null;
}
async readChunks(from: number, length: number): Promise<Blob> {

View File

@@ -18,16 +18,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { File } from "./types";
import { IFileStorage } from "./interfaces";
export default class FileStreamSource {
private storage: LocalForage;
private file: File;
private offset = 0;
constructor(storage: LocalForage, file: File) {
this.storage = storage;
this.file = file;
}
constructor(
private readonly storage: IFileStorage,
private readonly file: File
) {}
start() {}
@@ -42,7 +41,7 @@ export default class FileStreamSource {
private readChunk(offset: number) {
if (offset > this.file.chunks) return;
return this.storage.getItem<Uint8Array>(this.getChunkKey(offset));
return this.storage.readChunk(this.getChunkKey(offset));
}
private getChunkKey(offset: number): string {

View File

@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import FileHandle from "./filehandle";
import { File } from "./types";
export interface IStreamableFS {
createFile(filename: string, size: number, type: string): Promise<FileHandle>;
@@ -26,3 +27,13 @@ export interface IStreamableFS {
deleteFile(filename: string): Promise<boolean>;
clear(): Promise<void>;
}
export interface IFileStorage {
clear(): Promise<void>;
setMetadata(filename: string, metadata: File): Promise<void>;
getMetadata(filename: string): Promise<File | undefined>;
deleteMetadata(filename: string): Promise<void>;
writeChunk(chunkName: string, data: Uint8Array): Promise<void>;
deleteChunk(chunkName: string): Promise<void>;
readChunk(chunkName: string): Promise<Uint8Array | undefined>;
}