<commit_before>import merge from './internal/merge';
import jsSHA from 'jssha';
import StackTrace from 'stacktrace-js';

type Value = // TODO: Gauge
  { event: string };

type Message =
  { name: ?Array
  ; value: Value // TODO: value: Event|Gauge
  ; timestamp: ?Date|?string
  ; context: ?Object
  ; fields: ?Object
  ; session: ?Object // TODO: remove when removed from logary
  ; level: string }

export const LogLevel = {
  VERBOSE: 'verbose',
  DEBUG: 'debug',
  INFO: 'info',
  WARN: 'warn',
  ERROR: 'error',
  FATAL: 'fatal',

  lessThan(a: string, b: string) {
    const order = {
      VERBOSE: 0,
      DEBUG: 1,
      INFO: 2,
      WARN: 3,
      ERROR: 4,
      FATAL: 5
    };
    const A = a.toUpperCase();
    const B = b.toUpperCase();
    if (order.hasOwnProperty(A) && order.hasOwnProperty(B)) {
      return order[A] < order[B];
    } else return false;
  }
};

export const Message = {
  defaultLevel: LogLevel.DEBUG,
  defaultEventLevel: LogLevel.INFO,

  create({ template, fields, context, level }) {
    return {
      value: { event: template },
      fields,
      context,
      level: level || this.defaultLevel
    };
  },

  event(template, level, fields = {}, context = {}) {
    return this.create({
      template,
      fields,
      context,
      level: level || this.defaultEventLevel
    });
  },

  eventError(template, error: Error, fields = {}, context = {}) {
    return this.create({
      template,
      level: LogLevel.ERROR,
      fields: {
        ...fields,
        errors: [ error ],
      },
      context
    });
  }
};

export const HashUtils = {
  normalise(o: Object): string {
    return traverse(
      o, '',
      (state, key, value) => {
        //console.log('####### state', JSON.stringify(state), 'key', key, 'value', value);
        return state + key + "\t" + value + "\n"
      }
    );
  }
};

type Middleware = (next:Function) => (m: Message) => Message;

export const Middleware = {
  messageId: next => msg => {
    // https://github.com/Caligatio/jsSHA
    const hasher = new jsSHA('SHA-256', 'TEXT'),
          messageId = msg.messageId ||
                      hasher.update(HashUtils.normalise(msg)) ||
                      hasher.getHash('HEX');
    return next({
      messageId,
      ...msg
    });
  },

  stacktrace: next => msg => {
    if (msg.fields && msg.fields.errors && msg.fields.errors.length > 0) {
      const allErrors = msg.fields.errors.map(error => StackTrace.fromError(error));
      return Promise.all(allErrors).
        then(newErrors => {
          //console.log('newErrors', newErrors);
          return next({
            ...msg,
            fields: {
              ...msg.fields,
              errors: newErrors
            }
          });
        },
        internalError => next(msg));
    } else {
      return next(msg);
    }
  },

  timestamp: next => msg => next(merge(msg, {
    timestamp: (typeof msg.timestamp === 'Date' ?
                   Number((msg.timestamp.getTime() + '000000')) :
                   Number(Date.now() + '000000'))
  })),

  minLevel(level: string) {
    return next => msg =>
        LogLevel.lessThan(msg.level, level) ?
          msg :
          next(msg);
  }
};

export default function Logary(service: string, target, mid) {
  const missingTarget = msg => {
    // eslint-disable-next-line
    if (console) { console.warn('No target specified in logary.'); }
    return Promise.resolve(msg);
  };

  const missingMid = [
    Middleware.minLevel(LogLevel.INFO),
    Middleware.messageId,
    Middleware.stacktrace,
    Middleware.timestamp
  ];

  const actualMid =
    (!!mid ? (Array.isArray(mid) ? mid : [ mid ])
           : missingMid);

  this.service = service;
  this.target = target || missingTarget;
  this.middleware = actualMid;
}

type Logger = (m: Message) => Message;

type FinalLogger = (m: Message) => Promise;

/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {...Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions
 * from right to left. For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */
export function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg;
  } else {
    const last = funcs[funcs.length - 1];
    const rest = funcs.slice(0, -1);
    return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args));
  }
};

