<commit_before>'use strict';

// //////////////////////////////////////////////////////////////////////////////
// / @brief JavaScript base module
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2004-2013 triAGENS GmbH, Cologne, Germany
// /
// / Licensed under the Apache License, Version 2.0 (the "License")
// / you may not use this file except in compliance with the License.
// / You may obtain a copy of the License at
// /
// /     http://www.apache.org/licenses/LICENSE-2.0
// /
// / Unless required by applicable law or agreed to in writing, software
// / distributed under the License is distributed on an "AS IS" BASIS,
// / WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// / See the License for the specific language governing permissions and
// / limitations under the License.
// /
// / Copyright holder is triAGENS GmbH, Cologne, Germany
// /
// / @author Dr. <NAME>
// / @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////

var internal = require('internal');
var fs = require('fs');

// //////////////////////////////////////////////////////////////////////////////
// / @brief errors
// //////////////////////////////////////////////////////////////////////////////

Object.keys(internal.errors).forEach(function (key) {
  exports[key] = internal.errors[key].code;
});

exports.aql = function (strings, ...args) {
  const bindVars = {};
  const bindVals = [];
  let query = strings[0];
  for (let i = 0; i < args.length; i++) {
    const rawValue = args[i];
    let value = rawValue;
    if (rawValue && typeof rawValue.toAQL === 'function') {
      query += `${rawValue.toAQL()}${strings[i + 1]}`;
      continue;
    }
    const index = bindVals.indexOf(rawValue);
    const isKnown = index !== -1;
    let name = `value${isKnown ? index : bindVals.length}`;
    if (rawValue && rawValue.isArangoCollection) {
      name = `@${name}`;
      value = rawValue.name();
    }
    if (!isKnown) {
      bindVals.push(rawValue);
      bindVars[name] = value;
    }
    query += `@${name}${strings[i + 1]}`;
  }
  return {query, bindVars};
};

exports.errors = internal.errors;

// //////////////////////////////////////////////////////////////////////////////
// / @brief ArangoError
// //////////////////////////////////////////////////////////////////////////////

exports.ArangoError = internal.ArangoError;

// //////////////////////////////////////////////////////////////////////////////
// / @brief defines a module
// //////////////////////////////////////////////////////////////////////////////

