All files / src/storage OPFSStorage.ts

93.33% Statements 28/30
50% Branches 1/2
100% Functions 6/6
93.33% Lines 28/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78                8x       2x 2x 2x 1x 1x     1x         3x 3x 3x 3x 3x 1x   2x 2x       2x         1x 1x 1x             1x 1x 1x 1x             1x   1x       1x 1x 2x                  
import { IFileStorage } from "../types.js";
 
/**
 * Origin Private File System (OPFS) を使用したファイルストレージ実装。
 * 2026年時点でのブラウザ標準 API を使用し、バイナリデータの高速な読み書きを実現します。
 */
export class OPFSStorage implements IFileStorage {
  private async getDirectory(): Promise<FileSystemDirectoryHandle> {
    return await navigator.storage.getDirectory();
  }
 
  async get(key: string): Promise<ArrayBuffer | null> {
    try {
      const root = await this.getDirectory();
      const fileHandle = await root.getFileHandle(key);
      const file = await fileHandle.getFile();
      return await file.arrayBuffer();
    } catch {
      // ファイルが存在しない、またはアクセス権限がない場合は null を返す
      return null;
    }
  }
 
  async set(key: string, data: ArrayBuffer): Promise<void> {
    const root = await this.getDirectory();
    const fileHandle = await root.getFileHandle(key, { create: true });
    const writable = await fileHandle.createWritable();
    try {
      await writable.write(data);
      await writable.close();
    } catch (error) {
      try {
        await writable.abort();
      } catch {
        // abort 失敗は二次的エラーのため抑制し、元のエラーを優先する
      }
      throw error;
    }
  }
 
  async delete(key: string): Promise<void> {
    try {
      const root = await this.getDirectory();
      await root.removeEntry(key);
    } catch {
      // ファイルが存在しない場合は何もしない
    }
  }
 
  async has(key: string): Promise<boolean> {
    try {
      const root = await this.getDirectory();
      await root.getFileHandle(key);
      return true;
    } catch {
      return false;
    }
  }
 
  async clear(): Promise<void> {
    const root = await this.getDirectory();
    // 2026 Best Practice: Feature detection for non-standard iterator without using 'any'
    const rootWithKeys = root as FileSystemDirectoryHandle & {
      keys?(): AsyncIterable<string>;
    };
 
    if (typeof rootWithKeys.keys === "function") {
      for await (const name of rootWithKeys.keys()) {
        await root.removeEntry(name, { recursive: true });
      }
    } else E{
      throw new Error(
        "[OPFSStorage] Directory iteration (keys()) is not supported in this environment. Cannot clear storage.",
      );
    }
  }
}