<commit_before>var PROMISE_TIMEOUT = 5000, // 5 seconds
    ZAF_EVENT       = /^zaf\./,
    version         = require('version'),
    Promise         = window.Promise || require('../vendor/native-promise-only'),
    when            = require('utils').when,
    repliesPending  = {},
    pendingPromises = {},
    ids             = {};

function nextIdFor(name) {
  if (isNaN(ids[name])) {
    ids[name] = 0;
  }
  return ++ids[name];
}

function rawPostMessage(client, msg, forceReady) {
  if (client.ready || forceReady) {
    client._source.postMessage(msg, client._origin);
  } else {
    client.on('app.registered', rawPostMessage.bind(null, client, msg));
  }
}

function wrappedPostMessage(name) {
  var id = nextIdFor('promise'),
      timeoutId,
      params = Array.prototype.slice.call(arguments, 1);

  var promise = new Promise(function(resolve, reject) {
    // Time out the promise to ensure it will be garbage collected if nobody responds
    timeoutId = setTimeout(function() {
      reject(new Error('Invocation request timeout'));
    }, PROMISE_TIMEOUT);

    pendingPromises[id] = {resolve: resolve, reject: reject};

    var msg = JSON.stringify({ id: id, request: name, params: params, appGuid: this._appGuid });
    rawPostMessage(this, msg);
  }.bind(this));

  // ensure promise is cleaned up when resolved
  return promise.then(removePromise.bind(null, id, timeoutId), removePromise.bind(null, id, timeoutId));
}

function removePromise(id, timeoutId, args) {
  clearTimeout(timeoutId);
  delete pendingPromises[id];
  if (args instanceof Error) throw args;
  return args;
}

function isValidEvent(client, event) {
  return client._origin === event.origin && client._source === event.source;
}

function messageHandler(client, event) {
  if (!isValidEvent(client, event)) { return; }

  var data = event.data;

  if (!data) { return; }

  if (typeof data === 'string') {
    try {
      data = JSON.parse(event.data);
    } catch (e) {
      return e;
    }
  }

  var pendingPromise;
  if (data.id && (pendingPromise = pendingPromises[data.id])) {
    if (data.error) {
      var err = data.error;
      if (err.code) {
        err = new Error(data.error.msg);
        err.name = data.error.code;
        err.stack = data.error.stack;
      }
      pendingPromise.reject(err);
    } else {
      pendingPromise.resolve(data.result);
    }
  } else if (ZAF_EVENT.test(data.key)) {
    var key = data.key.replace(ZAF_EVENT, '');
    if (client) {
      client.trigger(key, data.message);
    }
  }
}

var Client = function(origin, appGuid) {
  this._origin = origin;
  this._source = window.parent;
  this._appGuid = appGuid;
  this._messageHandlers = {};
  this._metadata = null;
  this._context = null;
  this.ready = false;

  this.on('app.registered', function(data) {
    this.ready = true;
    this._metadata = data.metadata;
    this._context = data.context;
  }, this);

  this.on('context.updated', function(context) {
    this._context = context;
  }, this);

  this.postMessage('iframe.handshake', { version: version });

  window.addEventListener('message', messageHandler.bind(null, this));
};

