All files / src/capabilities EnvironmentDetector.ts

96.87% Statements 31/32
90.9% Branches 20/22
100% Functions 7/7
100% Lines 26/26

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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115                          11x                 11x                       11x             10x               11x       11x   11x 11x             11x     11x   11x             4x       4x 3x 2x 1x       13x 13x 13x     12x 12x   1x         13x 13x     12x             1x        
import { ICapabilities, ISecurityStatus } from "../types.js";
import { SecurityAdvisor } from "./SecurityAdvisor.js";
 
/**
 * 実行環境の能力、メモリ、CPU、およびセキュリティ状態を検知するユーティリティ。
 */
export class EnvironmentDetector {
  /**
   * 現在の環境能力を検知します。
   */
  public static async detect(): Promise<ICapabilities> {
    // 2026: Capabilities detection for WASM threads/simd
    // Node.js/Bun 環境でも正常に動作するように globalThis を使用
    const g = globalThis as unknown as {
      process?: { arch?: string; platform?: string };
      navigator?: {
        hardwareConcurrency?: number;
        deviceMemory?: number;
        storage?: { getDirectory?: () => unknown };
      };
    };
 
    const caps: ICapabilities = {
      wasmThreads: this.checkWasmThreads(),
      wasmSimd: this.checkWasmSimd(),
      threads: !!(
        g.navigator?.hardwareConcurrency && g.navigator.hardwareConcurrency > 1
      ),
      simd: !!(
        g.process?.arch?.includes("x64") || g.process?.arch?.includes("arm64")
      ),
      opfs: !!g.navigator?.storage?.getDirectory,
    };
 
    return caps;
  }
 
  /**
   * 2026 Zenith Standard: セキュリティ状態とアドバイスの取得
   */
  public static async getSecurityStatus(): Promise<ISecurityStatus> {
    return await SecurityAdvisor.getStatus();
  }
 
  /**
   * 推奨される最大メモリ使用量 (bytes) を算出します。
   * モバイル (Jetsam) 対策として、RAM の一定割合に制限します。
   */
  public static getRecommendedMaxMemory(): number {
    const g = globalThis as unknown as {
      navigator?: { deviceMemory?: number };
    };
    // navigator.deviceMemory は GB 単位。不明な場合は安全な 2GB と仮定。
    const ramGB = g.navigator?.deviceMemory || 2;
    // RAM の 1/4 〜 1/2 を上限とする (max 1GB for safe buffer)
    const limitGB = Math.min(1, ramGB / 2);
    return limitGB * 1024 * 1024 * 1024;
  }
 
  /**
   * 推奨されるスレッド数を算出します。
   */
  public static getRecommendedThreads(): number {
    const g = globalThis as unknown as {
      navigator?: { hardwareConcurrency?: number };
    };
    const cores = g.navigator?.hardwareConcurrency || 1;
    // ブラウザのUIスレッド等を考慮し、コア数 - 1 or 2
    return Math.max(1, cores > 4 ? cores - 2 : cores - 1);
  }
 
  /**
   * 実行環境の種類。
   */
  public static getRuntime(): "browser" | "node" | "bun" | "unknown" {
    const g = globalThis as unknown as {
      process?: { versions?: { node?: string; bun?: string } };
      window?: unknown;
    };
    if (g.process?.versions?.bun) return "bun";
    if (g.process?.versions?.node) return "node";
    if (g.window) return "browser";
    return "unknown";
  }
 
  private static checkWasmThreads(): boolean {
    try {
      Iif (typeof WebAssembly === "undefined") return false;
      if (typeof SharedArrayBuffer === "undefined") return false;
 
      // 2026: 実際に共有メモリが作成可能かチェック(COOP/COEP が未設定だとここで失敗する可能性がある)
      new WebAssembly.Memory({ initial: 1, maximum: 1, shared: true });
      return true;
    } catch {
      return false;
    }
  }
 
  private static checkWasmSimd(): boolean {
    try {
      if (typeof WebAssembly === "undefined") return false;
      // 2026: Physical SIMD detection using a minimal SIMD bytecode (v128.const)
      // This ensures the browser's Wasm engine actually supports the SIMD section.
      return WebAssembly.validate(
        new Uint8Array([
          0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 3, 2, 1, 0, 10, 9, 1,
          7, 0, 65, 0, 253, 15, 1, 11,
        ]),
      );
    } catch {
      return false;
    }
  }
}