/**
 * Compose the logger with the target, to create a FinalLogger.<commit_msg>Comments<commit_after>import merge from './internal/merge';
import jsSHA from 'jssha';
import StackTrace from 'stacktrace-js';

type Value = // TODO: Gauge
  { event: string };

type Message =
  { name: ?Array
  ; value: Value // TODO: value: Event|Gauge
  ; timestamp: ?Date|?string
  ; context: ?Object
  ; fields: ?Object
  ; session: ?Object // TODO: remove when removed from logary
  ; level: string }

export const LogLevel = {
  VERBOSE: 'verbose',
  DEBUG: 'debug',
  INFO: 'info',
  WARN: 'warn',
  ERROR: 'error',
  FATAL: 'fatal',

  lessThan(a: string, b: string) {
    const order = {
      VERBOSE: 0,
      DEBUG: 1,
      INFO: 2,
      WARN: 3,
      ERROR: 4,
      FATAL: 5
    };
    const A = a.toUpperCase();
    const B = b.toUpperCase();
    if (order.hasOwnProperty(A) && order.hasOwnProperty(B)) {
      return order[A] < order[B];
    } else return false;
  }
};

export const Message = {
  defaultLevel: LogLevel.DEBUG,
  defaultEventLevel: LogLevel.INFO,

  create({ template, fields, context, level }) {
    return {
      value: { event: template },
      fields,
      context,
      level: level || this.defaultLevel
    };
  },

  event(template, level, fields = {}, context = {}) {
    return this.create({
      template,
      fields,
      context,
      level: level || this.defaultEventLevel
    });
  },

  eventError(template, error: Error, fields = {}, context = {}) {
    return this.create({
      template,
      level: LogLevel.ERROR,
      fields: {
        ...fields,
        errors: [ error ],
      },
      context
    });
  }
};

export const HashUtils = {
  normalise(o: Object): string {
    return traverse(
      o, '',
      (state, key, value) => {
        //console.log('####### state', JSON.stringify(state), 'key', key, 'value', value);
        return state + key + "\t" + value + "\n"
      }
    );
  }
};

type Middleware = (next:Function) => (m: Message) => Message;

export const Middleware = {
  messageId: next => msg => {
    // https://github.com/Caligatio/jsSHA
    const hasher = new jsSHA('SHA-256', 'TEXT'),
          messageId = msg.messageId ||
                      hasher.update(HashUtils.normalise(msg)) ||
                      hasher.getHash('HEX');
    return next({
      messageId,
      ...msg
    });
  },

  stacktrace: next => msg => {
    if (msg.fields && msg.fields.errors && msg.fields.errors.length > 0) {
      const allErrors = msg.fields.errors.map(error => StackTrace.fromError(error));
      return Promise.all(allErrors).
        then(newErrors => {
          //console.log('newErrors', newErrors);
          return next({
            ...msg,
            fields: {
              ...msg.fields,
              errors: newErrors
            }
          });
        },
        internalError => next(msg));
    } else {
      return next(msg);
    }
  },

  timestamp: next => msg => next(merge(msg, {
    timestamp: (typeof msg.timestamp === 'Date' ?
                   Number((msg.timestamp.getTime() + '000000')) :
                   Number(Date.now() + '000000'))
  })),

  minLevel(level: string) {
    return next => msg =>
        LogLevel.lessThan(msg.level, level) ?
          msg :
          next(msg);
  }
};

export default function Logary(service: string, target, mid) {
  const missingTarget = msg => {
    // eslint-disable-next-line
    if (console) { console.warn('No target specified in logary.'); }
    return Promise.resolve(msg);
  };

  const missingMid = [
    Middleware.minLevel(LogLevel.INFO),
    Middleware.messageId,
    Middleware.stacktrace,
    Middleware.timestamp
  ];

  const actualMid =
    (!!mid ? (Array.isArray(mid) ? mid : [ mid ])
           : missingMid);

  this.service = service;
  this.target = target || missingTarget;
  this.middleware = actualMid;
}

type Logger = (m: Message) => Message;

type FinalLogger = (m: Message) => Promise;

/**
 * Composes single-argument functions from right to left. The rightmost
 * function can take multiple arguments as it provides the signature for
 * the resulting composite function.
 *
 * @param {...Function} funcs The functions to compose.
 * @returns {Function} A function obtained by composing the argument functions
 * from right to left. For example, compose(f, g, h) is identical to doing
 * (...args) => f(g(h(...args))).
 */
export function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg;
  } else {
    // Alternative formulation with higher-order functions only.
    // return funcs.reduceRight((composed, f) => f(composed), x => x);
    const last = funcs[funcs.length - 1];
    const rest = funcs.slice(0, -1);
    return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args));
  }
};

/**
 * Compose the logger with the target, to create a FinalLogger.
{'fraction_non_alphanumeric': 0.15205667759331712, 'fraction_numerical': 0.005498572485989214, 'mean_word_length': 2.361051883439943, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 49, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '707853', 'n_tokens_mistral': 2978, 'n_tokens_neox': 2892, 'n_words': 771}