Client.prototype = {

  /// ### Client Object
  ///
  /// #### client.postMessage(name, [data])
  ///
  /// Allows you to send message events to the Zendesk app.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the message event. This determines the name of the iframe
  ///     event your app will receive. For example, if you set this to 'hello', your app will receive
  ///     the event 'iframe.hello'
  ///   * `data` (optional) a JSON object with any data that you want to pass along with the
  ///     event
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  /// client.postMessage('hello', { awesome: true });
  /// ```
  postMessage: function(name, data) {
    var msg = JSON.stringify({ key: name, message: data, appGuid: this._appGuid });
    rawPostMessage(this, msg, name === 'iframe.handshake');
  },

  /// #### client.on(name, handler, [context])
  ///
  /// Allows you to add handlers to a framework event. You can add as many handler as you wish.
  /// They will be executed in the order they were added.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the framework event you want to listen to. This can be
  ///     [framework](./events.html#framework-events), [request](./events.html#request-events), or
  ///     [custom](./events.html#custom-events) events. Your iframe can listen to any events your app
  ///     receives, apart from DOM events. You don't need to register these events in the app first
  ///   * `handler` a function to be called when this event fires. You can expect to receive the same
  ///     event object your app would receive, parsed as JSON
  ///   * `context` (optional) the value of `this` within your handler
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  /// client.on('app.registered', function(e) {
  ///   // go nuts
  /// });
  /// ```
  ///
  /// Note: As soon as communication with the Zendesk app is established, the SDK triggers an
  /// `app.registered` event. You can add as many handlers to `app.registered` as you like. They're
  /// called immediately after the `init` callback.
  on: function(name, handler, context) {
    if (typeof handler == 'function') {
      var id = nextIdFor('event');

      this._messageHandlers[name] = this._messageHandlers[name] || [];
      this._messageHandlers[name].push(handler.bind(context || null));

      if (name !== 'app.registered') {
        this.postMessage('iframe.on:' + name, { id: id });
      }
    }
  },

  /// #### client.off(name, handler)
  ///
  /// Allows you to remove a handler for a framework event.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event
  ///   * `handler` the function you attached earlier with `on`
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('app.registered', function appRegistered(e) {
  ///   // do stuff then remove the handler
  ///   client.off('app.registered', appRegistered);
  /// });
  /// ```
  off: function(name, handler) {
    if (!this._messageHandlers[name]) { return false; }
    var index = this._messageHandlers[name].indexOf(handler);
    return this._messageHandlers[name].splice(index, 1)[0];
  },

  /// #### client.has(name, handler)
  ///
  /// Returns whether or not an event has the specified handler attached to it.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event
  ///   * `handler` the handler you want to test
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('app.registered', function appRegistered(e) {
  ///   // do stuff
  /// });
  ///
  /// client.has('app.registered', appRegistered); // true
  /// client.has('app.activated', appRegistered); // false
  /// ```
  has: function(name, handler) {
    if (!this._messageHandlers[name]) { return false; }
    return this._messageHandlers[name].indexOf(handler) !== -1;
  },

  /// #### client.trigger(name, [data])
  ///
  /// Triggers the specified event on the client.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event you want to trigger
  ///   * `data` (optional) data you want to pass to the handler
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('activation', function {
  ///   console.log('activating!')
  /// });
  ///
  /// client.trigger('activation') // activating!
  /// ```
  trigger: function(name, data) {
    if (!this._messageHandlers[name] || repliesPending[name]) { return false; }
    if (data && data.reply) {
      repliesPending[name] = true;
      // When receives a list of things to wrap as Promises,
      // it will then wait for:
      //  a) all to settle
      //  b) first to reject
      //  c) first to settle with a falsy value (?)
      // c) is sub-optimal, since it has a specific narrow use-case
      // for save hooks :(
      when(this._messageHandlers[name]).then(
        this.postMessage.bind(this, 'iframe.reply:' + name)
      ).catch(function(reason) {
        this.postMessage('iframe.reply:' + name, { error: true, reason: reason });
      }.bind(this)).then(
        function() { delete repliesPending[name]; }
      );
    } else {
      this._messageHandlers[name].forEach(function(handler) {
        handler(data);
      });
    }
  },

  /// #### client.request(options)
  ///
  /// Dispatch [requests](./requests) via the Zendesk app.
  ///
  /// ##### Arguments
  ///
  ///   * `options` the url of the request or an options object containing a url key/value
  ///
  /// ##### Returns
  ///
  /// A [Promises/A+](https://promisesaplus.com) conformant `promise` object.
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.request('/api/v2/tickets.json').then(
  ///   function(tickets) {
  ///     console.log(tickets);
  ///   },
  ///   function(response) {
  ///     console.error(response.responseText);
  ///   }
  /// );
  /// ```
  request: function(options) {
    var requestKey = 'request:' + nextIdFor('request');

    return new Promise(function(resolve, reject) {
      if (typeof options === 'string') {
        options = { url: options };
      }

      this.on(requestKey + '.done', function(evt) {
        resolve.apply(this, evt.responseArgs);
      });

      this.on(requestKey + '.fail', function(evt) {
        reject.apply(this, evt.responseArgs);
      });

      this.postMessage(requestKey, options);
    }.bind(this));
  },

  metadata: function() {
    return new Promise(function(resolve) {
      if (this._metadata) {
        resolve(this._metadata);
      } else {
        this.on('app.registered', function() {
          resolve(this._metadata);
        }.bind(this));
      }
    }.bind(this));
  },

  context: function() {
    return new Promise(function(resolve) {
      if (this._context) {
        resolve(this._context);
      } else {
        this.on('app.registered', function() {
          resolve(this._context);
        }.bind(this));
      }
    }.bind(this));
  },

  get: function(path) {
    var paths = Array.isArray(path) ? path : [path];
    return wrappedPostMessage.call(this, 'get', paths);
  },

  set: function(key, val) {
    var obj = key;
    if (typeof key === 'string') {
      obj = {};
      obj[key] = val;
    }
    return wrappedPostMessage.call(this, 'set', obj);
  },

  invoke: function() {
    return wrappedPostMessage.bind(this, 'invoke').apply(null, arguments);
  }
};

module.exports = Client;
<commit_msg>Makes repliesPending a property of the client, which is more future-proof in the event of multiple clients<commit_after>var PROMISE_TIMEOUT = 5000, // 5 seconds
    ZAF_EVENT       = /^zaf\./,
    version         = require('version'),
    Promise         = window.Promise || require('../vendor/native-promise-only'),
    when            = require('utils').when,
    pendingPromises = {},
    ids             = {};

function nextIdFor(name) {
  if (isNaN(ids[name])) {
    ids[name] = 0;
  }
  return ++ids[name];
}

function rawPostMessage(client, msg, forceReady) {
  if (client.ready || forceReady) {
    client._source.postMessage(msg, client._origin);
  } else {
    client.on('app.registered', rawPostMessage.bind(null, client, msg));
  }
}

function wrappedPostMessage(name) {
  var id = nextIdFor('promise'),
      timeoutId,
      params = Array.prototype.slice.call(arguments, 1);

  var promise = new Promise(function(resolve, reject) {
    // Time out the promise to ensure it will be garbage collected if nobody responds
    timeoutId = setTimeout(function() {
      reject(new Error('Invocation request timeout'));
    }, PROMISE_TIMEOUT);

    pendingPromises[id] = {resolve: resolve, reject: reject};

    var msg = JSON.stringify({ id: id, request: name, params: params, appGuid: this._appGuid });
    rawPostMessage(this, msg);
  }.bind(this));

  // ensure promise is cleaned up when resolved
  return promise.then(removePromise.bind(null, id, timeoutId), removePromise.bind(null, id, timeoutId));
}

function removePromise(id, timeoutId, args) {
  clearTimeout(timeoutId);
  delete pendingPromises[id];
  if (args instanceof Error) throw args;
  return args;
}

function isValidEvent(client, event) {
  return client._origin === event.origin && client._source === event.source;
}

function messageHandler(client, event) {
  if (!isValidEvent(client, event)) { return; }

  var data = event.data;

  if (!data) { return; }

  if (typeof data === 'string') {
    try {
      data = JSON.parse(event.data);
    } catch (e) {
      return e;
    }
  }

  var pendingPromise;
  if (data.id && (pendingPromise = pendingPromises[data.id])) {
    if (data.error) {
      var err = data.error;
      if (err.code) {
        err = new Error(data.error.msg);
        err.name = data.error.code;
        err.stack = data.error.stack;
      }
      pendingPromise.reject(err);
    } else {
      pendingPromise.resolve(data.result);
    }
  } else if (ZAF_EVENT.test(data.key)) {
    var key = data.key.replace(ZAF_EVENT, '');
    if (client) {
      client.trigger(key, data.message);
    }
  }
}

var Client = function(origin, appGuid) {
  this._origin = origin;
  this._source = window.parent;
  this._appGuid = appGuid;
  this._messageHandlers = {};
  this._repliesPending = {};
  this._metadata = null;
  this._context = null;
  this.ready = false;

  this.on('app.registered', function(data) {
    this.ready = true;
    this._metadata = data.metadata;
    this._context = data.context;
  }, this);

  this.on('context.updated', function(context) {
    this._context = context;
  }, this);

  this.postMessage('iframe.handshake', { version: version });

  window.addEventListener('message', messageHandler.bind(null, this));
};

Client.prototype = {

  /// ### Client Object
  ///
  /// #### client.postMessage(name, [data])
  ///
  /// Allows you to send message events to the Zendesk app.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the message event. This determines the name of the iframe
  ///     event your app will receive. For example, if you set this to 'hello', your app will receive
  ///     the event 'iframe.hello'
  ///   * `data` (optional) a JSON object with any data that you want to pass along with the
  ///     event
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  /// client.postMessage('hello', { awesome: true });
  /// ```
  postMessage: function(name, data) {
    var msg = JSON.stringify({ key: name, message: data, appGuid: this._appGuid });
    rawPostMessage(this, msg, name === 'iframe.handshake');
  },

  /// #### client.on(name, handler, [context])
  ///
  /// Allows you to add handlers to a framework event. You can add as many handler as you wish.
  /// They will be executed in the order they were added.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the framework event you want to listen to. This can be
  ///     [framework](./events.html#framework-events), [request](./events.html#request-events), or
  ///     [custom](./events.html#custom-events) events. Your iframe can listen to any events your app
  ///     receives, apart from DOM events. You don't need to register these events in the app first
  ///   * `handler` a function to be called when this event fires. You can expect to receive the same
  ///     event object your app would receive, parsed as JSON
  ///   * `context` (optional) the value of `this` within your handler
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  /// client.on('app.registered', function(e) {
  ///   // go nuts
  /// });
  /// ```
  ///
  /// Note: As soon as communication with the Zendesk app is established, the SDK triggers an
  /// `app.registered` event. You can add as many handlers to `app.registered` as you like. They're
  /// called immediately after the `init` callback.
  on: function(name, handler, context) {
    if (typeof handler == 'function') {
      var id = nextIdFor('event');

      this._messageHandlers[name] = this._messageHandlers[name] || [];
      this._messageHandlers[name].push(handler.bind(context || null));

      if (name !== 'app.registered') {
        this.postMessage('iframe.on:' + name, { id: id });
      }
    }
  },

  /// #### client.off(name, handler)
  ///
  /// Allows you to remove a handler for a framework event.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event
  ///   * `handler` the function you attached earlier with `on`
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('app.registered', function appRegistered(e) {
  ///   // do stuff then remove the handler
  ///   client.off('app.registered', appRegistered);
  /// });
  /// ```
  off: function(name, handler) {
    if (!this._messageHandlers[name]) { return false; }
    var index = this._messageHandlers[name].indexOf(handler);
    return this._messageHandlers[name].splice(index, 1)[0];
  },

  /// #### client.has(name, handler)
  ///
  /// Returns whether or not an event has the specified handler attached to it.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event
  ///   * `handler` the handler you want to test
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('app.registered', function appRegistered(e) {
  ///   // do stuff
  /// });
  ///
  /// client.has('app.registered', appRegistered); // true
  /// client.has('app.activated', appRegistered); // false
  /// ```
  has: function(name, handler) {
    if (!this._messageHandlers[name]) { return false; }
    return this._messageHandlers[name].indexOf(handler) !== -1;
  },

  /// #### client.trigger(name, [data])
  ///
  /// Triggers the specified event on the client.
  ///
  /// ##### Arguments
  ///
  ///   * `name` the name of the event you want to trigger
  ///   * `data` (optional) data you want to pass to the handler
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.on('activation', function {
  ///   console.log('activating!')
  /// });
  ///
  /// client.trigger('activation') // activating!
  /// ```
  trigger: function(name, data) {
    if (!this._messageHandlers[name] || this._repliesPending[name]) { return false; }
    if (data && data.reply) {
      this._repliesPending[name] = true;
      when(this._messageHandlers[name]).then(
        this.postMessage.bind(this, 'iframe.reply:' + name)
      ).catch(function(reason) {
        this.postMessage('iframe.reply:' + name, { error: true, reason: reason });
      }.bind(this)).then(
        function() { delete this._repliesPending[name]; }
      );
    } else {
      this._messageHandlers[name].forEach(function(handler) {
        handler(data);
      });
    }
  },

  /// #### client.request(options)
  ///
  /// Dispatch [requests](./requests) via the Zendesk app.
  ///
  /// ##### Arguments
  ///
  ///   * `options` the url of the request or an options object containing a url key/value
  ///
  /// ##### Returns
  ///
  /// A [Promises/A+](https://promisesaplus.com) conformant `promise` object.
  ///
  /// ```javascript
  /// var client = ZAFClient.init();
  ///
  /// client.request('/api/v2/tickets.json').then(
  ///   function(tickets) {
  ///     console.log(tickets);
  ///   },
  ///   function(response) {
  ///     console.error(response.responseText);
  ///   }
  /// );
  /// ```
  request: function(options) {
    var requestKey = 'request:' + nextIdFor('request');

    return new Promise(function(resolve, reject) {
      if (typeof options === 'string') {
        options = { url: options };
      }

      this.on(requestKey + '.done', function(evt) {
        resolve.apply(this, evt.responseArgs);
      });

      this.on(requestKey + '.fail', function(evt) {
        reject.apply(this, evt.responseArgs);
      });

      this.postMessage(requestKey, options);
    }.bind(this));
  },

  metadata: function() {
    return new Promise(function(resolve) {
      if (this._metadata) {
        resolve(this._metadata);
      } else {
        this.on('app.registered', function() {
          resolve(this._metadata);
        }.bind(this));
      }
    }.bind(this));
  },

  context: function() {
    return new Promise(function(resolve) {
      if (this._context) {
        resolve(this._context);
      } else {
        this.on('app.registered', function() {
          resolve(this._context);
        }.bind(this));
      }
    }.bind(this));
  },

  get: function(path) {
    var paths = Array.isArray(path) ? path : [path];
    return wrappedPostMessage.call(this, 'get', paths);
  },

  set: function(key, val) {
    var obj = key;
    if (typeof key === 'string') {
      obj = {};
      obj[key] = val;
    }
    return wrappedPostMessage.call(this, 'set', obj);
  },

  invoke: function() {
    return wrappedPostMessage.bind(this, 'invoke').apply(null, arguments);
  }
};

module.exports = Client;
<|endoftext|>


<commit_before>
// show the installed versions of a package

module.exports = exports = ls

var npm = require("../npm")
  , readInstalled = require("./utils/read-installed")
  , registry = require("./utils/registry")
  , semver = require("./utils/semver")
  , output = require("./utils/output")

ls.usage = "npm ls [some search terms ...]"

function ls (args, cb_) {
  readInstalled([], function (er, installed) {
    if (er) return cb_(er)
    registry.get(function (er, remote) {
      if (er) remote = {}
      var info = merge(installed, remote)
        , pretty = prettify(info, args)
      function cb () { cb_(null, info) }
      output.write(npm.config.get("outfd"), pretty, cb)
    })
  })
}
function strcmp (a, b) { return a > b ? 1 : -1 }
function prettify (data, args) {
  var pkgs = Object.keys(data).sort(strcmp)
    , attrs = []
    , names = []
    , pretty = []
    , beginAttrList = 28
  pkgs.forEach(function (name) {
    var pkg = data[name]
    Object.keys(pkg.versions).sort(semver.compare).forEach(function (v) {
      var ver = pkg.versions[v]
        , p = []
      ver.tags = ver.tags.length === 0
               ? ""
               : (ver.tags.join(" "))
      for (var i in ver) if (ver[i]) {
        p.push((typeof ver[i] === "string") ? ver[i] : i)
      }
      if (pkg.maintainers) pkg.maintainers.forEach(function (m) {
        p.push('='+m.name)
      })
      names.push(name + "@" + v)
      attrs.push(p.sort(strcmp).join(" "))
    })
  })
  for (var n = 0, l = names.length; n < l; n ++) {
    pretty.push({name:names[n], attrs:attrs[n]})
  }
  var colors = [36, 32, 33, 31, 35 ]
    , c = 0
    , l = colors.length
  var maxNameLen = 0
  if (args) pretty = pretty.filter(function (line) {
    var keep = true
    args.forEach(function (arg) {
      keep = keep && (line.name.match(arg) || line.attrs.match(arg))
    })
    return keep
  })
  var space = "                       "
  pretty.forEach(function (line) {
    maxNameLen = Math.min(Math.max(maxNameLen, line.name.length), space.length)
  })
  maxNameLen += 2

  // now turn each line obj into a single line, with only as much ws as necessary.
  pretty = pretty.map(function (line) {
    var addSpace = maxNameLen - line.name.length
    return line.name + (space.substr(0, addSpace) || "") + " " + line.attrs
  })

  if (args) args.forEach(function (arg) {
    pretty = pretty.map(function (line) {
      return line.split(arg).join("\033["+colors[c]+"m" + arg + "\033[m")
    })
    c = (c + 1) % l
  })
  if (!pretty.length) pretty = ["Nothing found"]
  pretty.push("")
  return pretty.join("\n")
}
function merge (installed, remote) {
  var merged = {}
  // first, just copy the installed stuff
  for (var p in installed) {
    merged[p] = {versions:{}}
    for (var v in installed[p]) {
      merged[p].versions[v] = { installed : true, tags : [] }
      for (var s in installed[p][v]) {
        merged[p].versions[v][s] = installed[p][v][s]
      }
    }
  }
  // now merge in the remote stuff.
  for (var p in remote) {
    merged[p] = merged[p] || {versions:{}}
    for (var d in remote[p]) if (!merged[p].hasOwnProperty(d)) {
      merged[p][d] = remote[p][d]
    }
    for (var v in remote[p].versions) {
      merged[p].versions[v] = merged[p].versions[v] || {}
      merged[p].versions[v].remote = true
      merged[p].versions[v].tags = []
      Object.keys(remote[p]["dist-tags"]).forEach(function (tag) {
        if (remote[p]["dist-tags"][tag] === v) merged[p].versions[v].tags.push(tag)
      })
      // merged[p][v].__proto__ = remote[p][v]
      // Object.keys(remote[p][v]).forEach(function (i) {
      //   merged[p][v][i] = merged[p][v][i] || remote[p][v][i]
      // })
    }
  }
  return merged
}
<commit_msg>got excutableized somehow.  That ain't right.<commit_after>
// show the installed versions of a package

module.exports = exports = ls

var npm = require("../npm")
  , readInstalled = require("./utils/read-installed")
  , registry = require("./utils/registry")
  , semver = require("./utils/semver")
  , output = require("./utils/output")

ls.usage = "npm ls [some search terms ...]"

function ls (args, cb_) {
  readInstalled([], function (er, installed) {
    if (er) return cb_(er)
    registry.get(function (er, remote) {
      if (er) remote = {}
      var info = merge(installed, remote)
        , pretty = prettify(info, args)
      function cb () { cb_(null, info) }
      output.write(npm.config.get("outfd"), pretty, cb)
    })
  })
}
function strcmp (a, b) { return a > b ? 1 : -1 }
function prettify (data, args) {
  var pkgs = Object.keys(data).sort(strcmp)
    , attrs = []
    , names = []
    , pretty = []
    , beginAttrList = 28
  pkgs.forEach(function (name) {
    var pkg = data[name]
    Object.keys(pkg.versions).sort(semver.compare).forEach(function (v) {
      var ver = pkg.versions[v]
        , p = []
      ver.tags = ver.tags.length === 0
               ? ""
               : (ver.tags.join(" "))
      for (var i in ver) if (ver[i]) {
        p.push((typeof ver[i] === "string") ? ver[i] : i)
      }
      if (pkg.maintainers) pkg.maintainers.forEach(function (m) {
        p.push('='+m.name)
      })
      names.push(name + "@" + v)
      attrs.push(p.sort(strcmp).join(" "))
    })
  })
  for (var n = 0, l = names.length; n < l; n ++) {
    pretty.push({name:names[n], attrs:attrs[n]})
  }
  var colors = [36, 32, 33, 31, 35 ]
    , c = 0
    , l = colors.length
  var maxNameLen = 0
  if (args) pretty = pretty.filter(function (line) {
    var keep = true
    args.forEach(function (arg) {
      keep = keep && (line.name.match(arg) || line.attrs.match(arg))
    })
    return keep
  })
  var space = "                       "
  pretty.forEach(function (line) {
    maxNameLen = Math.min(Math.max(maxNameLen, line.name.length), space.length)
  })
  maxNameLen += 2

  // now turn each line obj into a single line, with only as much ws as necessary.
  pretty = pretty.map(function (line) {
    var addSpace = maxNameLen - line.name.length
    return line.name + (space.substr(0, addSpace) || "") + " " + line.attrs
  })

  if (args) args.forEach(function (arg) {
    pretty = pretty.map(function (line) {
      return line.split(arg).join("\033["+colors[c]+"m" + arg + "\033[m")
    })
    c = (c + 1) % l
  })
  if (!pretty.length) pretty = ["Nothing found"]
  pretty.push("")
  return pretty.join("\n")
}
function merge (installed, remote) {
  var merged = {}
  // first, just copy the installed stuff
  for (var p in installed) {
    merged[p] = {versions:{}}
    for (var v in installed[p]) {
      merged[p].versions[v] = { installed : true, tags : [] }
      for (var s in installed[p][v]) {
        merged[p].versions[v][s] = installed[p][v][s]
      }
    }
  }
  // now merge in the remote stuff.
  for (var p in remote) {
    merged[p] = merged[p] || {versions:{}}
    for (var d in remote[p]) if (!merged[p].hasOwnProperty(d)) {
      merged[p][d] = remote[p][d]
    }
    for (var v in remote[p].versions) {
      merged[p].versions[v] = merged[p].versions[v] || {}
      merged[p].versions[v].remote = true
      merged[p].versions[v].tags = []
      Object.keys(remote[p]["dist-tags"]).forEach(function (tag) {
        if (remote[p]["dist-tags"][tag] === v) merged[p].versions[v].tags.push(tag)
      })
      // merged[p][v].__proto__ = remote[p][v]
      // Object.keys(remote[p][v]).forEach(function (i) {
      //   merged[p][v][i] = merged[p][v][i] || remote[p][v][i]
      // })
    }
  }
  return merged
}
<|endoftext|>


<commit_before>/*!
 * ws: a node.js websocket client
 * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
 * MIT Licensed
 */

var events = require('events')
  , util = require('util')
  , EventEmitter = events.EventEmitter
  , ErrorCodes = require('./ErrorCodes');

/**
 * Node version 0.4 and 0.6 compatibility
 */
var isNodeV4 = /^v0\.4/.test(process.version);
var writeUInt16BE = !isNodeV4
  ? Buffer.prototype.writeUInt16BE
  : function(value, offset) {
    this[offset] = value >>> 8;
    this[offset + 1] = value % 256;
  };
var writeUInt32BE = !isNodeV4
  ? Buffer.prototype.writeUInt32BE
  : function(value, offset) {
    for (var i = offset + 3; i >= offset; --i) {
      this[i] = value & 0xff;
      value >>>= 8;
    }
  };

/**
 * HyBi Sender implementation
 */

function Sender (socket) {
  this._socket = socket;
  this.firstFragment = true;
}

/**
 * Inherits from EventEmitter.
 */

util.inherits(Sender, events.EventEmitter);

/**
 * Sends a close instruction to the remote party.
 *
 * @api public
 */

Sender.prototype.close = function(code, data, mask) {
  if (typeof code !== 'undefined') {
    if (typeof code !== 'number' ||
      !ErrorCodes.isValidErrorCode(code)) throw new Error('first argument must be a valid error code number');
  }
  code = code || 1000;
  var dataBuffer = new Buffer(2 + (data ? Buffer.byteLength(data) : 0));
  writeUInt16BE.call(dataBuffer, code, 0);
  if (dataBuffer.length > 2) dataBuffer.write(data, 2);
  var buf = this.frameData(0x8, dataBuffer, true, mask);
  try {
    this._socket.write(buf, 'binary');
  }
  catch (e) {
    this.emit('error', e);
  }
}

/**
 * Sends a ping message to the remote party.
 *
 * @api public
 */

Sender.prototype.ping = function(data, options) {
  var mask = options && options.mask;
  var buf = this.frameData(0x9, data || '', true, mask);
  try {
    this._socket.write(buf, 'binary');
  }
  catch (e) {
    this.emit('error', e);
  }
}

/**
 * Sends a pong message to the remote party.
 *
 * @api public
 */

Sender.prototype.pong = function(data, options) {
  var mask = options && options.mask;
  var buf = this.frameData(0xa, data || '', true, mask);
  try {
    this._socket.write(buf, 'binary');
  }
  catch (e) {
    this.emit('error', e);
  }
}

/**
 * Sends text or binary data to the remote party.
 *
 * @api public
 */

Sender.prototype.send = function(data, options, cb) {
  var buf;
  var finalFragment = options && options.fin === false ? false : true;
  var mask = options && options.mask;
  var opcode = options && options.binary ? 2 : 1;
  if (this.firstFragment === false) opcode = 0;
  else this.firstFragment = false;
  buf = this.frameData(opcode, data, finalFragment, mask);
  if (finalFragment) this.firstFragment = true
  try {
    this._socket.write(buf, 'binary', cb);
  }
  catch (e) {
    if (typeof cb == 'function') cb(e);
    else this.emit('error', e);
  }
}

/**
 * Frames a piece of data according to the HyBi WebSocket protocol.
 *
 * @api private
 */

Sender.prototype.frameData = function(opcode, data, finalFragment, maskData) {
  var dataBuffer = getBufferFromData(data)
    , dataLength = dataBuffer.length
    , dataOffset = maskData ? 6 : 2
    , secondByte = dataLength;
  if (dataLength >= 65536) {
    dataOffset += 8;
    secondByte = 127;
  }
  else if (dataLength > 125) {
    dataOffset += 2;
    secondByte = 126;
  }
  var outputBuffer = new Buffer(dataLength + dataOffset);
  if (finalFragment) opcode = opcode | 0x80;
  outputBuffer[0] = opcode;
  switch (secondByte) {
    case 126:
      writeUInt16BE.call(outputBuffer, dataLength, 2);
      break;
    case 127:
      writeUInt32BE.call(outputBuffer, 0, 2);
      writeUInt32BE.call(outputBuffer, dataLength, 6);
  }
  if (maskData) {
    var mask = this._randomMask || (this._randomMask = getRandomMask());
    mask.copy(outputBuffer, dataOffset - 4);
    applyMaskToBuffer(dataBuffer, mask);
    secondByte = secondByte | 0x80;
  }
  outputBuffer[1] = secondByte;
  dataBuffer.copy(outputBuffer, dataOffset);
  return outputBuffer;
}

module.exports = Sender;

function applyMaskToBuffer(buf, mask) {
  if (typeof buf == 'string') buf = new Buffer(buf);
  for (var i = 0, l = buf.length; i < l; ++i) buf[i] ^= mask[i % 4];
  return buf;
}

function getBufferFromData(data) {
  if (!data) return new Buffer(0);
  if (data instanceof Buffer) return data;
  return (data && typeof data.buffer !== 'undefined')
     ? getArrayBuffer(data.buffer)
     : new Buffer(data);
}

function getArrayBuffer(array) {
  var l = array.byteLength
    , buffer = new Buffer(l);
  for (var i = 0; i < l; ++i) {
    buffer[i] = array[i];
  }
  return buffer;
}

function getRandomMask() {
  return new Buffer([
    ~~(Math.random() * 255),
    ~~(Math.random() * 255),
    ~~(Math.random() * 255),
    ~~(Math.random() * 255)
  ]);
}
<commit_msg>copy buffer, to not overwrite outside data<commit_after>/*!
 * ws: a node.js websocket client
 * Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
 * MIT Licensed
 */

var events = require('events')
  , util = require('util')
  , EventEmitter = events.EventEmitter
  , ErrorCodes = require('./ErrorCodes');

/**
 * Node version 0.4 and 0.6 compatibility
 */
var isNodeV4 = /^v0\.4/.test(process.version);
var writeUInt16BE = !isNodeV4
  ? Buffer.prototype.writeUInt16BE
  : function(value, offset) {
    this[offset] = value >>> 8;
    this[offset + 1] = value % 256;
  };
var writeUInt32BE = !isNodeV4
  ? Buffer.prototype.writeUInt32BE
  : function(value, offset) {
    for (var i = offset + 3; i >= offset; --i) {
      this[i] = value & 0xff;
      value >>>= 8;
    }
  };

/**
 * HyBi Sender implementation
 */

function Sender (socket) {
  this._socket = socket;
  this.firstFragment = true;
}

/**
 * Inherits from EventEmitter.
 */

util.inherits(Sender, events.EventEmitter);

/**
 * Sends a close instruction to the remote party.
 *
 * @api public
 */

Sender.prototype.close = function(code, data, mask) {
  if (typeof code !== 'undefined') {
    if (typeof code !== 'number' ||
      !ErrorCodes.isValidErrorCode(code)) throw new Error('first argument must be a valid error code number');
  }
  code = code || 1000;
  var dataBuffer = new Buffer(2 + (data ? Buffer.byteLength(data) : 0));
  writeUInt16BE.call(dataBuffer, code, 0);
  if (dataBuffer.length > 2) dataBuffer.write(data, 2);
  var buf = this.frameData(0x8, dataBuffer, true, mask);
  try {
    this._socket.write(buf, 'binary');
  }
  catch (e) {
    this.emit('error', e);
  }
}

/**
 * Sends a ping message to the remote party.
 *
 * @api public
 */

Sender.prototype.ping = function(data, options) {
  var mask = options && options.mask;
  var buf = this.frameData(0x9, data || '', true, mask);
  try {
    this._socket.write(buf, 'binary');
  }
  catch (e) {
    this.emit('error', e);
  }
}

/**
 * Sends a pong message to the remote party.
 *
 * @api public
 */

Sender.prototype.pong = function(data, options) {
  var mask = options && options.mask;
  var buf = this.frameData(0xa, data || '', true, mask);
  try {
    this._socket.write(buf, 'binary');
  }
  catch (e) {
    this.emit('error', e);
  }
}

/**
 * Sends text or binary data to the remote party.
 *
 * @api public
 */

Sender.prototype.send = function(data, options, cb) {
  var buf;
  var finalFragment = options && options.fin === false ? false : true;
  var mask = options && options.mask;
  var opcode = options && options.binary ? 2 : 1;
  if (this.firstFragment === false) opcode = 0;
  else this.firstFragment = false;
  buf = this.frameData(opcode, data, finalFragment, mask);
  if (finalFragment) this.firstFragment = true
  try {
    this._socket.write(buf, 'binary', cb);
  }
  catch (e) {
    if (typeof cb == 'function') cb(e);
    else this.emit('error', e);
  }
}

/**
 * Frames a piece of data according to the HyBi WebSocket protocol.
 *
 * @api private
 */

Sender.prototype.frameData = function(opcode, data, finalFragment, maskData) {
  var dataBuffer = getBufferFromData(data)
    , dataLength = dataBuffer.length
    , dataOffset = maskData ? 6 : 2
    , secondByte = dataLength;
  if (dataLength >= 65536) {
    dataOffset += 8;
    secondByte = 127;
  }
  else if (dataLength > 125) {
    dataOffset += 2;
    secondByte = 126;
  }
  var outputBuffer = new Buffer(dataLength + dataOffset);
  if (finalFragment) opcode = opcode | 0x80;
  outputBuffer[0] = opcode;
  switch (secondByte) {
    case 126:
      writeUInt16BE.call(outputBuffer, dataLength, 2);
      break;
    case 127:
      writeUInt32BE.call(outputBuffer, 0, 2);
      writeUInt32BE.call(outputBuffer, dataLength, 6);
  }
  if (maskData) {
    var mask = this._randomMask || (this._randomMask = getRandomMask());
    mask.copy(outputBuffer, dataOffset - 4);
    applyMaskToBuffer(dataBuffer, mask);
    secondByte = secondByte | 0x80;
  }
  outputBuffer[1] = secondByte;
  dataBuffer.copy(outputBuffer, dataOffset);
  return outputBuffer;
}

module.exports = Sender;

function applyMaskToBuffer(buf, mask) {
  if (typeof buf == 'string') buf = new Buffer(buf);
  for (var i = 0, l = buf.length; i < l; ++i) buf[i] ^= mask[i % 4];
  return buf;
}

function getBufferFromData(data) {
  if (!data) return new Buffer(0);
  if (data instanceof Buffer) return new Buffer(data);
  return (data && typeof data.buffer !== 'undefined')
     ? getArrayBuffer(data.buffer)
     : new Buffer(data);
}

function getArrayBuffer(array) {
  var l = array.byteLength
    , buffer = new Buffer(l);
  for (var i = 0; i < l; ++i) {
    buffer[i] = array[i];
  }
  return buffer;
}

function getRandomMask() {
  return new Buffer([
    ~~(Math.random() * 255),
    ~~(Math.random() * 255),
    ~~(Math.random() * 255),
    ~~(Math.random() * 255)
  ]);
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2014, Ian Gulliver
 *
 * 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.
 */

/*
A quick note on testing philosophy:

These tests cover both the client (JavaScript) and the server (Python), as
well as the server's interaction with appengine (via dev_appserver or a
deployed instance). There is intentionally no mock server. The client and
server interactions are complex and tests should be structured to verify them,
not to verify the behavior of a simulation.
*/

QUnit.begin(localStorage.clear.bind(localStorage));
QUnit.done(localStorage.clear.bind(localStorage));

var randstring = function() {
  var ret = [];
  for (var i = 0; i < 16; i++) {
    var ran = (Math.random() * 16) | 0;
    ret.push(ran.toString(16));
  }
  return ret.join('');
};

test('Construct/shutdown', function() {
  expect(2);
  var cosmo = new Cosmopolite({});
  ok(true, 'new Cosmopolite() succeeds');
  cosmo.shutdown();
  ok(true, 'shutdown() succeeds');
});

asyncTest('onLogin or onLogout', function() {
  expect(1);
  var callbacks = {
    'onLogin': function(user, logout_url) {
      ok(true, 'onLogin fired');
      cosmo.shutdown();
      start();
    },
    'onLogout': function(login_url) {
      ok(true, 'onLogout fired');
      cosmo.shutdown();
      start();
    }
  };
  var cosmo = new Cosmopolite(callbacks);
});

asyncTest('Message round trip', function() {
  expect(2);

  var subject = randstring();
  var message = randstring();

  var callbacks1 = {
    'onReady': function() {
      cosmo1.sendMessage(subject, message);
    },
  };

  var callbacks2 = {
    'onReady': function() {
      cosmo2.subscribe(subject, -1);
    },
    'onMessage': function(e) {
      equal(e['subject'], subject, 'subject matches');
      equal(e['message'], message, 'message matches');
      cosmo1.shutdown();
      cosmo2.shutdown();
      start();
    },
  };

  var cosmo1 = new Cosmopolite(callbacks1, null, randstring());
  var cosmo2 = new Cosmopolite(callbacks2, null, randstring());
});

asyncTest('Overwrite key', function() {
  expect(8);

  var subject = randstring();
  var message1 = randstring();
  var message2 = randstring();
  var key = randstring();

  var messages1 = 0;

  var callbacks1 = {
    'onReady': function() {
      cosmo1.subscribe(subject, -1);
      cosmo1.sendMessage(subject, message1, key);
    },
    'onMessage': function(e) {
      messages1++;
      if (messages1 == 1) {
        cosmo1.sendMessage(subject, message2, key);
      }
    },
  };

  var messages2 = 0;

  var callbacks2 = {
    'onReady': function() {
      cosmo2.subscribe(subject, -1);
    },
    'onMessage': function(e) {
      messages2++;
      equal(e['subject'], subject, 'subject matches');
      equal(e['key'], key, 'key matches');
      if (messages2 == 1) {
        equal(e['message'], message1, 'message #1 matches');
        equal(cosmo2.getKeyMessage(subject, key)['message'], message1, 'message #1 matches by key')
        return;
      }
      equal(e['message'], message2, 'message #2 matches');
      equal(cosmo2.getKeyMessage(subject, key)['message'], message2, 'message #2 matches by key')
      cosmo1.shutdown();
      cosmo2.shutdown();
      start();
    },
  };

  var cosmo1 = new Cosmopolite(callbacks1, null, randstring());
  var cosmo2 = new Cosmopolite(callbacks2, null, randstring());
});
<commit_msg>Add test for complex object<commit_after>/**
 * @license
 * Copyright 2014, Ian Gulliver
 *
 * 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.
 */

/*
A quick note on testing philosophy:

These tests cover both the client (JavaScript) and the server (Python), as
well as the server's interaction with appengine (via dev_appserver or a
deployed instance). There is intentionally no mock server. The client and
server interactions are complex and tests should be structured to verify them,
not to verify the behavior of a simulation.
*/

var randstring = function() {
  var ret = [];
  for (var i = 0; i < 16; i++) {
    var ran = (Math.random() * 16) | 0;
    ret.push(ran.toString(16));
  }
  return ret.join('');
};

QUnit.begin(localStorage.clear.bind(localStorage));
QUnit.done(localStorage.clear.bind(localStorage));

module('General');

test('Construct/shutdown', function() {
  expect(2);
  var cosmo = new Cosmopolite({});
  ok(true, 'new Cosmopolite() succeeds');
  cosmo.shutdown();
  ok(true, 'shutdown() succeeds');
});

asyncTest('onLogin or onLogout', function() {
  expect(1);
  var callbacks = {
    'onLogin': function(user, logout_url) {
      ok(true, 'onLogin fired');
      cosmo.shutdown();
      start();
    },
    'onLogout': function(login_url) {
      ok(true, 'onLogout fired');
      cosmo.shutdown();
      start();
    }
  };
  var cosmo = new Cosmopolite(callbacks);
});

asyncTest('Message round trip', function() {
  expect(2);

  var subject = randstring();
  var message = randstring();

  var callbacks1 = {
    'onReady': function() {
      cosmo1.sendMessage(subject, message);
    },
  };

  var callbacks2 = {
    'onReady': function() {
      cosmo2.subscribe(subject, -1);
    },
    'onMessage': function(e) {
      equal(e['subject'], subject, 'subject matches');
      equal(e['message'], message, 'message matches');
      cosmo1.shutdown();
      cosmo2.shutdown();
      start();
    },
  };

  var cosmo1 = new Cosmopolite(callbacks1, null, randstring());
  var cosmo2 = new Cosmopolite(callbacks2, null, randstring());
});

asyncTest('Overwrite key', function() {
  expect(8);

  var subject = randstring();
  var message1 = randstring();
  var message2 = randstring();
  var key = randstring();

  var messages1 = 0;

  var callbacks1 = {
    'onReady': function() {
      cosmo1.subscribe(subject, -1);
      cosmo1.sendMessage(subject, message1, key);
    },
    'onMessage': function(e) {
      messages1++;
      if (messages1 == 1) {
        cosmo1.sendMessage(subject, message2, key);
      }
    },
  };

  var messages2 = 0;

  var callbacks2 = {
    'onReady': function() {
      cosmo2.subscribe(subject, -1);
    },
    'onMessage': function(e) {
      messages2++;
      equal(e['subject'], subject, 'subject matches');
      equal(e['key'], key, 'key matches');
      if (messages2 == 1) {
        equal(e['message'], message1, 'message #1 matches');
        equal(cosmo2.getKeyMessage(subject, key)['message'], message1, 'message #1 matches by key')
        return;
      }
      equal(e['message'], message2, 'message #2 matches');
      equal(cosmo2.getKeyMessage(subject, key)['message'], message2, 'message #2 matches by key')
      cosmo1.shutdown();
      cosmo2.shutdown();
      start();
    },
  };

  var cosmo1 = new Cosmopolite(callbacks1, null, randstring());
  var cosmo2 = new Cosmopolite(callbacks2, null, randstring());
});

asyncTest('Complex object', function() {
  expect(2);

  var subject = randstring();
  var message = {
    'foo': 'bar',
    5: 'zig',
    'zag': [16, 22, 59, 76],
    'boo': {
      'nested': 'object',
      10: 100,
    },
    'unicode': '☠☣☃𠜎',
  };

  var callbacks1 = {
    'onReady': function() {
      cosmo1.sendMessage(subject, message);
    },
  };

  var callbacks2 = {
    'onReady': function() {
      cosmo2.subscribe(subject, -1);
    },
    'onMessage': function(e) {
      equal(e['subject'], subject, 'subject matches');
      deepEqual(e['message'], message, 'message matches');
      cosmo1.shutdown();
      cosmo2.shutdown();
      start();
    },
  };

  var cosmo1 = new Cosmopolite(callbacks1, null, randstring());
  var cosmo2 = new Cosmopolite(callbacks2, null, randstring());
});
<|endoftext|>


<commit_before>'use strict';
var _ = require('lodash');
var squel = require('squel');
var when = require('when');
var sequence = require('when/sequence');

var enforceValue = function(name, message) {
  if(!name) {
    throw new Error(message);
  }
};

var defaultValue = function(obj, val) {
  return obj || val;
};

var Model = function(opts) {
  enforceValue(opts, 'You must initialise a model with the request parameters: table');
  enforceValue(opts.table, 'You must initialise a model with the table option');
  enforceValue(opts.identity, 'You must initialise a model with the id option');

  var items = [];
  var save = function(saveOpts) {
    saveOpts = defaultValue(saveOpts, {});

    var insertMe =_.filter(items, function(item) {
      return item._meta.existing === false;
    });

    var updateMe =_.filter(items, function(item) {
      return item._meta.modified() === true;
    });

    // TODO: Far too many clones going on here, need a better
    // way to remove the _meta property from the setFieldsRows
    // perhaps _.map or _.reduce
    // At the moment this will insert one by one and get the identity
    // for ORM purposes, however it's much quicker to bulk insert
    // but then theres no identity.  Perhaps a save {bulk: true} is needed?
    var delegates = [];
    var chainPromise = function(delegate) {
      delegates.push(delegate);
    };

    var generateBulkInsert = function() {
      var insertingItems = _.cloneDeep(insertMe);
      _.forEach(insertingItems, function(insertingItem) {
        delete insertingItem._meta;
      });
      var sql = squel.insert()
        .into(opts.table)
        .setFieldsRows(insertingItems)
        .toString();
      var insertPromise = function() {
        return when.promise(function(resolve) {
          return opts.dal.execute(sql).then(function() {
            resolve();
          });
        });
      };
      chainPromise(insertPromise);
    };

    var generateOrmInsert = function() {
      _.forEach(insertMe, function(insertMeItem) {
        var insertingItem = _.cloneDeep(insertMeItem);
        delete insertingItem._meta;
        var sql = squel.insert()
          .into(opts.table)
          .setFields(insertingItem)
          .toString();
        var insertPromise = function() {
          return when.promise(function(resolve) {
            return opts.dal.execute(sql).then(function() {
              return opts.dal.getLastInsertedId(opts.table)
                .then(function(id) {
                  insertMeItem._meta.original = _.cloneDeep(insertingItem);
                  insertMeItem._meta.existing = true;
                  insertMeItem[opts.identity] = id;
                  resolve();
                });
            });
          });
        };
        chainPromise(insertPromise);
      });
    };

    var generateOrmUpdate = function() {
      _.forEach(updateMe, function(updateMeItem) {
        if(!updateMeItem[opts.identity]) {
          throw new Error('A model flagged for update must have an identifier set');
        }
        var updatingItem = _.cloneDeep(updateMeItem);
        var id = updateMeItem[opts.identity];
        delete updatingItem._meta;
        delete updatingItem[opts.identity];
        var sql = squel.update()
          .table(opts.table)
          .setFields(updatingItem)
          .where(opts.identity + ' = ' + id)
          .toString();
        var updatePromise = function() {
          return when.promise(function(resolve) {
            return opts.dal.execute(sql).then(function() {
              updateMeItem._meta.original = _.cloneDeep(updatingItem);
              resolve();
            });
          });
        };
        chainPromise(updatePromise);
      });
    };

    if(saveOpts.bulk) {
      generateBulkInsert();
    } else {
      generateOrmInsert();
    }

    generateOrmUpdate();

    var promise = sequence(delegates);
    return promise;
  };

  var create = function(item, meta) {
    item._meta = {
      existing: false,
      original: _.clone(item)
    };
    item._meta = _.merge(item._meta, meta);
    item._meta.modified = function() {
      var sample = _.clone(item);
      // Sanitise the sample object
      delete sample._meta;
      delete sample[opts.identity];
      // Remove the identity from the meta
      delete item._meta.original[opts.identity];
      // Compare the two to find out if they've changed
      return !_.isEqual(sample, item._meta.original);
    };

    items.push(item);
    return item;
  };

  return Object.freeze({
    save: save,
    create: create
  });
};

module.exports = Model;
<commit_msg>Cleaned up the insert and update statements<commit_after>'use strict';
var _ = require('lodash');
var squel = require('squel');
var when = require('when');
var sequence = require('when/sequence');

var enforceValue = function(name, message) {
  if(!name) {
    throw new Error(message);
  }
};

var defaultValue = function(obj, val) {
  return obj || val;
};

var Model = function(opts) {
  enforceValue(opts, 'You must initialise a model with the request parameters: table');
  enforceValue(opts.table, 'You must initialise a model with the table option');
  enforceValue(opts.identity, 'You must initialise a model with the id option');

  var items = [];
  var save = function(saveOpts) {
    saveOpts = defaultValue(saveOpts, {});

    var insertMe =_.filter(items, function(item) {
      return item._meta.existing === false;
    });

    var updateMe =_.filter(items, function(item) {
      return item._meta.modified() === true;
    });

    var trim = function(item) {
      delete item._meta;
      delete item[opts.identity];
    };

    // TODO: Far too many clones going on here, need a better
    // way to remove the _meta property from the setFieldsRows
    // perhaps _.map or _.reduce
    // At the moment this will insert one by one and get the identity
    // for ORM purposes, however it's much quicker to bulk insert
    // but then theres no identity.  Perhaps a save {bulk: true} is needed?
    var delegates = [];
    var chainPromise = function(delegate) {
      delegates.push(delegate);
    };

    var generateBulkInsert = function() {
      var insertingItems = _.cloneDeep(insertMe);
      _.forEach(insertingItems, function(insertingItem) {
        trim(insertingItem);
      });
      var sql = squel.insert()
        .into(opts.table)
        .setFieldsRows(insertingItems)
        .toString();
      var insertPromise = function() {
        return when.promise(function(resolve) {
          return opts.dal.execute(sql).then(function() {
            resolve();
          });
        });
      };
      chainPromise(insertPromise);
    };

    var generateOrmInsert = function() {
      _.forEach(insertMe, function(insertMeItem) {
        var insertingItem = _.cloneDeep(insertMeItem);
        trim(insertingItem);
        var sql = squel.insert()
          .into(opts.table)
          .setFields(insertingItem)
          .toString();
        var insertPromise = function() {
          return when.promise(function(resolve) {
            return opts.dal.execute(sql).then(function() {
              return opts.dal.getLastInsertedId(opts.table)
                .then(function(id) {
                  insertMeItem._meta.original = insertingItem;
                  insertMeItem._meta.existing = true;
                  insertMeItem[opts.identity] = id;
                  resolve();
                });
            });
          });
        };
        chainPromise(insertPromise);
      });
    };

    var generateOrmUpdate = function() {
      _.forEach(updateMe, function(updateMeItem) {
        if(!updateMeItem[opts.identity]) {
          throw new Error('A model flagged for update must have an identifier set');
        }
        var updatingItem = _.cloneDeep(updateMeItem);
        var id = updateMeItem[opts.identity];
        trim(updatingItem);
        var sql = squel.update()
          .table(opts.table)
          .setFields(updatingItem)
          .where(opts.identity + ' = ' + id)
          .toString();
        var updatePromise = function() {
          return when.promise(function(resolve) {
            return opts.dal.execute(sql).then(function() {
              updateMeItem._meta.original = updatingItem;
              resolve();
            });
          });
        };
        chainPromise(updatePromise);
      });
    };

    if(saveOpts.bulk) {
      generateBulkInsert();
    } else {
      generateOrmInsert();
    }

    generateOrmUpdate();

    var promise = sequence(delegates);
    return promise;
  };

  var create = function(item, meta) {
    item._meta = {
      existing: false,
      original: _.clone(item)
    };
    item._meta = _.merge(item._meta, meta);
    item._meta.modified = function() {
      var sample = _.clone(item);
      // Sanitise the sample object
      delete sample._meta;
      delete sample[opts.identity];
      // Remove the identity from the meta
      delete item._meta.original[opts.identity];
      // Compare the two to find out if they've changed
      return !_.isEqual(sample, item._meta.original);
    };

    items.push(item);
    return item;
  };

  return Object.freeze({
    save: save,
    create: create
  });
};

module.exports = Model;
<|endoftext|>


<commit_before>(function (window) {
    'use strict';

    var storageTypes = {
        localStorage: window.localStorage,
        sessionStorage: window.sessionStorage
    };

    // LS error messages
    var errorMessages = {
        doesNotExist: {
            message: 'Endpoint does not exist'
        },
        typeError: {
            message: 'Function is missing a parameter, or first parameter type incorrect'
        }
    };

    /**
     * main constructor
     *
     * @param {Object} settings
     * @return {this} for chaining
     * @public
     */

    var LS = function LS (settings) {
        var currentDate = new Date().getTime(),
            self = this,
            mergedSettings,

            promise = new Promise(function (resolve, reject) {

                // merge default settings with custom settings
                mergedSettings = LS.mergedSettings = Object.assign(
                    {}, LS.defaultSettings, settings
                );

                LS.storageType =
                    storageTypes[mergedSettings.storageType] ||
                    LS.defaultSettings.storageType;

                if (mergedSettings.endpoint) {

                    // if local storage exists configure instance settings
                    // and properties accordingly
                    if (self.check()) {
                        LS.storageItem = self.get().storageItem;

                        // remove expired storage
                        if (mergedSettings.expires !== undefined) {
                            if (currentDate > (LS.storageItem.dateCreated + mergedSettings.expires)) {
                                self.remove();
                            }
                        }

                        resolve(self);
                    }
                } else {
                    reject(Error('init error: ' + errorMessages.typeError.message));
                }
            });

        promise.then(function (instance) {
            return instance;
        });

        promise.catch(function (err) {
            return err;
        });
    };

    // instance properties
    LS.defaultSettings = {
        storageType: storageTypes.localStorage,
        endpoint: undefined,
        expires: undefined
    };

    LS.mergedSettings = {};
    LS.storageType = null;
    LS.storageItem = {};

    /**
     * set local storage and instance data
     *
     * @param {Object} data
     * @param {Function} fn (err, instance, data)
     * @return {this} for chaining
     * @public
     */

    LS.prototype.set = function (data, fn) {

        var promise = new Promise(function (resolve, reject) {
            if (data && typeof data !== 'function') {
                LS.storageItem.endpoint = LS.mergedSettings.endpoint;
                LS.storageItem.data = data;
                LS.storageItem.dateCreated = new Date().getTime();

                LS.storageType[LS.mergedSettings.endpoint] = JSON.stringify(LS.storageItem);

                resolve(LS.storageItem.data);
            } else {
                reject(Error('set error: ' + errorMessages.typeError.message));
            }
        });

        if (fn && typeof fn === 'function') {
            promise.then(function (data) {
                fn(data);
            });
        }

        return promise;
    };

    /**
     * get local storage data and provide object to client with optional asynchronous callback
     *
     * @param {Function} fn (instance)
     * @return {this} if no callback for chaining
     * @public
     */

    LS.prototype.get = LS.prototype.find = function (fn) {

        var self = this,
            promise = new Promise(function (resolve, reject) {
                if (self.check()) {
                    LS.storageItem = JSON.parse(LS.storageType[LS.mergedSettings.endpoint]);

                    resolve(LS.storageItem.data);
                } else {
                    resolve({
                        error: {
                            message: 'get error: ' + errorMessages.doesNotExist.message
                        }
                    })
                }
            });

        if (fn && typeof fn === 'function') {
            promise.then(function (data) {
                fn(data);
            });
        }

        return promise;
    };

    /**
     * check if local storage data exists
     *
     * @param {Function} fn (err, instance, data)
     * @return {Boolean}
     * @public
     */

    LS.prototype.check = function () {
        return LS.storageType[LS.mergedSettings.endpoint] !== undefined;
    };

    /**
     * explicitly clear local storage and instance data
     *
     * @param {Function} fn (err, instance)
     * @return {this} for chaining
     * @public
     */

    LS.prototype.remove = LS.prototype.delete = function (fn) {

        var self = this,
            promise = new Promise(function (resolve, reject) {
                if (self.check()) {

                    // remove local storage
                    LS.storageType.removeItem(LS.mergedSettings.endpoint);

                    LS.storageItem = {};

                    resolve(self);
                } else {
                    resolve({
                        error: {
                            message: 'remove error: ' + errorMessages.doesNotExist.message
                        }
                    });
                }
            });

        if (fn && typeof fn === 'function') {
            promise.then(function (data) {
                fn(data);
            });
        }

        return promise;
    };

    /**
     * explicitly update local storage data
     *
     * @param {Boolean} bool
     * @return {this} for chaining
     * @public
     */

    LS.prototype.update = LS.prototype.save = function (fn, bool) {

        var self = this,
            promise = new Promise(function (resolve, reject) {
                if (self.check() && LS.storageItem.endpoint) {

                    // if user provides true for update dateCreated
                    if (fn && bool && typeof bool === 'boolean' || !bool && fn && typeof fn === 'boolean') {
                        LS.storageItem.dateCreated = new Date().getTime();
                    }

                    LS.storageType[LS.mergedSettings.endpoint] = JSON.stringify(LS.storageItem);

                    resolve(LS.storageItem.data);
                } else {
                    reject(Error('update error: ' + errorMessages.doesNotExist.message));
                }
            });

        if (fn && typeof fn === 'function') {
            promise.then(function (data) {
                fn(data);
            });
        }

        return promise;
    };

    // Object.assign shim
    if (typeof Object.assign != 'function') {
        (function () {
            Object.assign = function (target) {
                if (target === undefined || target === null) {
                    throw new TypeError('Cannot convert undefined or null to object');
                }

                var output = Object(target);

                for (var index = 1; index < arguments.length; index++) {
                    var source = arguments[index];

                    if (source !== undefined && source !== null) {
                        for (var nextKey in source) {
                            if (source.hasOwnProperty(nextKey)) {
                                output[nextKey] = source[nextKey];
                            }
                        }
                    }
                }

                return output;
            };
        })();
    }

    // globalize LS object/constructor
    window.LS = LS;

})(window);
<commit_msg>update comments<commit_after>(function (window) {
    'use strict';

    var storageTypes = {
        localStorage: window.localStorage,
        sessionStorage: window.sessionStorage
    };

    // LS error messages
    var errorMessages = {
        doesNotExist: {
            message: 'Endpoint does not exist'
        },
        typeError: {
            message: 'Function is missing a parameter, or first parameter type incorrect'
        }
    };

    /**
     * main constructor
     *
     * @param {Object} settings
     * @return {this}
     * @public
     */

    var LS = function LS (settings) {
        var currentDate = new Date().getTime(),
            self = this,
            mergedSettings,

            promise = new Promise(function (resolve, reject) {

                // merge default settings with custom settings
                mergedSettings = LS.mergedSettings = Object.assign(
                    {}, LS.defaultSettings, settings
                );

                LS.storageType =
                    storageTypes[mergedSettings.storageType] ||
                    LS.defaultSettings.storageType;

                if (mergedSettings.endpoint) {

                    // if local storage exists configure instance settings
                    // and properties accordingly
                    if (self.check()) {
                        LS.storageItem = self.get().storageItem;

                        // remove expired storage
                        if (mergedSettings.expires !== undefined) {
                            if (currentDate > (LS.storageItem.dateCreated + mergedSettings.expires)) {
                                self.remove();
                            }
                        }

                        resolve(self);
                    }
                } else {
                    reject(Error('init error: ' + errorMessages.typeError.message));
                }
            });

        promise.then(function (instance) {
            return instance;
        });

        promise.catch(function (err) {
            return err;
        });
    };

    // instance properties
    LS.defaultSettings = {
        storageType: storageTypes.localStorage,
        endpoint: undefined,
        expires: undefined
    };

    LS.mergedSettings = {};
    LS.storageType = null;
    LS.storageItem = {};

    /**
     * set local storage and instance data
     *
     * @param {Object} data
     * @param {Function} fn (response)
     * @return {promise}
     * @public
     */

    LS.prototype.set = function (data, fn) {

        var promise = new Promise(function (resolve, reject) {
            if (data && typeof data !== 'function') {
                LS.storageItem.endpoint = LS.mergedSettings.endpoint;
                LS.storageItem.data = data;
                LS.storageItem.dateCreated = new Date().getTime();

                LS.storageType[LS.mergedSettings.endpoint] = JSON.stringify(LS.storageItem);

                resolve(LS.storageItem.data);
            } else {
                reject(Error('set error: ' + errorMessages.typeError.message));
            }
        });

        if (fn && typeof fn === 'function') {
            promise.then(function (response) {
                fn(response);
            });
        }

        return promise;
    };

    /**
     * get local storage data and provide object to client with optional callback
     *
     * @param {Function} fn (response)
     * @return {promise}
     * @public
     */

    LS.prototype.get = LS.prototype.find = function (fn) {

        var self = this,
            promise = new Promise(function (resolve, reject) {
                if (self.check()) {
                    LS.storageItem = JSON.parse(LS.storageType[LS.mergedSettings.endpoint]);

                    resolve(LS.storageItem.data);
                } else {
                    resolve({
                        error: {
                            message: 'get error: ' + errorMessages.doesNotExist.message
                        }
                    })
                }
            });

        if (fn && typeof fn === 'function') {
            promise.then(function (response) {
                fn(response);
            });
        }

        return promise;
    };

    /**
     * check if local storage data exists
     *
     * @return {Boolean}
     * @public
     */

    LS.prototype.check = function () {
        return LS.storageType[LS.mergedSettings.endpoint] !== undefined;
    };

    /**
     * clear local storage and instance data with optional callback
     *
     * @param {Function} fn (response)
     * @return {promise}
     * @public
     */

    LS.prototype.remove = LS.prototype.delete = function (fn) {

        var self = this,
            promise = new Promise(function (resolve, reject) {
                if (self.check()) {

                    // remove local storage
                    LS.storageType.removeItem(LS.mergedSettings.endpoint);

                    LS.storageItem = {};

                    resolve(self);
                } else {
                    resolve({
                        error: {
                            message: 'remove error: ' + errorMessages.doesNotExist.message
                        }
                    });
                }
            });

        if (fn && typeof fn === 'function') {
            promise.then(function (response) {
                fn(response);
            });
        }

        return promise;
    };

    /**
     * update local storage data with optional callback
     *
     * @param {Function} fn (response)
     * @param {Boolean} bool
     * @return {promise}
     * @public
     */

    LS.prototype.update = LS.prototype.save = function (fn, bool) {

        var self = this,
            promise = new Promise(function (resolve, reject) {
                if (self.check() && LS.storageItem.endpoint) {

                    // if user provides true for update dateCreated
                    if (fn && bool && typeof bool === 'boolean' || !bool && fn && typeof fn === 'boolean') {
                        LS.storageItem.dateCreated = new Date().getTime();
                    }

                    LS.storageType[LS.mergedSettings.endpoint] = JSON.stringify(LS.storageItem);

                    resolve(LS.storageItem.data);
                } else {
                    reject(Error('update error: ' + errorMessages.doesNotExist.message));
                }
            });

        if (fn && typeof fn === 'function') {
            promise.then(function (response) {
                fn(response);
            });
        }

        return promise;
    };

    // Object.assign shim
    if (typeof Object.assign != 'function') {
        (function () {
            Object.assign = function (target) {
                if (target === undefined || target === null) {
                    throw new TypeError('Cannot convert undefined or null to object');
                }

                var output = Object(target);

                for (var index = 1; index < arguments.length; index++) {
                    var source = arguments[index];

                    if (source !== undefined && source !== null) {
                        for (var nextKey in source) {
                            if (source.hasOwnProperty(nextKey)) {
                                output[nextKey] = source[nextKey];
                            }
                        }
                    }
                }

                return output;
            };
        })();
    }

    // globalize LS object/constructor
    window.LS = LS;

})(window);
<|endoftext|>


<commit_before>import {
    NativeModules
} from 'react-native';
import Raven from 'raven-js';

const {
    RNSentry
} = NativeModules;

const DEFAULT_MODULE_IGNORES = [
    "AccessibilityManager",
    "ActionSheetManager",
    "AlertManager",
    "AppState",
    "AsyncLocalStorage",
    "Clipboard",
    "DevLoadingView",
    "DevMenu",
    "ExceptionsManager",
    "I18nManager",
    "ImageEditingManager",
    "ImageStoreManager",
    "ImageViewManager",
    "IOSConstants",
    "JSCExecutor",
    "JSCSamplingProfiler",
    "KeyboardObserver",
    "LinkingManager",
    "LocationObserver",
    "NativeAnimatedModule",
    "NavigatorManager",
    "NetInfo",
    "Networking",
    "RedBox",
    "ScrollViewManager",
    "SettingsManager",
    "SourceCode",
    "StatusBarManager",
    "Timing",
    "UIManager",
    "Vibration",
    "WebSocketModule",
    "WebViewManager"
];

export const SentrySeverity = {
    Fatal: 0,
    Error: 1,
    Warning: 2,
    Info: 3,
    Debug: 4
}

export const SentryLog = {
    None: 0,
    Error: 1,
    Debug: 2,
    Verbose: 3
}

export class Sentry {
    static install() {
        if (RNSentry && RNSentry.nativeClientAvailable) {
            Sentry._nativeClient = new NativeClient(Sentry._dsn, Sentry.options);
        }
        Sentry._ravenClient = new RavenClient(Sentry._dsn, Sentry.options);
    }

    static config(dsn, options) {
        if (dsn.constructor !== String) {
            throw new Error('Sentry: A DSN must be provided');
        }
        Sentry._dsn = dsn;
        Sentry.options = {
            logLevel: SentryLog.None,
            instrument: {
              timers: false,
              eventTargets: false,
            }
        }
        Object.assign(Sentry.options, options);
        return Sentry;
    }

    static isNativeClientAvailable() {
        return (Sentry._nativeClient);
    }

    static crash() {
        throw new Error('Sentry: TEST crash');
    }

    static nativeCrash() {
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.nativeCrash();
    }

    static setUserContext(user) {
        Sentry._ravenClient.setUserContext(user);
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.setUserContext(user);
    }

    static setTagsContext(tags) {
        Sentry._ravenClient.setTagsContext(tags);
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.setTagsContext(tags);
    }

    static setExtraContext(extra) {
        Sentry._ravenClient.setExtraContext(extra);
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.setExtraContext(extra);
    }

    static captureMessage(message, options) {
        Sentry._ravenClient.captureMessage(message, options);
    }

    static captureException(ex, options) {
        Sentry._ravenClient.captureException(ex, options);
    }

    static captureBreadcrumb(msg, options) {
        Sentry._ravenClient.captureBreadcrumb(msg, options);
    }

    static clearContext(clearContext) {
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.clearContext();
        Sentry._ravenClient.clearContext();
    }

    static context(options, func, args) {
        return Sentry._ravenClient.context(options, func, args);
    }

    static wrap(options, func, _before) {
        return Sentry._ravenClient.wrap(options, func, _before);
    }

    // Private helpers

    static _breadcrumbCallback(crumb) {
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.captureBreadcrumb(crumb);
    }

    static _captureEvent(event) {
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.captureEvent(event);
    }
}

class NativeClient {
    constructor(dsn, options) {
        if (dsn.constructor !== String) {
            throw new Error('Sentry: A DSN must be provided');
        }
        if (!RNSentry) {
            throw new Error('Sentry: There is no native client installed.');
        }

        this._dsn = dsn;
        this._activatedMerging = false;
        this.options = {
            ignoreModulesExclude: [],
            ignoreModulesInclude: [],
            deactivateStacktraceMerging: false
        }
        Object.assign(this.options, options);

        RNSentry.startWithDsnString(this._dsn);
        if (this.options.deactivateStacktraceMerging === false) {
            this._activateStacktraceMerging();
        }
        RNSentry.setLogLevel(options.logLevel);
    }

    nativeCrash() {
        RNSentry.crash();
    }

    captureEvent(event) {
        RNSentry.captureEvent(event);
    }

    setUserContext(user) {
        RNSentry.setUser(user);
    }

    setTagsContext(tags) {
        RNSentry.setTags(tags);
    }

    setExtraContext(extra) {
        RNSentry.setExtra(extra);
    }

    captureBreadcrumb(crumb) {
        RNSentry.captureBreadcrumb(crumb);
    }

    clearContext() {
        RNSentry.clearContext();
    }

    _activateStacktraceMerging = async() => {
        return RNSentry.activateStacktraceMerging().then(activated => {
            if (this._activatedMerging) {
                return;
            }
            this._ignoredModules = {};
            __fbBatchedBridgeConfig.remoteModuleConfig.forEach((module, moduleID) => {
                if (module !== null &&
                    this.options.ignoreModulesExclude.indexOf(module[0]) === -1 &&
                    (DEFAULT_MODULE_IGNORES.indexOf(module[0]) >= 0 ||
                        this.options.ignoreModulesInclude.indexOf(module[0]) >= 0)) {
                    this._ignoredModules[moduleID] = true;
                }
            });
            this._activatedMerging = true;
            this._overwriteEnqueueNativeCall();
        });
    }

    _overwriteEnqueueNativeCall() {
        const BatchedBridge = require('react-native/Libraries/BatchedBridge/BatchedBridge');
        const original = BatchedBridge.enqueueNativeCall;
        const that = this;
        BatchedBridge.enqueueNativeCall = function(moduleID: number, methodID: number, params: Array < any > , onFail: ? Function, onSucc : ? Function) {
            if (that._ignoredModules[moduleID]) {
                return original.apply(this, arguments);
            }
            params.push({
                '__sentry_stack': new Error().stack
            });
            return original.apply(this, arguments);
        }
    }
}

class RavenClient {
    constructor(dsn, options) {
        if (dsn.constructor !== String) {
            throw new Error('SentryClient: A DSN must be provided');
        }
        this._dsn = dsn;
        this.options = {
            allowSecretKey: true,
        }
        Object.assign(this.options, options);
        Raven.addPlugin(require('./raven-plugin'), {
            'nativeClientAvailable': Sentry.isNativeClientAvailable()
        });
        Raven.config(dsn, this.options).install();
        if (options.logLevel >= SentryLog.Debug) {
            Raven.debug = true;
        }
        if (Sentry.isNativeClientAvailable()) {
            // We overwrite the default transport handler when the native
            // client is available, because we want to send the event with native
            Raven.setTransport((options) => {
                Sentry._captureEvent(options.data);
            });
            Raven.setBreadcrumbCallback(Sentry._breadcrumbCallback);
            const oldCaptureBreadcrumb = Raven.captureBreadcrumb;
            Raven.captureBreadcrumb = function(obj) {
                if (obj.data && typeof obj.data === 'object') {
                    obj.data = Object.assign({}, obj.data);
                }
                return oldCaptureBreadcrumb.apply(this, arguments);
            }
        }
    }

    setUserContext(user) {
        Raven.setUserContext(user);
    }

    setTagsContext(tags) {
        Raven.setTagsContext(tags);
    }

    setExtraContext(extra) {
        Raven.setExtraContext(extra)
    }

    captureException(ex, options) {
        Raven.captureException(ex, options);
    }

    captureBreadcrumb(msg, options) {
        Raven.captureBreadcrumb(msg, options);
    }

    captureMessage(message, options) {
        Raven.captureMessage(message, options);
    }

    context(options, func, args) {
        return Raven.context(options, func, args);
    }

    wrap(options, func, _before) {
        return Raven.wrap(options, func, _before);
    }
}
<commit_msg>Expose setDataCallback<commit_after>import {
    NativeModules
} from 'react-native';
import Raven from 'raven-js';

const {
    RNSentry
} = NativeModules;

const DEFAULT_MODULE_IGNORES = [
    "AccessibilityManager",
    "ActionSheetManager",
    "AlertManager",
    "AppState",
    "AsyncLocalStorage",
    "Clipboard",
    "DevLoadingView",
    "DevMenu",
    "ExceptionsManager",
    "I18nManager",
    "ImageEditingManager",
    "ImageStoreManager",
    "ImageViewManager",
    "IOSConstants",
    "JSCExecutor",
    "JSCSamplingProfiler",
    "KeyboardObserver",
    "LinkingManager",
    "LocationObserver",
    "NativeAnimatedModule",
    "NavigatorManager",
    "NetInfo",
    "Networking",
    "RedBox",
    "ScrollViewManager",
    "SettingsManager",
    "SourceCode",
    "StatusBarManager",
    "Timing",
    "UIManager",
    "Vibration",
    "WebSocketModule",
    "WebViewManager"
];

export const SentrySeverity = {
    Fatal: 0,
    Error: 1,
    Warning: 2,
    Info: 3,
    Debug: 4
}

export const SentryLog = {
    None: 0,
    Error: 1,
    Debug: 2,
    Verbose: 3
}

export class Sentry {
    static install() {
        if (RNSentry && RNSentry.nativeClientAvailable) {
            Sentry._nativeClient = new NativeClient(Sentry._dsn, Sentry.options);
        }
        Sentry._ravenClient = new RavenClient(Sentry._dsn, Sentry.options);
    }

    static config(dsn, options) {
        if (dsn.constructor !== String) {
            throw new Error('Sentry: A DSN must be provided');
        }
        Sentry._dsn = dsn;
        Sentry.options = {
            logLevel: SentryLog.None,
            instrument: {
              timers: false,
              eventTargets: false,
            }
        }
        Object.assign(Sentry.options, options);
        return Sentry;
    }

    static isNativeClientAvailable() {
        return (Sentry._nativeClient);
    }

    static crash() {
        throw new Error('Sentry: TEST crash');
    }

    static nativeCrash() {
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.nativeCrash();
    }

    static setDataCallback(callback) {
      Sentry._ravenClient.setDataCallback(callback);
      if (Sentry.isNativeClientAvailable()) {
          console.warn('setDataCallback not supported on native client');
      }
    }

    static setUserContext(user) {
        Sentry._ravenClient.setUserContext(user);
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.setUserContext(user);
    }

    static setTagsContext(tags) {
        Sentry._ravenClient.setTagsContext(tags);
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.setTagsContext(tags);
    }

    static setExtraContext(extra) {
        Sentry._ravenClient.setExtraContext(extra);
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.setExtraContext(extra);
    }

    static captureMessage(message, options) {
        Sentry._ravenClient.captureMessage(message, options);
    }

    static captureException(ex, options) {
        Sentry._ravenClient.captureException(ex, options);
    }

    static captureBreadcrumb(msg, options) {
        Sentry._ravenClient.captureBreadcrumb(msg, options);
    }

    static clearContext(clearContext) {
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.clearContext();
        Sentry._ravenClient.clearContext();
    }

    static context(options, func, args) {
        return Sentry._ravenClient.context(options, func, args);
    }

    static wrap(options, func, _before) {
        return Sentry._ravenClient.wrap(options, func, _before);
    }

    // Private helpers

    static _breadcrumbCallback(crumb) {
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.captureBreadcrumb(crumb);
    }

    static _captureEvent(event) {
        if (Sentry.isNativeClientAvailable()) Sentry._nativeClient.captureEvent(event);
    }
}

class NativeClient {
    constructor(dsn, options) {
        if (dsn.constructor !== String) {
            throw new Error('Sentry: A DSN must be provided');
        }
        if (!RNSentry) {
            throw new Error('Sentry: There is no native client installed.');
        }

        this._dsn = dsn;
        this._activatedMerging = false;
        this.options = {
            ignoreModulesExclude: [],
            ignoreModulesInclude: [],
            deactivateStacktraceMerging: false
        }
        Object.assign(this.options, options);

        RNSentry.startWithDsnString(this._dsn);
        if (this.options.deactivateStacktraceMerging === false) {
            this._activateStacktraceMerging();
        }
        RNSentry.setLogLevel(options.logLevel);
    }

    nativeCrash() {
        RNSentry.crash();
    }

    captureEvent(event) {
        RNSentry.captureEvent(event);
    }

    setUserContext(user) {
        RNSentry.setUser(user);
    }

    setTagsContext(tags) {
        RNSentry.setTags(tags);
    }

    setExtraContext(extra) {
        RNSentry.setExtra(extra);
    }

    captureBreadcrumb(crumb) {
        RNSentry.captureBreadcrumb(crumb);
    }

    clearContext() {
        RNSentry.clearContext();
    }

    _activateStacktraceMerging = async() => {
        return RNSentry.activateStacktraceMerging().then(activated => {
            if (this._activatedMerging) {
                return;
            }
            this._ignoredModules = {};
            __fbBatchedBridgeConfig.remoteModuleConfig.forEach((module, moduleID) => {
                if (module !== null &&
                    this.options.ignoreModulesExclude.indexOf(module[0]) === -1 &&
                    (DEFAULT_MODULE_IGNORES.indexOf(module[0]) >= 0 ||
                        this.options.ignoreModulesInclude.indexOf(module[0]) >= 0)) {
                    this._ignoredModules[moduleID] = true;
                }
            });
            this._activatedMerging = true;
            this._overwriteEnqueueNativeCall();
        });
    }

    _overwriteEnqueueNativeCall() {
        const BatchedBridge = require('react-native/Libraries/BatchedBridge/BatchedBridge');
        const original = BatchedBridge.enqueueNativeCall;
        const that = this;
        BatchedBridge.enqueueNativeCall = function(moduleID: number, methodID: number, params: Array < any > , onFail: ? Function, onSucc : ? Function) {
            if (that._ignoredModules[moduleID]) {
                return original.apply(this, arguments);
            }
            params.push({
                '__sentry_stack': new Error().stack
            });
            return original.apply(this, arguments);
        }
    }
}

class RavenClient {
    constructor(dsn, options) {
        if (dsn.constructor !== String) {
            throw new Error('SentryClient: A DSN must be provided');
        }
        this._dsn = dsn;
        this.options = {
            allowSecretKey: true,
        }
        Object.assign(this.options, options);
        Raven.addPlugin(require('./raven-plugin'), {
            'nativeClientAvailable': Sentry.isNativeClientAvailable()
        });
        Raven.config(dsn, this.options).install();
        if (options.logLevel >= SentryLog.Debug) {
            Raven.debug = true;
        }
        if (Sentry.isNativeClientAvailable()) {
            // We overwrite the default transport handler when the native
            // client is available, because we want to send the event with native
            Raven.setTransport((options) => {
                Sentry._captureEvent(options.data);
            });
            Raven.setBreadcrumbCallback(Sentry._breadcrumbCallback);
            const oldCaptureBreadcrumb = Raven.captureBreadcrumb;
            Raven.captureBreadcrumb = function(obj) {
                if (obj.data && typeof obj.data === 'object') {
                    obj.data = Object.assign({}, obj.data);
                }
                return oldCaptureBreadcrumb.apply(this, arguments);
            }
        }
    }

    setDataCallback(callback) {
        Raven.setDataCallback(callback);
    }

    setUserContext(user) {
        Raven.setUserContext(user);
    }

    setTagsContext(tags) {
        Raven.setTagsContext(tags);
    }

    setExtraContext(extra) {
        Raven.setExtraContext(extra)
    }

    captureException(ex, options) {
        Raven.captureException(ex, options);
    }

    captureBreadcrumb(msg, options) {
        Raven.captureBreadcrumb(msg, options);
    }

    captureMessage(message, options) {
        Raven.captureMessage(message, options);
    }

    context(options, func, args) {
        return Raven.context(options, func, args);
    }

    wrap(options, func, _before) {
        return Raven.wrap(options, func, _before);
    }
}
<|endoftext|>