text: import buffer from 'buffer';
  import mongodbStitchBson from 'mongodb-stitch-bson';
  
  var Buffer = buffer.Buffer;
  
  /**
   * Module dependencies.
   * @ignore
   */
  function convert(integer) {
    var str = Number(integer).toString(16);
    return str.length === 1 ? '0' + str : str;
  }
  
  function toExtendedJSON(obj) {
    var base64String = Buffer.isBuffer(obj.buffer) ? obj.buffer.toString('base64') : Buffer.from(obj.buffer).toString('base64');
  
    return {
      $binary: {
        base64: base64String,
        subType: convert(obj.sub_type)
      }
    };
  }
  
  function fromExtendedJSON(BSON, doc) {
    var type = doc.$binary.subType ? parseInt(doc.$binary.subType, 16) : 0;
  
    var data = new Buffer(doc.$binary.base64, 'base64');
  
    return new BSON.Binary(data, type);
  }
  
  var binary = {
    toExtendedJSON: toExtendedJSON,
    fromExtendedJSON: fromExtendedJSON
  };
  
  function toExtendedJSON$1(obj) {
    if (obj.scope) {
      return { $code: obj.code, $scope: obj.scope };
    }
  
    return { $code: obj.code };
  }
  
  function fromExtendedJSON$1(BSON, doc) {
    return new BSON.Code(doc.$code, doc.$scope);
  }
  
  var code = {
    toExtendedJSON: toExtendedJSON$1,
    fromExtendedJSON: fromExtendedJSON$1
  };
  
  function toExtendedJSON$2(obj) {
    var o = {
      $ref: obj.collection,
      $id: obj.oid
    };
    if (obj.db) o.$db = obj.db;
    o = Object.assign(o, obj.fields);
    return o;
  }
  
  function fromExtendedJSON$2(BSON, doc) {
    var copy = Object.assign({}, doc);
    ['$ref', '$id', '$db'].forEach(function (k) {
      return delete copy[k];
    });
    return new BSON.DBRef(doc.$ref, doc.$id, doc.$db, copy);
  }
  
  var db_ref = {
    toExtendedJSON: toExtendedJSON$2,
    fromExtendedJSON: fromExtendedJSON$2
  };
  
  function toExtendedJSON$3(obj) {
    return { $numberDecimal: obj.toString() };
  }
  
  function fromExtendedJSON$3(BSON, doc) {
    return new BSON.Decimal128.fromString(doc.$numberDecimal);
  }
  
  var decimal128 = {
    toExtendedJSON: toExtendedJSON$3,
    fromExtendedJSON: fromExtendedJSON$3
  };
  
  function toExtendedJSON$4(obj, options) {
    if (options && options.relaxed && isFinite(obj.value)) return obj.value;
    return { $numberDouble: obj.value.toString() };
  }
  
  function fromExtendedJSON$4(BSON, doc, options) {
    return options && options.relaxed ? parseFloat(doc.$numberDouble) : new BSON.Double(parseFloat(doc.$numberDouble));
  }
  
  var double_1 = {
    toExtendedJSON: toExtendedJSON$4,
    fromExtendedJSON: fromExtendedJSON$4
  };
  
  function toExtendedJSON$5(obj, options) {
    if (options && options.relaxed) return obj.value;
    return { $numberInt: obj.value.toString() };
  }
  
  function fromExtendedJSON$5(BSON, doc, options) {
    return options && options.relaxed ? parseInt(doc.$numberInt, 10) : new BSON.Int32(doc.$numberInt);
  }
  
  var int_32 = {
    toExtendedJSON: toExtendedJSON$5,
    fromExtendedJSON: fromExtendedJSON$5
  };
  
  function toExtendedJSON$6(obj, options) {
    if (options && options.relaxed) return obj.toNumber();
    return { $numberLong: obj.toString() };
  }
  
  function fromExtendedJSON$6(BSON, doc, options) {
    var result = BSON.Long.fromString(doc.$numberLong);
    return options && options.relaxed ? result.toNumber() : result;
  }
  
  var long_1 = {
    toExtendedJSON: toExtendedJSON$6,
    fromExtendedJSON: fromExtendedJSON$6
  };
  
  function toExtendedJSON$7() {
    return { $maxKey: 1 };
  }
  
  function fromExtendedJSON$7(BSON) {
    return new BSON.MaxKey();
  }
  
  var max_key = {
    toExtendedJSON: toExtendedJSON$7,
    fromExtendedJSON: fromExtendedJSON$7
  };
  
  function toExtendedJSON$8() {
    return { $minKey: 1 };
  }
  
  function fromExtendedJSON$8(BSON) {
    return new BSON.MinKey();
  }
  
  var min_key = {
    toExtendedJSON: toExtendedJSON$8,
    fromExtendedJSON: fromExtendedJSON$8
  };
  
  function toExtendedJSON$9(obj) {
    if (obj.toHexString) return { $oid: obj.toHexString() };
    return { $oid: obj.toString('hex') };
  }
  
  function fromExtendedJSON$9(BSON, doc) {
    return new BSON.ObjectID(doc.$oid);
  }
  
  var objectid = {
    toExtendedJSON: toExtendedJSON$9,
    fromExtendedJSON: fromExtendedJSON$9
  };
  
  function toExtendedJSON$10(obj) {
    return { $regularExpression: { pattern: obj.pattern, options: obj.options } };
  }
  
  function fromExtendedJSON$10(BSON, doc) {
    return new BSON.BSONRegExp(doc.$regularExpression.pattern, doc.$regularExpression.options.split('').sort().join(''));
  }
  
  var regexp = {
    toExtendedJSON: toExtendedJSON$10,
    fromExtendedJSON: fromExtendedJSON$10
  };
  
  function toExtendedJSON$11(obj) {
    return { $symbol: obj.value };
  }
  
  function fromExtendedJSON$11(BSON, doc) {
    return new BSON.Symbol(doc.$symbol);
  }
  
  var symbol = {
    toExtendedJSON: toExtendedJSON$11,
    fromExtendedJSON: fromExtendedJSON$11
  };
  
  function toExtendedJSON$12(obj) {
    return {
      $timestamp: {
        t: obj.high_,
        i: obj.low_
      }
    };
  }
  
  function fromExtendedJSON$12(BSON, doc) {
    return new BSON.Timestamp(doc.$timestamp.i, doc.$timestamp.t);
  }
  
  var timestamp = {
    toExtendedJSON: toExtendedJSON$12,
    fromExtendedJSON: fromExtendedJSON$12
  };
  
  var bson = {
    Binary: binary,
    Code: code,
    DBRef: db_ref,
    Decimal128: decimal128,
    Double: double_1,
    Int32: int_32,
    Long: long_1,
    MaxKey: max_key,
    MinKey: min_key,
    ObjectID: objectid,
    BSONRegExp: regexp,
    Symbol: symbol,
    Timestamp: timestamp
  };
  
  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
    return typeof obj;
  } : function (obj) {
    return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  };
  
  var BSON = mongodbStitchBson;
  
  var BSONTypes = ['Binary', 'Code', 'DBRef', 'Decimal128', 'Double', 'Int32', 'Long', 'MaxKey', 'MinKey', 'ObjectID', 'BSONRegExp', 'Symbol', 'Timestamp'];
  
  setBSONModule(BSON);
  
  // all the types where we don't need to do any special processing and can just pass the EJSON
  //straight to type.fromExtendedJSON
  var keysToCodecs = {
    $oid: bson.ObjectID,
    $binary: bson.Binary,
    $symbol: bson.Symbol,
    $numberInt: bson.Int32,
    $numberDecimal: bson.Decimal128,
    $numberDouble: bson.Double,
    $numberLong: bson.Long,
    $minKey: bson.MinKey,
    $maxKey: bson.MaxKey,
    $regularExpression: bson.BSONRegExp,
    $timestamp: bson.Timestamp
  };
  
  function setBSONModule(module) {
    BSONTypes.forEach(function (t) {
      if (!module[t]) throw new Error('passed in module does not contain all BSON types required');
    });
    BSON = module;
  }
  
  function deserializeValue(self, key, value, options) {
    if (typeof value === 'number') {
      // if it's an integer, should interpret as smallest BSON integer
      // that can represent it exactly. (if out of range, interpret as double.)
      if (Math.floor(value) === value) {
        var int32Range = value >= BSON_INT32_MIN && value <= BSON_INT32_MAX,
            int64Range = value >= BSON_INT64_MIN && value <= BSON_INT64_MAX;
  
        if (int32Range) return options.strict ? new BSON.Int32(value) : value;
        if (int64Range) return options.strict ? new BSON.Long.fromNumber(value) : value;
      }
      // If the number is a non-integer or out of integer range, should interpret as BSON Double.
      return new BSON.Double(value);
    }
  
    // from here on out we're looking for bson types, so bail if its not an object
    if (value == null || (typeof value === 'undefined' ? 'undefined' : _typeof(value)) !== 'object') return value;
  
    // upgrade deprecated undefined to null
    if (value.$undefined) return null;
  
    var keys = Object.keys(value).filter(function (k) {
      return k.startsWith('$') && value[k] != null;
    });
    for (var i = 0; i < keys.length; i++) {
      var c = keysToCodecs[keys[i]];
      if (c) return c.fromExtendedJSON(BSON, value, options);
    }
  
    if (value.$date != null) {
      var d = value.$date,
          date = new Date();
  
      if (typeof d === 'string') date.setTime(Date.parse(d));else if (d instanceof BSON.Long) date.setTime(d.toNumber());else if (typeof d === 'number' && options.relaxed) date.setTime(d);
      return date;
    }
  
    if (value.$code != null) {
      if (value.$scope) var scope = deserializeValue(self, null, value.$scope);
      var copy = Object.assign({}, value);
      copy.$scope = scope;
      return bson.Code.fromExtendedJSON(BSON, value);
    }
  
    if (value.$ref != null || value.$dbPointer != null) {
      var v = value.$ref ? value : value.$dbPointer;
  
      // we run into this in a "degenerate EJSON" case (with $id and $ref order flipped)
      // because of the order JSON.parse goes through the document
      if (v instanceof BSON.DBRef) return v;
  
      var dollarKeys = Object.keys(v).filter(function (k) {
        return k.startsWith('$');
      }),
          valid = true;
      dollarKeys.forEach(function (k) {
        if (['$ref', '$id', '$db'].indexOf(k) === -1) valid = false;
      });
  
      // only make DBRef if $ keys are all valid
      if (valid) return bson.DBRef.fromExtendedJSON(BSON, v);
    }
  
    return value;
  }
  
  var parse = function parse(text, options) {
    var self = this;
    options = options || { relaxed: false };
  
    // relaxed implies not strict
    if (typeof options.relaxed === 'boolean') options.strict = !options.relaxed;
    if (typeof options.strict === 'boolean') options.relaxed = !options.strict;
  
    return JSON.parse(text, function (key, value) {
      return deserializeValue(self, key, value, options);
    });
  };
  
  //
  // Serializer
  //
  
  // MAX INT32 boundaries
  var BSON_INT32_MAX = 0x7fffffff,
      BSON_INT32_MIN = -0x80000000,
      BSON_INT64_MAX = 0x7fffffffffffffff,
      BSON_INT64_MIN = -0x8000000000000000;
  
  var stringify = function stringify(value, reducer, indents, options) {
    var opts = {};
    if (options != null && (typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') opts = options;else if (indents != null && (typeof indents === 'undefined' ? 'undefined' : _typeof(indents)) === 'object') {
      opts = indents;
      indents = 0;
    } else if (reducer != null && (typeof reducer === 'undefined' ? 'undefined' : _typeof(reducer)) === 'object') {
      opts = reducer;
      reducer = null;
    }
  
    var doc = Array.isArray(value) ? serializeArray(value, opts) : serializeDocument(value, opts);
    return JSON.stringify(doc, reducer, indents);
  };
  
  function serializeArray(array, options) {
    return array.map(function (v) {
      return serializeValue(v, options);
    });
  }
  
  function getISOString(date) {
    var isoStr = date.toISOString();
    // we should only show milliseconds in timestamp if they're non-zero
    return date.getUTCMilliseconds() !== 0 ? isoStr : isoStr.slice(0, -5) + 'Z';
  }
  
  function serializeValue(value, options) {
    if (Array.isArray(value)) return serializeArray(value, options);
  
    if (value === undefined) return null;
  
    if (value instanceof Date) {
      var dateNum = value.getTime(),
  
      // is it in year range 1970-9999?
      inRange = dateNum > -1 && dateNum < 253402318800000;
  
      return options.relaxed && inRange ? { $date: getISOString(value) } : { $date: { $numberLong: value.getTime().toString() } };
    }
  
    if (typeof value === 'number' && !options.relaxed) {
      // it's an integer
      if (Math.floor(value) === value) {
        var int32Range = value >= BSON_INT32_MIN && value <= BSON_INT32_MAX,
            int64Range = value >= BSON_INT64_MIN && value <= BSON_INT64_MAX;
  
        // interpret as being of the smallest BSON integer type that can represent the number exactly
        if (int32Range) return { $numberInt: value.toString() };
        if (int64Range) return { $numberLong: value.toString() };
      }
      return { $numberDouble: value.toString() };
    }
  
    if (value != null && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') return serializeDocument(value, options);
    return value;
  }
  
  function serializeDocument(doc, options) {
    if (doc == null || (typeof doc === 'undefined' ? 'undefined' : _typeof(doc)) !== 'object') throw new Error('not an object instance');
  
    // the document itself is a BSON type
    if (doc._bsontype && BSONTypes.indexOf(doc._bsontype) !== -1) {
      // we need to separately serialize the embedded scope document
      if (doc._bsontype === 'Code' && doc.scope) {
        var tempScope = serializeDocument(doc.scope, options),
            tempDoc = Object.assign({}, doc, { scope: tempScope });
        return bson['Code'].toExtendedJSON(tempDoc, options);
        // we need to separately serialize the embedded OID document
      } else if (doc._bsontype === 'DBRef' && doc.oid) {
        var tempId = serializeDocument(doc.oid, options),
            _tempDoc = Object.assign({}, doc, { oid: tempId });
        return bson['DBRef'].toExtendedJSON(_tempDoc, options);
      }
      return bson[doc._bsontype].toExtendedJSON(doc, options);
    }
  
    // the document is an object with nested BSON types
    var _doc = {};
    for (var name in doc) {
      var val = doc[name];
      if (Array.isArray(val)) {
        _doc[name] = serializeArray(val, options);
      } else if (val != null && val._bsontype && BSONTypes.indexOf(val._bsontype) !== -1) {
        // we need to separately serialize the embedded scope document
        if (val._bsontype === 'Code' && val.scope) {
          var _tempScope = serializeDocument(val.scope, options),
              tempVal = Object.assign({}, val, { scope: _tempScope });
          _doc[name] = bson['Code'].toExtendedJSON(tempVal, options);
          // we need to separately serialize the embedded OID document
        } else if (val._bsontype === 'DBRef' && val.oid) {
          var _tempId = serializeDocument(val.oid, options),
              _tempVal = Object.assign({}, val, { oid: _tempId });
          _doc[name] = bson['DBRef'].toExtendedJSON(_tempVal, options);
        } else _doc[name] = bson[val._bsontype].toExtendedJSON(val, options);
      } else if (val instanceof Date) {
        _doc[name] = serializeValue(val, options);
      } else if (val != null && (typeof val === 'undefined' ? 'undefined' : _typeof(val)) === 'object') {
        _doc[name] = serializeDocument(val, options);
      }
      _doc[name] = serializeValue(val, options);
      if (val instanceof RegExp) {
        var flags = val.flags;
        if (flags === undefined) {
          flags = val.toString().match(/[gimuy]*$/)[0];
        }
        _doc[name] = bson['BSONRegExp'].toExtendedJSON({ pattern: val.source, options: flags });
      }
    }
  
    return _doc;
  }
  
  var ext_json = {
    parse: parse,
    stringify: stringify,
    setBSONModule: setBSONModule,
    BSON: BSON
  };
  
  var mongodbExtjson = {
    parse: ext_json.parse,
    stringify: ext_json.stringify,
    setBSONModule: ext_json.setBSONModule,
    BSON: ext_json.BSON
  };
  var mongodbExtjson_1 = mongodbExtjson.parse;
  var mongodbExtjson_2 = mongodbExtjson.stringify;
  var mongodbExtjson_3 = mongodbExtjson.setBSONModule;
  var mongodbExtjson_4 = mongodbExtjson.BSON;
  
  export default mongodbExtjson;
  export { mongodbExtjson_1 as parse, mongodbExtjson_2 as stringify, mongodbExtjson_3 as setBSONModule, mongodbExtjson_4 as BSON };
  