All files / src/fsa-to-node/worker FsaNodeSyncAdapterWorker.ts

12.82% Statements 5/39
0% Branches 0/7
0% Functions 0/4
13.51% Lines 5/37

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 791x   1x 1x                         1x   1x                                                                                                                        
import { Defer } from '../../thingies/Defer';
import { FsaNodeWorkerMessageCode } from './constants';
import { SyncMessenger } from './SyncMessenger';
import { decoder, encoder } from '../json';
import type * as fsa from '../../fsa/types';
import type { FsaNodeSyncAdapter, FsaNodeSyncAdapterApi } from '../types';
import type {
  FsaNodeWorkerMsg,
  FsaNodeWorkerMsgInit,
  FsaNodeWorkerMsgRequest,
  FsaNodeWorkerMsgResponse,
  FsaNodeWorkerMsgResponseError,
  FsaNodeWorkerMsgRootSet,
  FsaNodeWorkerMsgSetRoot,
} from './types';
 
let rootId = 0;
 
export class FsaNodeSyncAdapterWorker implements FsaNodeSyncAdapter {
  public static async start(
    url: string,
    dir: fsa.IFileSystemDirectoryHandle | Promise<fsa.IFileSystemDirectoryHandle>,
  ): Promise<FsaNodeSyncAdapterWorker> {
    const worker = new Worker(url);
    const future = new Defer<FsaNodeSyncAdapterWorker>();
    let id = rootId++;
    let messenger: SyncMessenger | undefined = undefined;
    const _dir = await dir;
    worker.onmessage = e => {
      const data = e.data;
      Iif (!Array.isArray(data)) return;
      const msg = data as FsaNodeWorkerMsg;
      const code = msg[0] as FsaNodeWorkerMessageCode;
      switch (code) {
        case FsaNodeWorkerMessageCode.Init: {
          const [, sab] = msg as FsaNodeWorkerMsgInit;
          messenger = new SyncMessenger(sab);
          const setRootMessage: FsaNodeWorkerMsgSetRoot = [FsaNodeWorkerMessageCode.SetRoot, id, _dir];
          worker.postMessage(setRootMessage);
          break;
        }
        case FsaNodeWorkerMessageCode.RootSet: {
          const [, rootId] = msg as FsaNodeWorkerMsgRootSet;
          Iif (id !== rootId) return;
          const adapter = new FsaNodeSyncAdapterWorker(messenger!, _dir);
          future.resolve(adapter);
          break;
        }
      }
    };
    return await future.promise;
  }
 
  public constructor(
    protected readonly messenger: SyncMessenger,
    protected readonly root: fsa.IFileSystemDirectoryHandle,
  ) {}
 
  public call<K extends keyof FsaNodeSyncAdapterApi>(
    method: K,
    payload: Parameters<FsaNodeSyncAdapterApi[K]>[0],
  ): ReturnType<FsaNodeSyncAdapterApi[K]> {
    const request: FsaNodeWorkerMsgRequest = [FsaNodeWorkerMessageCode.Request, method, payload];
    const encoded = encoder.encode(request);
    const encodedResponse = this.messenger.callSync(encoded);
    type MsgBack = FsaNodeWorkerMsgResponse | FsaNodeWorkerMsgResponseError;
    const [code, data] = decoder.decode(encodedResponse) as MsgBack;
    switch (code) {
      case FsaNodeWorkerMessageCode.Response:
        return data as any;
      case FsaNodeWorkerMessageCode.ResponseError:
        throw data;
      default: {
        throw new Error('Invalid response message code');
      }
    }
  }
}