ai-labs-team/casium-devtools

View on GitHub
src/util.ts

Summary

Maintainability
A
0 mins
Test Coverage
F
58%
import { applyDiff } from '@warrenseymour/json-delta';
import { and, both, contains, equals, flip, has, is, lensPath, map, merge, nth, pipe, propSatisfies, reduce, set, tail, view } from 'ramda';

import { SerializedMessage } from './instrumenter';
import { GenericObject } from 'casium/core';

export interface DownloadOptions {
  filename: string;
  type: string;
  data?: {};
}

export const download = (options: Partial<DownloadOptions> = {}) => {
  const opts: DownloadOptions = merge({
    filename: 'file-' + Math.random().toString(36).substr(2, 5),
    type: 'application/octet-stream'
  }, options);

  if (opts.data === null || opts.data === undefined) {
    return false;
  }

  const link = document.createElement('a'),
    evt = document.createEvent("MouseEvents"),
    blob = new Blob([opts.data], { type: opts.type });

  link.href = window.URL.createObjectURL(blob);
  link.download = opts.filename;
  evt.initEvent('click', true, true);
  link.dispatchEvent(evt);
};

export interface UploadOptions {
  type: string;
}

export interface UploadResult {
  filename: string;
  content: string;
}

export const readFile = (file: File): Promise<UploadResult> => new Promise((resolve, reject) => {
  const reader = new FileReader();

  reader.onload = (e: any) => resolve({
    filename: file.name,
    content: e.target.result
  });
  reader.onerror = reject;
  reader.readAsText(file);
});

/**
 * Prompts the user to select a file, and returns a `FileList` wrapped in a promise.
 */
const requestFile = (options: Partial<UploadOptions> = {}): Promise<FileList> => new Promise((resolve, reject) => {
  const input = document.createElement('input');
  input.setAttribute('type', 'file');

  if (options.type) {
    input.setAttribute('accept', options.type);
  }
  input.addEventListener('change', () => (!input.files || !input.files[0]) ? reject() : resolve(input.files));

  const evt = document.createEvent('MouseEvents');
  evt.initEvent('click', true, true);

  input.dispatchEvent(evt);
});

/**
 * Prompt the user to select a file, then read it.
 */
export const upload = (options: Partial<UploadOptions> = {}): Promise<UploadResult> => (
  requestFile(options).then(nth(0) as any as (fl: FileList) => File).then(readFile)
);

/**
 * Applies a single message's delta to an initial state, yielding the state
 * after that message occurred.
 */
export const applyDelta = (initialState: GenericObject, { delta }: SerializedMessage) =>
  applyDiff(initialState, delta);

/**
 * Applies multiple messages' deltas to an initial state, yielding the final
 * state after all messages occurred.
 */
export const applyDeltas = (initial: GenericObject, messages: SerializedMessage[]) =>
  messages.reduce(applyDelta as any, initial);

export const nextState = (path: string[], next: GenericObject, prev: GenericObject | null) =>
  set(lensPath(path), next, prev);

export const isModifiedObject = both(
  is(Object),
  diff => ('__old' in diff) && ('__new' in diff) && (Object.keys(diff).length === 2)
);

export const typeOf = (val: any) => (
  ((val === null || val === undefined) && 'null') ||
  (typeof val === 'object' && val.constructor === Array && 'array') ||
  typeof val
);

export const isModifiedArray = both(
  pipe(typeOf, equals('array')),
  pipe(
    map((val: any[]) => {
      return typeOf(val) === 'array' && (
        val.length === 2 && ['-', '+', '~'].includes(val[0]) ||
        val.length === 1 && val[0] === ' '
      );
    }),
    reduce<boolean, boolean>(and, true)
  )
);

export const hasPath = (path: string[], data: any): boolean => {
  const key = path[0];

  if (!has(key, data)) {
    return false;
  }

  const value = data[key];
  if (is(Object, value)) {
    return path.length === 1 || hasPath(tail(path), value);
  }

  return true;
}

export const deepPick = <T extends {}>(data: T, paths: string[][]): Partial<T> =>
  paths
    .sort((a, b) => a.length - b.length)
    .reduce((result, path) => {
      if (!hasPath(path, data)) {
        return result;
      }

      const lens = lensPath(path);
      const value = view(lens, data);

      return set(lens, value, result);
    }, {});

const _contains = flip(contains);

export const fromMatches = (senders: string[]) =>
  propSatisfies(_contains(senders), 'from');