mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-16 11:47:54 +01:00
fs: get rid of localforage and use generic interface
This commit is contained in:
committed by
Abdullah Atta
parent
ec6fc6839e
commit
c88bc722e1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
13
packages/streamable-fs/package-lock.json
generated
13
packages/streamable-fs/package-lock.json
generated
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user