exports.defineModule = function (path, file) {
  var content;
  var m;
  var mc;

  content = fs.read(file);

  mc = internal.db._collection('_modules');

  if (mc === null) {
    mc = internal.db._create('_modules', { isSystem: true });
  }

  path = module.normalize(path);
  m = mc.firstExample({ path: path });

  if (m === null) {
    mc.save({ path: path, content: content });
  }else {
    mc.replace(m, { path: path, content: content });
  }
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief normalizeURL
// /
// / If @FA{path} starts with "." or "..", then it is a relative path.
// / Otherwise it is an absolute path. Normalizing will remove `//`,
// / `/./`, `/../` from the url - expect in the beginning, where it keeps
// / `../` and or at most one `./`.
// /
// / If @FA{path} is empty, the url `./` will be returned.
// //////////////////////////////////////////////////////////////////////////////

exports.normalizeURL = function (path) {
  var i;
  var n;
  var p;
  var q;
  var r;
  var x;

  if (path === '') {
    return './';
  }

  p = path.split('/');

  // relative path
  if (p[0] === '.' || p[0] === '..') {
    r = p[0] + '/';
    p.shift();
    q = p;
  }

  // absolute path
  else if (p[0] === '') {
    r = '/';
    p.shift();
    q = p;
  }

  // assume that the path is relative
  else {
    r = './';
    q = p;
  }

  // normalize path
  n = [];

  for (i = 0;  i < q.length;  ++i) {
    x = q[i];

    if (x === '..') {
      if (n.length === 0) {
        if (r === '../') {
          n.push(x);
        }
        else if (r === './') {
          r = '../';
        }else {
          throw "cannot use '..' to escape top-level-directory";
        }
      }
      else if (n[n.length - 1] === '..') {
        n.push(x);
      }else {
        n.pop();
      }
    }
    else if (x !== '' && x !== '.') {
      n.push(x);
    }
  }

  return r + n.join('/');
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief inspect
// //////////////////////////////////////////////////////////////////////////////

exports.inspect = internal.inspect;

// //////////////////////////////////////////////////////////////////////////////
// / @brief output
// /
// / In order to allow "capture" output to work, we cannot assigne the
// / function here.
// //////////////////////////////////////////////////////////////////////////////

exports.output = function () {
  internal.output.apply(internal.output, arguments);
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief print
// //////////////////////////////////////////////////////////////////////////////

exports.print = internal.print;

// //////////////////////////////////////////////////////////////////////////////
// / @brief printf
// //////////////////////////////////////////////////////////////////////////////

exports.printf = internal.printf;

// //////////////////////////////////////////////////////////////////////////////
// / @brief sprintf
// //////////////////////////////////////////////////////////////////////////////

exports.sprintf = internal.sprintf;

// //////////////////////////////////////////////////////////////////////////////
// / @brief printObject
// //////////////////////////////////////////////////////////////////////////////

exports.printObject = internal.printObject;

// //////////////////////////////////////////////////////////////////////////////
// / @brief 2D ASCII table printing
// //////////////////////////////////////////////////////////////////////////////

exports.printTable = function (list, columns, options) {
  options = options || { };
  if (options.totalString === undefined) {
    options.totalString = '%s document(s)\n';
  }

  var pad = '...';
  var descriptions, matrix, col, what, j;

  if (columns === undefined) {
    what = list[0];
  }
  else if (Array.isArray(columns)) {
    what = { };

    columns.forEach(function (col) {
      what[col] = null;
    });
  }else {
    what = columns;
  }

  j = 0;
  descriptions = [];
  matrix = [ [] ];

  for (col in what) {
    if (what.hasOwnProperty(col)) {
      var fixedLength = null;

      if (columns && columns.hasOwnProperty(col) && columns[col] > 0) {
        fixedLength = columns[col] >= pad.length ? columns[col] : pad.length;
      }

      // header
      var name = col;

      // rename header?
      if (options.hasOwnProperty('rename')) {
        if (options.rename.hasOwnProperty(col)) {
          name = options.rename[col];
        }
      }

      descriptions.push({
        id: col,
        fixedLength: fixedLength,
        length: fixedLength || name.length
      });

      matrix[0][j++] = name;
    }
  }

  // determine values & max widths
  list.forEach(function (row, i) {
    matrix[i + 1] = [];
    descriptions.forEach(function (col) {
      if (row.hasOwnProperty(col.id)) {
        var value;
        if (options.prettyStrings && typeof row[col.id] === 'string') {
          value = row[col.id];
        }else {
          value = JSON.stringify(row[col.id]) || '';
        }

        matrix[i + 1].push(value);

        if (value.length > col.length && ! col.fixedLength) {
          col.length = Math.min(value.length, 100);
        }
      }else {
        // undefined
        matrix[i + 1].push('');
      }
    });
  });

  var divider = function () {
    var parts = [];
    descriptions.forEach(function (desc) {
      parts.push(exports.stringPadding('', desc.length, '-', 'r'));
    });

    if (options.framed) {
      return '+-' + parts.join('-+-') + '-+\n';
    }

    return parts.join('   ') + '\n';
  };

  var compose = function () {
    var result = '';

    if (options.framed) {
      result += divider();
    }
    matrix.forEach(function (row, i) {
      var parts = [];

      row.forEach(function (col, j) {
        var len = descriptions[j].length, value = row[j];
        if (value.length > len) {
          value = value.substr(0, len - pad.length) + pad;
        }
        parts.push(exports.stringPadding(value, len, ' ', 'r'));
      });

      if (options.framed) {
        result += '| ' + parts.join(' | ') + ' |\n';
      }else {
        result += parts.join('   ') + '\n';
      }

      if (i === 0) {
        result += divider();
      }
    });

    result += divider();

    if (! options.hideTotal) {
      result += internal.sprintf(options.totalString, String(list.length));
    }
    return result;
  };

  if (! Array.isArray(list)) {
    // not an array
    return;
  }

  if (list.length === 0) {
    exports.print(options.emptyString || 'no document(s)');
  }else {
    exports.print(compose());
  }
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief stringPadding
// //////////////////////////////////////////////////////////////////////////////

exports.stringPadding = function (str, len, pad, dir) {
  // yes, this is more code than new Array(length).join(chr), but it makes jslint happy
  function fill (length, chr) {
    var result = '', i;
    for (i = 0; i < length; ++i) {
      result += chr;
    }
    return result;
  }

  if (typeof (len) === 'undefined') {
    len = 0;
  }
  if (typeof (pad) === 'undefined') {
    pad = ' ';
  }

  if (len + 1 >= str.length) {
    switch (dir || 'r') {

      // LEFT
      case 'l':
        str = fill(len + 1 - str.length, pad) + str;
        break;

      // BOTH
      case 'b':
        var padlen = len - str.length;
        var right = Math.ceil(padlen / 2);
        var left = padlen - right;
        str = fill(left + 1, pad) + str + fill(right + 1, pad);
        break;

      default:
        str = str + fill(len + 1 - str.length, pad);
        break;
    }
  }

  return str;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief throws an error in case a download failed
// //////////////////////////////////////////////////////////////////////////////

exports.throwDownloadError = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_SERVICE_DOWNLOAD_FAILED.code,
    errorMessage: exports.errors.ERROR_SERVICE_DOWNLOAD_FAILED.message + ': ' + String(msg)
  });
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief throws an error in case of missing file
// //////////////////////////////////////////////////////////////////////////////

exports.throwFileNotFound = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_FILE_NOT_FOUND.code,
    errorMessage: exports.errors.ERROR_FILE_NOT_FOUND.message + ': ' + String(msg)
  });
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief throws an error in case of a bad parameter
// //////////////////////////////////////////////////////////////////////////////

exports.throwBadParameter = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_BAD_PARAMETER.code,
    errorMessage: exports.errors.ERROR_BAD_PARAMETER.message + ': ' + String(msg)
  });
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief checks parameter, throws an error if missing
// //////////////////////////////////////////////////////////////////////////////

