Skip to content

Commit

Permalink
feature(storage): local adapter + better docs + more methods
Browse files Browse the repository at this point in the history
  • Loading branch information
marcj committed Oct 8, 2023
1 parent 895ccae commit c9c486e
Show file tree
Hide file tree
Showing 5 changed files with 630 additions and 137 deletions.
2 changes: 2 additions & 0 deletions packages/storage/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from './src/storage.js';
export * from './src/local-adapter.js';
export * from './src/memory-adapter.js';
68 changes: 62 additions & 6 deletions packages/storage/src/local-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { File, FileType, pathDirectory, pathNormalize, Reporter, StorageAdapter } from './storage.js';
import { FileType, pathDirectory, pathNormalize, Reporter, StorageAdapter, StorageFile } from './storage.js';
import type * as fs from 'fs/promises';

export class StorageNodeLocalAdapter implements StorageAdapter {
Expand Down Expand Up @@ -30,6 +30,11 @@ export class StorageNodeLocalAdapter implements StorageAdapter {
await fs.rm(path);
}

async makeDirectory(path: string): Promise<void> {
const fs = await this.getFs();
await fs.mkdir(this.getPath(path), { recursive: true });
}

async deleteDirectory(path: string, reporter: Reporter): Promise<void> {
path = this.getPath(path);
const fs = await this.getFs();
Expand All @@ -47,13 +52,13 @@ export class StorageNodeLocalAdapter implements StorageAdapter {
}
}

async files(path: string): Promise<File[]> {
async files(path: string): Promise<StorageFile[]> {
const localPath = this.getPath(path);
const files: File[] = [];
const files: StorageFile[] = [];
const fs = await this.getFs();

for (const name of await fs.readdir(localPath)) {
const file = new File(path + '/' + name);
const file = new StorageFile(path + '/' + name);
const stat = await fs.stat(localPath + '/' + name);
file.size = stat.size;
file.lastModified = new Date(stat.mtime);
Expand All @@ -64,10 +69,61 @@ export class StorageNodeLocalAdapter implements StorageAdapter {
return files;
}

async get(path: string): Promise<File | undefined> {
async allFiles(path: string, reporter: Reporter): Promise<StorageFile[]> {
const files: StorageFile[] = [];
const fs = await this.getFs();

const queue: string[] = [path];
while (!reporter.aborted && queue.length) {
const currentPath = queue.shift()!;
for (const name of await fs.readdir(this.getPath(currentPath))) {
if (reporter.aborted) return files;
const file = new StorageFile(currentPath + '/' + name);
const stat = await fs.stat(this.getPath(currentPath + '/' + name));
file.size = stat.size;
file.lastModified = new Date(stat.mtime);
file.type = stat.isFile() ? FileType.File : FileType.Directory;
files.push(file);
reporter.progress(files.length, 0);
if (file.isDirectory()) queue.push(file.path);
}
}

return files;
}

async directories(path: string): Promise<StorageFile[]> {
return (await this.files(path)).filter(file => file.isDirectory());
}

async allDirectories(path: string, reporter: Reporter): Promise<StorageFile[]> {
const files: StorageFile[] = [];
const fs = await this.getFs();

const queue: string[] = [path];
while (!reporter.aborted && queue.length) {
const currentPath = queue.shift()!;
for (const name of await fs.readdir(this.getPath(currentPath))) {
if (reporter.aborted) return files;
const file = new StorageFile(currentPath + '/' + name);
const stat = await fs.stat(this.getPath(currentPath + '/' + name));
if (!stat.isDirectory()) continue;
file.size = stat.size;
file.lastModified = new Date(stat.mtime);
file.type = stat.isFile() ? FileType.File : FileType.Directory;
files.push(file);
reporter.progress(files.length, 0);
if (file.isDirectory()) queue.push(file.path);
}
}

return files;
}

async get(path: string): Promise<StorageFile | undefined> {
const localPath = this.getPath(path);
const fs = await this.getFs();
const file = new File(path);
const file = new StorageFile(path);
try {
const stat = await fs.stat(localPath);
file.size = stat.size;
Expand Down
103 changes: 103 additions & 0 deletions packages/storage/src/memory-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { StorageFileNotFound, FileType, pathDirectories, pathDirectory, Reporter, StorageAdapter, StorageFile } from './storage.js';

export class StorageMemoryAdapter implements StorageAdapter {
protected memory: { file: StorageFile, contents: Uint8Array }[] = [];

async files(path: string): Promise<StorageFile[]> {
return this.memory.filter(file => file.file.directory === path)
.map(v => v.file);
}

async makeDirectory(path: string): Promise<void> {
const directories = pathDirectories(path);
//filter out all parts that already exist
for (const dir of directories) {
const exists = await this.exists(dir);
if (exists) continue;
const file = new StorageFile(dir);
file.type = FileType.Directory;
this.memory.push({ file, contents: new Uint8Array });
}
}

async allFiles(path: string): Promise<StorageFile[]> {
return this.memory.filter(file => file.file.inDirectory(path))
.map(v => v.file);
}

async directories(path: string): Promise<StorageFile[]> {
return this.memory.filter(file => file.file.directory === path)
.filter(file => file.file.isDirectory())
.map(v => v.file);
}

async allDirectories(path: string): Promise<StorageFile[]> {
return this.memory.filter(file => file.file.inDirectory(path))
.filter(file => file.file.isDirectory())
.map(v => v.file);
}

async write(path: string, contents: Uint8Array, reporter: Reporter): Promise<void> {
let file = this.memory.find(file => file.file.path === path);
if (!file) {
await this.makeDirectory(pathDirectory(path));
file = { file: new StorageFile(path), contents };
this.memory.push(file);
}
file.contents = contents;
file.file.size = contents.length;
file.file.lastModified = new Date();
}

async read(path: string, reporter: Reporter): Promise<Uint8Array> {
const file = this.memory.find(file => file.file.path === path);
if (!file) throw new StorageFileNotFound('File not found');
return file.contents;
}

async exists(path: string): Promise<boolean> {
return !!this.memory.find(file => file.file.path === path);
}

async delete(path: string): Promise<void> {
const index = this.memory.findIndex(file => file.file.path === path);
if (index === -1) throw new StorageFileNotFound('File not found');
this.memory.splice(index, 1);
}

async deleteDirectory(path: string, reporter: Reporter): Promise<void> {
const files = this.memory.filter(file => file.file.path.startsWith(path));
reporter.progress(0, files.length);
let i = 0;
for (const file of files) {
this.memory.splice(this.memory.indexOf(file), 1);
reporter.progress(++i, files.length);
}
}

async get(path: string): Promise<StorageFile | undefined> {
return this.memory.find(file => file.file.path === path)?.file;
}

async copy(source: string, destination: string, reporter: Reporter): Promise<void> {
const files = this.memory.filter(file => file.file.path.startsWith(source));
reporter.progress(0, files.length);
let i = 0;
for (const file of files) {
const newPath = destination + file.file.path.slice(source.length);
this.memory.push({ file: new StorageFile(newPath), contents: file.contents });
reporter.progress(++i, files.length);
}
}

async move(source: string, destination: string, reporter: Reporter): Promise<void> {
const files = this.memory.filter(file => file.file.path.startsWith(source));
reporter.progress(0, files.length);
let i = 0;
for (const file of files) {
const newPath = destination + file.file.path.slice(source.length);
file.file.path = newPath;
reporter.progress(++i, files.length);
}
}
}
Loading

0 comments on commit c9c486e

Please # to comment.