exports.checkParameter = function (usage, descs, vars) {
  var i;

  for (i = 0;  i < descs.length;  ++i) {
    var desc = descs[i];

    if (typeof vars[i] === 'undefined') {
      exports.throwBadParameter(desc[0] + ' missing, usage: ' + usage);
    }

    if (typeof vars[i] !== desc[1]) {
      exports.throwBadParameter(desc[0] + " should be a '" + desc[1] + "', "
        + "not '" + (typeof vars[i]) + "'");
    }
  }
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief generate info message for newer version(s) available
// //////////////////////////////////////////////////////////////////////////////

exports.checkAvailableVersions = function (version) {
  var console = require('console');
  var log;

  if (require('@arangodb').isServer) {
    log = console.info;
  }else {
    log = internal.print;
  }

  if (version === undefined) {
    version = internal.version;
  }

  if (version.match(/beta|alpha|preview|milestone|devel/) !== null) {
    log("You are using an milestone/alpha/beta/preview version ('" + version + "') of ArangoDB");
    return;
  }

  try {
    var u = 'https://www.arangodb.com/repositories/versions.php?version=' + version +
    '&os=' + internal.platform;
    var d = internal.download(u, '', {timeout: 5});
    var v = JSON.parse(d.body);

    if (v.hasOwnProperty('bugfix')) {
      log("Please note that a new bugfix version '" + v.bugfix.version + "' is available");
    }

    if (v.hasOwnProperty('minor')) {
      log("Please note that a new minor version '" + v.minor.version + "' is available");
    }

    if (v.hasOwnProperty('major')) {
      log("Please note that a new major version '" + v.major.version + "' is available");
    }
  } catch (err) {
    if (console && console.debug) {
      console.debug('cannot check for newer version: ', err.stack);<commit_msg>fix message<commit_after>'use strict';

// //////////////////////////////////////////////////////////////////////////////
// / @brief JavaScript base module
// /
// / @file
// /
// / DISCLAIMER
// /
// / Copyright 2004-2013 triAGENS GmbH, Cologne, Germany
// /
// / Licensed under the Apache License, Version 2.0 (the "License")
// / you may not use this file except in compliance with the License.
// / You may obtain a copy of the License at
// /
// /     http://www.apache.org/licenses/LICENSE-2.0
// /
// / Unless required by applicable law or agreed to in writing, software
// / distributed under the License is distributed on an "AS IS" BASIS,
// / WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// / See the License for the specific language governing permissions and
// / limitations under the License.
// /
// / Copyright holder is triAGENS GmbH, Cologne, Germany
// /
// / @author Dr. <NAME>
// / @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
// //////////////////////////////////////////////////////////////////////////////

var internal = require('internal');
var fs = require('fs');

// //////////////////////////////////////////////////////////////////////////////
// / @brief errors
// //////////////////////////////////////////////////////////////////////////////

Object.keys(internal.errors).forEach(function (key) {
  exports[key] = internal.errors[key].code;
});

exports.aql = function (strings, ...args) {
  const bindVars = {};
  const bindVals = [];
  let query = strings[0];
  for (let i = 0; i < args.length; i++) {
    const rawValue = args[i];
    let value = rawValue;
    if (rawValue && typeof rawValue.toAQL === 'function') {
      query += `${rawValue.toAQL()}${strings[i + 1]}`;
      continue;
    }
    const index = bindVals.indexOf(rawValue);
    const isKnown = index !== -1;
    let name = `value${isKnown ? index : bindVals.length}`;
    if (rawValue && rawValue.isArangoCollection) {
      name = `@${name}`;
      value = rawValue.name();
    }
    if (!isKnown) {
      bindVals.push(rawValue);
      bindVars[name] = value;
    }
    query += `@${name}${strings[i + 1]}`;
  }
  return {query, bindVars};
};

exports.errors = internal.errors;

// //////////////////////////////////////////////////////////////////////////////
// / @brief ArangoError
// //////////////////////////////////////////////////////////////////////////////

exports.ArangoError = internal.ArangoError;

// //////////////////////////////////////////////////////////////////////////////
// / @brief defines a module
// //////////////////////////////////////////////////////////////////////////////

exports.defineModule = function (path, file) {
  var content;
  var m;
  var mc;

  content = fs.read(file);

  mc = internal.db._collection('_modules');

  if (mc === null) {
    mc = internal.db._create('_modules', { isSystem: true });
  }

  path = module.normalize(path);
  m = mc.firstExample({ path: path });

  if (m === null) {
    mc.save({ path: path, content: content });
  }else {
    mc.replace(m, { path: path, content: content });
  }
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief normalizeURL
// /
// / If @FA{path} starts with "." or "..", then it is a relative path.
// / Otherwise it is an absolute path. Normalizing will remove `//`,
// / `/./`, `/../` from the url - expect in the beginning, where it keeps
// / `../` and or at most one `./`.
// /
// / If @FA{path} is empty, the url `./` will be returned.
// //////////////////////////////////////////////////////////////////////////////

exports.normalizeURL = function (path) {
  var i;
  var n;
  var p;
  var q;
  var r;
  var x;

  if (path === '') {
    return './';
  }

  p = path.split('/');

  // relative path
  if (p[0] === '.' || p[0] === '..') {
    r = p[0] + '/';
    p.shift();
    q = p;
  }

  // absolute path
  else if (p[0] === '') {
    r = '/';
    p.shift();
    q = p;
  }

  // assume that the path is relative
  else {
    r = './';
    q = p;
  }

  // normalize path
  n = [];

  for (i = 0;  i < q.length;  ++i) {
    x = q[i];

    if (x === '..') {
      if (n.length === 0) {
        if (r === '../') {
          n.push(x);
        }
        else if (r === './') {
          r = '../';
        }else {
          throw "cannot use '..' to escape top-level-directory";
        }
      }
      else if (n[n.length - 1] === '..') {
        n.push(x);
      }else {
        n.pop();
      }
    }
    else if (x !== '' && x !== '.') {
      n.push(x);
    }
  }

  return r + n.join('/');
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief inspect
// //////////////////////////////////////////////////////////////////////////////

exports.inspect = internal.inspect;

// //////////////////////////////////////////////////////////////////////////////
// / @brief output
// /
// / In order to allow "capture" output to work, we cannot assigne the
// / function here.
// //////////////////////////////////////////////////////////////////////////////

exports.output = function () {
  internal.output.apply(internal.output, arguments);
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief print
// //////////////////////////////////////////////////////////////////////////////

exports.print = internal.print;

// //////////////////////////////////////////////////////////////////////////////
// / @brief printf
// //////////////////////////////////////////////////////////////////////////////

exports.printf = internal.printf;

// //////////////////////////////////////////////////////////////////////////////
// / @brief sprintf
// //////////////////////////////////////////////////////////////////////////////

exports.sprintf = internal.sprintf;

// //////////////////////////////////////////////////////////////////////////////
// / @brief printObject
// //////////////////////////////////////////////////////////////////////////////

exports.printObject = internal.printObject;

// //////////////////////////////////////////////////////////////////////////////
// / @brief 2D ASCII table printing
// //////////////////////////////////////////////////////////////////////////////

exports.printTable = function (list, columns, options) {
  options = options || { };
  if (options.totalString === undefined) {
    options.totalString = '%s document(s)\n';
  }

  var pad = '...';
  var descriptions, matrix, col, what, j;

  if (columns === undefined) {
    what = list[0];
  }
  else if (Array.isArray(columns)) {
    what = { };

    columns.forEach(function (col) {
      what[col] = null;
    });
  }else {
    what = columns;
  }

  j = 0;
  descriptions = [];
  matrix = [ [] ];

  for (col in what) {
    if (what.hasOwnProperty(col)) {
      var fixedLength = null;

      if (columns && columns.hasOwnProperty(col) && columns[col] > 0) {
        fixedLength = columns[col] >= pad.length ? columns[col] : pad.length;
      }

      // header
      var name = col;

      // rename header?
      if (options.hasOwnProperty('rename')) {
        if (options.rename.hasOwnProperty(col)) {
          name = options.rename[col];
        }
      }

      descriptions.push({
        id: col,
        fixedLength: fixedLength,
        length: fixedLength || name.length
      });

      matrix[0][j++] = name;
    }
  }

  // determine values & max widths
  list.forEach(function (row, i) {
    matrix[i + 1] = [];
    descriptions.forEach(function (col) {
      if (row.hasOwnProperty(col.id)) {
        var value;
        if (options.prettyStrings && typeof row[col.id] === 'string') {
          value = row[col.id];
        }else {
          value = JSON.stringify(row[col.id]) || '';
        }

        matrix[i + 1].push(value);

        if (value.length > col.length && ! col.fixedLength) {
          col.length = Math.min(value.length, 100);
        }
      }else {
        // undefined
        matrix[i + 1].push('');
      }
    });
  });

  var divider = function () {
    var parts = [];
    descriptions.forEach(function (desc) {
      parts.push(exports.stringPadding('', desc.length, '-', 'r'));
    });

    if (options.framed) {
      return '+-' + parts.join('-+-') + '-+\n';
    }

    return parts.join('   ') + '\n';
  };

  var compose = function () {
    var result = '';

    if (options.framed) {
      result += divider();
    }
    matrix.forEach(function (row, i) {
      var parts = [];

      row.forEach(function (col, j) {
        var len = descriptions[j].length, value = row[j];
        if (value.length > len) {
          value = value.substr(0, len - pad.length) + pad;
        }
        parts.push(exports.stringPadding(value, len, ' ', 'r'));
      });

      if (options.framed) {
        result += '| ' + parts.join(' | ') + ' |\n';
      }else {
        result += parts.join('   ') + '\n';
      }

      if (i === 0) {
        result += divider();
      }
    });

    result += divider();

    if (! options.hideTotal) {
      result += internal.sprintf(options.totalString, String(list.length));
    }
    return result;
  };

  if (! Array.isArray(list)) {
    // not an array
    return;
  }

  if (list.length === 0) {
    exports.print(options.emptyString || 'no document(s)');
  }else {
    exports.print(compose());
  }
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief stringPadding
// //////////////////////////////////////////////////////////////////////////////

exports.stringPadding = function (str, len, pad, dir) {
  // yes, this is more code than new Array(length).join(chr), but it makes jslint happy
  function fill (length, chr) {
    var result = '', i;
    for (i = 0; i < length; ++i) {
      result += chr;
    }
    return result;
  }

  if (typeof (len) === 'undefined') {
    len = 0;
  }
  if (typeof (pad) === 'undefined') {
    pad = ' ';
  }

  if (len + 1 >= str.length) {
    switch (dir || 'r') {

      // LEFT
      case 'l':
        str = fill(len + 1 - str.length, pad) + str;
        break;

      // BOTH
      case 'b':
        var padlen = len - str.length;
        var right = Math.ceil(padlen / 2);
        var left = padlen - right;
        str = fill(left + 1, pad) + str + fill(right + 1, pad);
        break;

      default:
        str = str + fill(len + 1 - str.length, pad);
        break;
    }
  }

  return str;
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief throws an error in case a download failed
// //////////////////////////////////////////////////////////////////////////////

exports.throwDownloadError = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_SERVICE_DOWNLOAD_FAILED.code,
    errorMessage: exports.errors.ERROR_SERVICE_DOWNLOAD_FAILED.message + ': ' + String(msg)
  });
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief throws an error in case of missing file
// //////////////////////////////////////////////////////////////////////////////

exports.throwFileNotFound = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_FILE_NOT_FOUND.code,
    errorMessage: exports.errors.ERROR_FILE_NOT_FOUND.message + ': ' + String(msg)
  });
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief throws an error in case of a bad parameter
// //////////////////////////////////////////////////////////////////////////////

exports.throwBadParameter = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_BAD_PARAMETER.code,
    errorMessage: exports.errors.ERROR_BAD_PARAMETER.message + ': ' + String(msg)
  });
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief checks parameter, throws an error if missing
// //////////////////////////////////////////////////////////////////////////////

exports.checkParameter = function (usage, descs, vars) {
  var i;

  for (i = 0;  i < descs.length;  ++i) {
    var desc = descs[i];

    if (typeof vars[i] === 'undefined') {
      exports.throwBadParameter(desc[0] + ' missing, usage: ' + usage);
    }

    if (typeof vars[i] !== desc[1]) {
      exports.throwBadParameter(desc[0] + " should be a '" + desc[1] + "', "
        + "not '" + (typeof vars[i]) + "'");
    }
  }
};

// //////////////////////////////////////////////////////////////////////////////
// / @brief generate info message for newer version(s) available
// //////////////////////////////////////////////////////////////////////////////

exports.checkAvailableVersions = function (version) {
  var console = require('console');
  var log;

  if (require('@arangodb').isServer) {
    log = console.info;
  }else {
    log = internal.print;
  }

  if (version === undefined) {
    version = internal.version;
  }

  if (version.match(/beta|alpha|preview|milestone|devel/) !== null) {
    log("You are using a milestone/alpha/beta/preview version ('" + version + "') of ArangoDB");
    return;
  }

  try {
    var u = 'https://www.arangodb.com/repositories/versions.php?version=' + version +
    '&os=' + internal.platform;
    var d = internal.download(u, '', {timeout: 5});
    var v = JSON.parse(d.body);

    if (v.hasOwnProperty('bugfix')) {
      log("Please note that a new bugfix version '" + v.bugfix.version + "' is available");
    }

    if (v.hasOwnProperty('minor')) {
      log("Please note that a new minor version '" + v.minor.version + "' is available");
    }

    if (v.hasOwnProperty('major')) {
      log("Please note that a new major version '" + v.major.version + "' is available");
    }
  } catch (err) {
    if (console && console.debug) {
      console.debug('cannot check for newer version: ', err.stack);
{'fraction_non_alphanumeric': 0.3438523262926847, 'fraction_numerical': 0.004317944658342629, 'mean_word_length': 1.2071156289707752, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 2, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2216974', 'n_tokens_mistral': 7612, 'n_tokens_neox': 7407, 'n_words': 1876}