<commit_before>/*
	Copyright 2013-2014, Marten de Vries

	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.
*/

"use strict";

var coucheval = require("couchdb-eval");
var couchdb_objects = require("couchdb-objects");
var nodify = require("promise-nodify");

var uuid = require("node-uuid");
var Promise = require("pouchdb-promise");
var PouchPluginError = require("pouchdb-plugin-error");

var dbs = [];
var methodNames = ["put", "post", "remove", "bulkDocs", "putAttachment", "removeAttachment"];
var methodsByDbsIdx = [];

function methods(db) {
  var index = dbs.indexOf(db);
  if (index === -1) {
    return methodsFromDb(db);
  }
  return methodsByDbsIdx[index];
}

function methodsFromDb(db) {
  var meths = {};
  methodNames.forEach(function (name) {
    meths[name] = db[name].bind(db);
  });
  return meths;
}

function oldDoc(db, id) {
  return db.get(id, {revs: true}).catch(function () {
    return null;
  });
}

function validate(validationFuncs, newDoc, oldDoc, options) {
  newDoc._revisions = (oldDoc || {})._revisions;

  try {
    validationFuncs.forEach(function (validationFuncInfo) {
      var func = validationFuncInfo.func;
      var designDoc = validationFuncInfo.designDoc;
      func.call(designDoc, newDoc, oldDoc, options.userCtx, options.secObj);
    });
  } catch (e) {
    if (typeof e.unauthorized !== "undefined") {
      throw {
        name: "unauthorized",
        message: e.unauthorized,
        status: 401
      };
    } else if (typeof e.forbidden !== "undefined") {
      throw {
        name: "forbidden",
        message: e.forbidden,
        status: 403
      };
    } else {
      throw coucheval.wrapExecutionError(e);
    }
  }
  //passed all validation functions (no errors thrown) -> success
}

function doValidation(db, newDoc, options, callback) {
  var isHttp = ["http", "https"].indexOf(db.type()) !== -1;
  if (isHttp && !(options || {}).checkHttp) {
    //CouchDB does the checking for itself. Validate succesful.
    return Promise.resolve();
  }
  if ((newDoc._id || "").indexOf("_design/") === 0) {
    //a design document -> always validates succesful.
    return Promise.resolve();
  }
  return getValidationFunctions(db).then(function (validationFuncs) {
    if (!validationFuncs.length) {
      //no validation functions, so valid!
      return;
    }
    var completeOptionsPromise = completeValidationOptions(db, options);
    var oldDocPromise = oldDoc(db, newDoc._id);

    return Promise.all([completeOptionsPromise, oldDocPromise]).then(function (args) {
      var completeOptions = args[0];
      var oldDoc = args[1];
      return validate(validationFuncs, newDoc, oldDoc, completeOptions);
    });
  });
}

function completeValidationOptions(db, options) {
  if (!options) {
    options = {};
  }
  if (!options.secObj) {
    options.secObj = {};
  }

  var userCtxPromise;
  if (options.userCtx) {
    userCtxPromise = Promise.resolve(options.userCtx);
  } else {
    var buildUserContext = couchdb_objects.buildUserContextObject;
    userCtxPromise = db.info().then(buildUserContext);
  }
  return userCtxPromise.then(function (userCtx) {
    options.userCtx = userCtx;
    return options;
  });
}

function getValidationFunctions(db, callback) {
  return db.allDocs({
    startkey: "_design/",
    endkey: "_design0",
    include_docs: true
  }).then(parseValidationFunctions);
}

function parseValidationFunctions(resp) {
  var validationFuncs = resp.rows.map(function (row) {
    return {
      designDoc: row.doc,
      code: row.doc.validate_doc_update
    };
  });
  validationFuncs = validationFuncs.filter(function (info) {
    return typeof info.code !== "undefined";
  });
  validationFuncs.forEach(function (info) {
    //convert str -> function
    info.func = coucheval.evaluate(info.designDoc, {}, info.code);
  });
  return validationFuncs;
}

function processArgs(db, callback, options) {
  if (typeof options === "function") {
    callback = options;
    options = {};
  }
  return {
    db: db,
    callback: callback,
    options: options
  };
}

exports.validatingPut = function (doc, options, callback) {
  var args = processArgs(this, callback, options);
  var promise = doValidation(args.db, doc, args.options).then(function () {
    return methods(args.db).put(doc, args.options);
  });
  nodify(promise, callback);
  return promise;
};

exports.validatingPost = function (doc, options, callback) {
  var args = processArgs(this, callback, options);

  doc._id = doc._id || uuid.v4();
  var promise = doValidation(args.db, doc, args.options).then(function () {
    return methods(args.db).post(doc, args.options);
  });
  nodify(promise, callback);
  return promise;
};

exports.validatingRemove = function (doc, options, callback) {
  var args = processArgs(this, callback, options);

  doc._deleted = true;
  var promise = doValidation(args.db, doc, args.options).then(function () {
    return methods(args.db).remove(doc, args.options);
  });
  nodify(promise, callback);
  return promise;
};

exports.validatingBulkDocs = function (bulkDocs, options, callback) {
  //the ``all_or_nothing`` attribute on ``bulkDocs`` is unsupported.
  //Also, the result array might not be in the same order as
  //``bulkDocs.docs``
  var args = processArgs(this, callback, options);

  var done = [];
  var notYetDone = [];

  var validations = bulkDocs.docs.map(function (doc) {
    doc._id = doc._id || uuid.v4();
    var validationPromise = doValidation(args.db, doc, args.options);

    return validationPromise.then(function (resp) {
      notYetDone.push(doc);
    }).catch(function (err) {
      err.id = doc._id;
      done.push(err);
    });
  });
  var allValidationsPromise = Promise.all(validations).then(function () {
    return methods(args.db).bulkDocs({docs: notYetDone}, args.options);
  }).then(function (insertedDocs) {
    return done.concat(insertedDocs);
  });
  nodify(allValidationsPromise, callback);
  return allValidationsPromise;
};

var vpa = function (docId, attachmentId, rev, attachment, type, options, callback) {
  var args = processArgs(this, callback, options);

  //get the doc
  var promise = args.db.get(docId, {rev: rev, revs: true}).catch(function (err) {
    return {_id: docId};
  }).then(function (doc) {
    //validate the doc + attachment
    doc._attachments = doc._attachments || {};
    doc._attachments[attachmentId] = {
      content_type: type,
      data: attachment,
    };
    return doValidation(args.db, doc, args.options);
  }).then(function () {
    //save the attachment
    return methods(args.db).putAttachment(docId, attachmentId, rev, attachment, type);
  });
  nodify(promise, callback);
  return promise;
};
exports.validatingPutAttachment = vpa;

var vra = function (docId, attachmentId, rev, options, callback) {
  var args = processArgs(this, callback, options);
  //get the doc
  var promise = args.db.get(docId, {rev: rev, revs: true}).then(function (doc) {
    //validate the doc without attachment
    delete doc._attachments[attachmentId];

    return doValidation(args.db, doc, args.options);
  }).then(function () {
    //remove the attachment
    return methods(args.db).removeAttachment(docId, attachmentId, rev);
  });
  nodify(promise, callback);
  return promise;
};
exports.validatingRemoveAttachment = vra;

exports.installValidationMethods = function () {
  var db = this;

  if (dbs.indexOf(db) !== -1) {
    throw new PouchPluginError({
      status: 500,
      name: "already_installed",
      message: "Validation methods are already installed on this database."
    });
  }

  dbs.push(db);
  methodsByDbsIdx.push(methodsFromDb(db));
  methodNames.forEach(function (name) {
    db[name] = exports["validating" + name[0].toUpperCase() + name.substr(1)].bind(db);
  });
};

exports.uninstallValidationMethods = function () {
  var db = this;

  var index = dbs.indexOf(db);
  if (index === -1) {
    throw new PouchPluginError({
      status: 500,
      name: "already_not_installed",
      message: "Validation methods are already not installed on this database."
    });
  }
  var meths = methods(db);
  methodNames.forEach(function (name) {
    db[name] = meths[name];
  });

  //cleanup
  dbs.splice(index, 1);
  methodsByDbsIdx.splice(index, 1);
};
<commit_msg>Improved docs + JS<commit_after>/*
	Copyright 2013-2014, Marten de Vries

	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.
*/

"use strict";

var coucheval = require("couchdb-eval");
var couchdb_objects = require("couchdb-objects");
var nodify = require("promise-nodify");

var uuid = require("node-uuid");
var Promise = require("pouchdb-promise");
var PouchPluginError = require("pouchdb-plugin-error");

var dbs = [];
var methodNames = ["put", "post", "remove", "bulkDocs", "putAttachment", "removeAttachment"];
var methodsByDbsIdx = [];

function methods(db) {
  var index = dbs.indexOf(db);
  if (index === -1) {
    return methodsFromDb(db);
  }
  return methodsByDbsIdx[index];
}

function methodsFromDb(db) {
  var meths = {};
  methodNames.forEach(function (name) {
    meths[name] = db[name].bind(db);
  });
  return meths;
}

function oldDoc(db, id) {
  return db.get(id, {revs: true}).catch(function () {
    return null;
  });
}

function validate(validationFuncs, newDoc, oldDoc, options) {
  newDoc._revisions = (oldDoc || {})._revisions;

  try {
    validationFuncs.forEach(function (validationFuncInfo) {
      var func = validationFuncInfo.func;
      var designDoc = validationFuncInfo.designDoc;
      func.call(designDoc, newDoc, oldDoc, options.userCtx, options.secObj);
    });
  } catch (e) {
    if (typeof e.unauthorized !== "undefined") {
      throw new PouchPluginError({
        name: "unauthorized",
        message: e.unauthorized,
        status: 401
      });
    } else if (typeof e.forbidden !== "undefined") {
      throw new PouchPluginError({
        name: "forbidden",
        message: e.forbidden,
        status: 403
      });
    } else {
      throw coucheval.wrapExecutionError(e);
    }
  }
  //passed all validation functions (no errors thrown) -> success
}

function doValidation(db, newDoc, options, callback) {
  var isHttp = ["http", "https"].indexOf(db.type()) !== -1;
  if (isHttp && !(options || {}).checkHttp) {
    //CouchDB does the checking for itself. Validate succesful.
    return Promise.resolve();
  }
  if ((newDoc._id || "").indexOf("_design/") === 0) {
    //a design document -> always validates succesful.
    return Promise.resolve();
  }
  return getValidationFunctions(db).then(function (validationFuncs) {
    if (!validationFuncs.length) {
      //no validation functions, so valid!
      return;
    }
    var completeOptionsPromise = completeValidationOptions(db, options);
    var oldDocPromise = oldDoc(db, newDoc._id);

    return Promise.all([completeOptionsPromise, oldDocPromise]).then(function (args) {
      var completeOptions = args[0];
      var oldDoc = args[1];
      return validate(validationFuncs, newDoc, oldDoc, completeOptions);
    });
  });
}

function completeValidationOptions(db, options) {
  if (!options) {
    options = {};
  }
  if (!options.secObj) {
    options.secObj = {};
  }

  var userCtxPromise;
  if (options.userCtx) {
    userCtxPromise = Promise.resolve(options.userCtx);
  } else {
    var buildUserContext = couchdb_objects.buildUserContextObject;
    userCtxPromise = db.info().then(buildUserContext);
  }
  return userCtxPromise.then(function (userCtx) {
    options.userCtx = userCtx;
    return options;
  });
}

function getValidationFunctions(db, callback) {
  return db.allDocs({
    startkey: "_design/",
    endkey: "_design0",
    include_docs: true
  }).then(parseValidationFunctions);
}

function parseValidationFunctions(resp) {
  var validationFuncs = resp.rows.map(function (row) {
    return {
      designDoc: row.doc,
      code: row.doc.validate_doc_update
    };
  });
  validationFuncs = validationFuncs.filter(function (info) {
    return typeof info.code !== "undefined";
  });
  validationFuncs.forEach(function (info) {
    //convert str -> function
    info.func = coucheval.evaluate(info.designDoc, {}, info.code);
  });
  return validationFuncs;
}

function processArgs(db, callback, options) {
  if (typeof options === "function") {
    callback = options;
    options = {};
  }
  return {
    db: db,
    callback: callback,
    options: options
  };
}

exports.validatingPut = function (doc, options, callback) {
  var args = processArgs(this, callback, options);
  var promise = doValidation(args.db, doc, args.options).then(function () {
    return methods(args.db).put(doc, args.options);
  });
  nodify(promise, callback);
  return promise;
};

exports.validatingPost = function (doc, options, callback) {
  var args = processArgs(this, callback, options);

  doc._id = doc._id || uuid.v4();
  var promise = doValidation(args.db, doc, args.options).then(function () {
    return methods(args.db).post(doc, args.options);
  });
  nodify(promise, callback);
  return promise;
};

exports.validatingRemove = function (doc, options, callback) {
  var args = processArgs(this, callback, options);

  doc._deleted = true;
  var promise = doValidation(args.db, doc, args.options).then(function () {
    return methods(args.db).remove(doc, args.options);
  });
  nodify(promise, callback);
  return promise;
};

exports.validatingBulkDocs = function (bulkDocs, options, callback) {
  //the ``all_or_nothing`` attribute on ``bulkDocs`` is unsupported.
  //Also, the result array might not be in the same order as
  //``bulkDocs.docs``
  var args = processArgs(this, callback, options);

  var done = [];
  var notYetDone = [];

  if (!Array.isArray(bulkDocs)) {
    bulkDocs = bulkDocs.docs;
  }

  var validations = bulkDocs.map(function (doc) {
    doc._id = doc._id || uuid.v4();
    var validationPromise = doValidation(args.db, doc, args.options);

    return validationPromise.then(function (resp) {
      notYetDone.push(doc);
    }).catch(function (err) {
      err.id = doc._id;
      done.push(err);
    });
  });
  var allValidationsPromise = Promise.all(validations).then(function () {
    return methods(args.db).bulkDocs(notYetDone, args.options);
  }).then(function (insertedDocs) {
    return done.concat(insertedDocs);
  });
  nodify(allValidationsPromise, callback);
  return allValidationsPromise;
};

var vpa = function (docId, attachmentId, rev, attachment, type, options, callback) {
  var args = processArgs(this, callback, options);

  //get the doc
  var promise = args.db.get(docId, {rev: rev, revs: true}).catch(function (err) {
    return {_id: docId};
  }).then(function (doc) {
    //validate the doc + attachment
    doc._attachments = doc._attachments || {};
    doc._attachments[attachmentId] = {
      content_type: type,
      data: attachment,
    };
    return doValidation(args.db, doc, args.options);
  }).then(function () {
    //save the attachment
    return methods(args.db).putAttachment(docId, attachmentId, rev, attachment, type);
  });
  nodify(promise, callback);
  return promise;
};
exports.validatingPutAttachment = vpa;

var vra = function (docId, attachmentId, rev, options, callback) {
  var args = processArgs(this, callback, options);
  //get the doc
  var promise = args.db.get(docId, {rev: rev, revs: true}).then(function (doc) {
    //validate the doc without attachment
    delete doc._attachments[attachmentId];

    return doValidation(args.db, doc, args.options);
  }).then(function () {
    //remove the attachment
    return methods(args.db).removeAttachment(docId, attachmentId, rev);
  });
  nodify(promise, callback);
  return promise;
};
exports.validatingRemoveAttachment = vra;

exports.installValidationMethods = function () {
  var db = this;

  if (dbs.indexOf(db) !== -1) {
    throw new PouchPluginError({
      status: 500,
      name: "already_installed",
      message: "Validation methods are already installed on this database."
    });
  }

  dbs.push(db);
  methodsByDbsIdx.push(methodsFromDb(db));
  methodNames.forEach(function (name) {
    db[name] = exports["validating" + name[0].toUpperCase() + name.substr(1)].bind(db);
  });
};

exports.uninstallValidationMethods = function () {
  var db = this;

  var index = dbs.indexOf(db);
  if (index === -1) {
    throw new PouchPluginError({
      status: 500,
      name: "already_not_installed",
      message: "Validation methods are already not installed on this database."
    });
  }
  var meths = methods(db);
  methodNames.forEach(function (name) {
    db[name] = meths[name];
  });

  //cleanup
  dbs.splice(index, 1);
  methodsByDbsIdx.splice(index, 1);
};
<|endoftext|>


<commit_before>//FIRST STEP, get atomic posts to couchdb.
//SECOND STEP, get npm format working.

//request a package, to get the current version.

var fs = require('fs')
var path = require('path')
var url = require('url')

var request   = require('request')
var npmClient = require('npm-registry-client')
var shasum    = require('shasum')
var mkdirp    = require('mkdirp')

var readJSON = require('./read-json')

var opts = require('optimist').argv

function isSuccess(res) {
  return !(res.statusCode < 200 || res.statusCode >= 300)
}

module.exports = function (package, tarball, config, cb) {

  //couchdb uses a md5 digest & length
  //so we'll calculate that too, so that we can
  //verify the local cache is the same as
  //the couch doc.
  var digest = shasum(tarball, 'md5', 'base64')
  var tbLength = tarball.length

  function saveDoc (data, cb) {
    if(!data._rev || !data._etag)
      return console.error('no _rev or _etag'), cb()
    var dir = path.join(config.cache, data._id)
    mkdirp(dir, function () {
      fs.writeFile(path.join(dir, '.cache.json'), JSON.stringify(data), cb)
    })
  }

  function defaults (package) {
    return { _id : package.name
      , name : package.name
      , description : package.description
      , "dist-tags" : {}
      , versions : {}
      , readme: package.readme || ""
      , maintainers :
        [ { name : config.username
          , email : config.email
          }
        ]
      }

  }

  var method = 'PUT', self = this
  if(!cb) cb = config, config = null

  var tbName = package.name + "-" + package.version + ".tgz"
  var tbURI = package.name + "/-/" + tbName

  config = config || {
    cache    : this.conf.get('cache'),
    registry : this.conf.get('registry'),
    _auth     : this.conf.get('_auth'),
    username : this.conf.get('username'),
    email    : this.conf.get('email'),
    _token   : this.conf.get('_token'),
    cert     : this.conf.get('cert'),
    ca       : this.conf.get('ca'),
    key      : this.conf.get('key'),
  }
  //if we have read this before, we should have this in our cache!
  //with the correct _rev and all (unless another maintainer has published)
  //or this is a fresh os install, and we havn't installed our package either.

  readJSON(path.join(config.cache, package.name, '.cache.json'), function (err, data) {
    //if there was no file, assume a new publish
    next(err ? defaults(package) : data)
  })

  function next (data, abortIfFail) {
    if(data.versions[package.version] && !(config.force || opts.force))
      return cb(new Error(package.name + '@' + package.version + ' already exists'))

    var attachments = data._attachments = data._attachments || {}

    attachments[tbName] = {
      content_type: 'application/octet-stream',
      data: tarball.toString('base64')
    }

    package.dist = package.dist || {}
    package.dist.tarball = url.resolve(config.registry, tbURI)
                              .replace(/^https:\/\//, 'http://')
    package.dist.shasum = shasum(tarball)

    data.versions[package.version] = package
    data['dist-tags'].latest = package.version

    // keys matching /^_/ are handled specially by couch,
    // so we have to remove it before sending it to couch.
    delete data._etag

    var pkgUrl = url.resolve(config.registry,
      encodeURIComponent(package.name))

    console.log(method, pkgUrl)
    var https = /^https/.test(pkgUrl)

    var headers = {}

    if(config._token)
      headers.cookie = 'AuthSession='+config._token.AuthSession
    else
      headers['WWW-Authenticate'] = 'Basic ' +config._auth

    request({
      method    : method,
      url       : pkgUrl,
      json      : true,
      body      : data,
      headers   : headers,
      ca        : https ? config.ca : undefined,
      strictSSL : https
    },
    function (err, res, json) {
      console.error(res && res.statusCode, pkgUrl, package.dist.shasum)
      if(err) return cb(err)
      if(res.statusCode == 409) { // get the current value, and try again.
        //throw new Error('not implemented yet')
        if(abortIfFail)
          return cb(new Error('not attempting to publish twice'))
        console.error('GET', pkgUrl)
        return request({url: pkgUrl, json: true, ca: config.ca, strictSSL: true},
        function (err, res, data) {
          if(err) cb(err)
          console.error(res.statusCode, pkgUrl)
          //if there was a 404, there must have been an unpublish.
          //try again, assuming this.

          if(res.statusCode === 404)
            return next(defaults(package), true)

          if(!isSuccess(res))
            return cb(new Error('get request failed, got:'+res.statusCode + ' ' + res.reason || data && data.reason))

          data._etag = res.etag

          saveDoc(data, function (err) {
            if(err) return cb(err)
            next(data, true)
          })
        })
      }

      if(!isSuccess(res))
        return cb(err, json)

      var attachment = data._attachments[tbName]

      delete attachment.data
      attachment.digest = 'md5-'+digest
      attachment.length = tbLength
      attachment.stub = true

      data._rev = json.rev || res.headers['x-couch-update-newrev']
      data._etag = res.headers.etag || data._rev

      saveDoc(data, function (err) {
        if(err) throw err
        console.log(res.headers)
        console.log(JSON.stringify(json, null, 2))
      })
    })
  }
}

<commit_msg>fix missing etag problem<commit_after>//FIRST STEP, get atomic posts to couchdb.
//SECOND STEP, get npm format working.

//request a package, to get the current version.

var fs = require('fs')
var path = require('path')
var url = require('url')

var request   = require('request')
var npmClient = require('npm-registry-client')
var shasum    = require('shasum')
var mkdirp    = require('mkdirp')

var readJSON = require('./read-json')

var opts = require('optimist').argv

function isSuccess(res) {
  return !(res.statusCode < 200 || res.statusCode >= 300)
}

module.exports = function (package, tarball, config, cb) {

  //couchdb uses a md5 digest & length
  //so we'll calculate that too, so that we can
  //verify the local cache is the same as
  //the couch doc.
  var digest = shasum(tarball, 'md5', 'base64')
  var tbLength = tarball.length

  function saveDoc (data, cb) {
    if(!data._rev || !data._etag)
      return console.error('no _rev or _etag'), cb()
    var dir = path.join(config.cache, data._id)
    mkdirp(dir, function () {
      fs.writeFile(path.join(dir, '.cache.json'), JSON.stringify(data), cb)
    })
  }

  function defaults (package) {
    return { _id : package.name
      , name : package.name
      , description : package.description
      , "dist-tags" : {}
      , versions : {}
      , readme: package.readme || ""
      , maintainers :
        [ { name : config.username
          , email : config.email
          }
        ]
      }

  }

  var method = 'PUT', self = this
  if(!cb) cb = config, config = null

  var tbName = package.name + "-" + package.version + ".tgz"
  var tbURI = package.name + "/-/" + tbName

  config = config || {
    cache    : this.conf.get('cache'),
    registry : this.conf.get('registry'),
    _auth     : this.conf.get('_auth'),
    username : this.conf.get('username'),
    email    : this.conf.get('email'),
    _token   : this.conf.get('_token'),
    cert     : this.conf.get('cert'),
    ca       : this.conf.get('ca'),
    key      : this.conf.get('key'),
  }
  //if we have read this before, we should have this in our cache!
  //with the correct _rev and all (unless another maintainer has published)
  //or this is a fresh os install, and we havn't installed our package either.

  readJSON(path.join(config.cache, package.name, '.cache.json'), function (err, data) {
    //if there was no file, assume a new publish
    next(err ? defaults(package) : data)
  })

  function next (data, abortIfFail) {
    if(data.versions[package.version] && !(config.force || opts.force))
      return cb(new Error(package.name + '@' + package.version + ' already exists'))

    var attachments = data._attachments = data._attachments || {}

    attachments[tbName] = {
      content_type: 'application/octet-stream',
      data: tarball.toString('base64')
    }

    package.dist = package.dist || {}
    package.dist.tarball = url.resolve(config.registry, tbURI)
                              .replace(/^https:\/\//, 'http://')
    package.dist.shasum = shasum(tarball)

    data.versions[package.version] = package
    data['dist-tags'].latest = package.version

    // keys matching /^_/ are handled specially by couch,
    // so we have to remove it before sending it to couch.
    delete data._etag

    var pkgUrl = url.resolve(config.registry,
      encodeURIComponent(package.name))

    console.log(method, pkgUrl)
    var https = /^https/.test(pkgUrl)

    var headers = {}

    if(config._token)
      headers.cookie = 'AuthSession='+config._token.AuthSession
    else
      headers['WWW-Authenticate'] = 'Basic ' +config._auth

    request({
      method    : method,
      url       : pkgUrl,
      json      : true,
      body      : data,
      headers   : headers,
      ca        : https ? config.ca : undefined,
      strictSSL : https
    },
    function (err, res, json) {
      console.error(res && res.statusCode, pkgUrl, package.dist.shasum)
      if(err) return cb(err)
      if(res.statusCode == 409) { // get the current value, and try again.
        //throw new Error('not implemented yet')
        if(abortIfFail)
          return cb(new Error('not attempting to publish twice'))
        console.error('GET', pkgUrl)
        return request({url: pkgUrl, json: true, ca: config.ca, strictSSL: true},
        function (err, res, data) {
          if(err) cb(err)
          console.error(res.statusCode, pkgUrl)
          //if there was a 404, there must have been an unpublish.
          //try again, assuming this.

          if(res.statusCode === 404)
            return next(defaults(package), true)

          if(!isSuccess(res))
            return cb(new Error('get request failed, got:'+res.statusCode + ' ' + res.reason || data && data.reason))

          data._etag = res.headers.etag || data._rev

          saveDoc(data, function (err) {
            if(err) return cb(err)
            next(data, true)
          })
        })
      }

      if(!isSuccess(res))
        return cb(err, json)

      var attachment = data._attachments[tbName]

      delete attachment.data
      attachment.digest = 'md5-'+digest
      attachment.length = tbLength
      attachment.stub = true

      data._rev = json.rev || res.headers['x-couch-update-newrev']
      data._etag = res.headers.etag || data._rev

      saveDoc(data, function (err) {
        if(err) throw err
        console.log(res.headers)
        console.log(JSON.stringify(json, null, 2))
      })
    })
  }
}

<|endoftext|>


<commit_before>/**
 * Client
 */

var events = require('events');
var crypto = require('crypto');
var inherits = require('util').inherits;
var Promise = require('bluebird').Promise;

var Schema = require('./schema');
Schema.Connection = require('./connection').Connection;

var DEFAULT_HOST = 'api.schema.io';
var DEFAULT_PORT = 8443;
var DEFAULT_VERIFY_CERT = true;
var DEFAULT_VERSION = 1;

/**
 * Client constructor
 *
 * @param  string clientId
 * @param  string clientKey
 * @param  object options
 * @param  function callback
 */
var Client = function(clientId, clientKey, options, callback) {

  events.EventEmitter.call(this);

  this.server = null;

  if (clientId) {
    this.init(clientId, clientKey, options);
  }
  if (callback) {
    this.connect(callback);
  }
};

inherits(Client, events.EventEmitter);

/**
 * Initialize client parameters
 *
 * @param  string clientId
 * @param  string clientKey
 * @param  object options
 */
Client.prototype.init = function(clientId, clientKey, options) {

  options = options || {};

  if (typeof clientKey === 'object') {
    options = clientKey;
    clientKey = undefined;
  } else if (typeof clientId === 'object') {
    options = clientId;
    clientId = undefined;
  }

  this.params = {
    clientId: clientId || options.id,
    clientKey: clientKey || options.key,
    host: options.host || DEFAULT_HOST,
    port: options.port || DEFAULT_PORT,
    verifyCert: options.verifyCert !== undefined ? options.verifyCert : DEFAULT_VERIFY_CERT,
    version: options.version || DEFAULT_VERSION,
    session: options.session,
    route: options.route,
    routeClientId: options.route && options.route.client
  };

  if (!this.params.clientId) {
    throw new Error('Schema client `id` is required to initialize');
  }
  if (!this.params.clientKey) {
    throw new Error('Schema client `key` is required to initialize');
  }
};

/**
 * Connect to server
 *
 * @param  function callback
 */
Client.prototype.connect = function(callback) {

  var self = this;
  this.server = new Schema.Connection(
    this.params.host,
    this.params.port,
    {
      verifyCert: this.params.verifyCert
    },
    function() {
      callback && callback(self);
      self.emit('connect', self);
    }
  );
  this.server.on('close', function() {
    self.emit('close');
  });
  this.server.on('error', function(err) {
    if (events.EventEmitter.listenerCount(self, 'error')) {
      self.emit('error', 'Error: '+err);
    }
  });
  this.server.on('error.network', function(err) {
    if (events.EventEmitter.listenerCount(self, 'error')) {
      self.emit('error', 'Network Error: '+err, 'network');
    }
  });
  this.server.on('error.protocol', function(err) {
    if (events.EventEmitter.listenerCount(self, 'error')) {
      self.emit('error', 'Protocol Error: '+err, 'protocol');
    }
  });
  this.server.on('error.server', function(err) {
    if (events.EventEmitter.listenerCount(self, 'error')) {
      self.emit('error', 'Server Error: '+err, 'server');
    }
  });
};

/**
 * Client request handler
 *
 * @param  string method
 * @param  string url
 * @param  mixed data
 * @param  function callback
 */
Client.prototype.request = function(method, url, data, callback) {

  if (typeof data === 'function') {
    callback = data;
    data = null;
  }

  // TODO: implement $cached

  if (!this.server) {
    this.connect();
  }

  // Resolve data as promised
  var promises = this.promisifyData(data);
  if (promises.length) {
    return Promise.all(promises).bind(this).then(function() {
      this.request(method, url, data, callback);
    });
  }

  // Prepare url and data for request
  url = url && url.toString ? url.toString() : '';
  data = {$data: data};

  if (this.authed !== true) {
    data.$client = this.params.clientId;
    data.$key = this.params.clientKey;

    if (this.params.route) {
      data.$route = this.params.route;
    }
  }

  var self = this;
  return new Promise(function(resolve, reject) {
    var responder = function(err, data, response) {
      if (callback) {
        callback(err, data, response);
      }
      if (err) {
        reject(err);
      } else {
        resolve(data, response);
      }
    };
    self.server.request(method, url, data, function(response) {
      if (response.$auth) {
        if (response.$end) {
          // Connection ended, retry auth
          return self.request(method, url, data.$data, callback);
        } else {
          self.authed = true;
          return self.auth(response.$auth, function(response) {
            return self.respond(method, url, response, responder);
          });
        }
      } else {
        return self.respond(method, url, response, responder);
      }
    });
  });
};

/**
 * Resolve and return promises array from data
 * Only resolve top level object keys
 *
 * @param  object data
 * @return array
 */
Client.prototype.promisifyData = function(data) {

  if (!data) {
    return [];
  }

  function thenResolvePromisedValue(data, key) {
    data[key].then(function(val) {
      data[key] = val;
    });
  }

  var promises = [];
  if (typeof data === 'object') {
    var keys = Object.keys(data);
    for (var i = 0; i < keys.length; i++) {
      var key = keys[i];
      if (data[key] && data[key].then) {
        promises.push(data[key]);
        thenResolvePromisedValue(data, key);
      }
    }
  } else if (data instanceof Array) {
    for (var i = 0; i < data.length; i++) {
      if (data[i] && data[i].then) {
        promises.push(data[i]);
        thenResolvePromisedValue(data, i);
      }
    }
  }

  return promises;
};

/**
 * Client response handler
 *
 * @param  string method
 * @param  string url
 * @param  mixed response
 * @param  function callback
 */
Client.prototype.respond = function(method, url, response, callback) {

  var err = undefined;
  var data = undefined;

  if (response) {
    if (response.$error) {
      err = response.$error;
    }
    if (response.$data && (typeof response.$data === 'object')) {
      data = Client.createResource(response.$url || url, response, this);
    } else {
      data = response.$data;
    }
  } else {
    response = {$error: 'Empty response from server', $status: 500};
    err = response.$error;
  }
  return callback.call(this, err, data, response);
};

/**
 * Client GET request
 *
 * @param  string url
 * @param  object data
 * @param  function callback
 * @return mixed
 */
Client.prototype.get = function(url, data, callback) {
  return this.request('get', url, data, callback);
};

/**
 * Client PUT request
 */
Client.prototype.put = function(url, data, callback) {
  return this.request('put', url, data, callback);
};

/**
 * Client POST request
 */
Client.prototype.post = function(url, data, callback) {
  return this.request('post', url, data, callback);
};

/**
 * Client DELETE request
 */
Client.prototype.delete = function(url, data, callback) {
  return this.request('delete', url, data, callback);
};

/**
 * Client auth request
 *
 * @param  object params
 * @param  function callback
 * @return mixed;
 */
Client.prototype.auth = function(nonce, callback) {

  var self = this;
  var clientId = this.params.clientId;
  var clientKey = this.params.clientKey;

  if (typeof nonce === 'function') {
    callback = nonce;
    nonce = null;
  }

  if (!this.server) {
    this.connect();
  }

  // 1) Get nonce
  if (!nonce) {
    return this.server.request('auth', function(nonce) {
      self.auth(nonce, callback);
    });
  }

  // 2) Create key hash
  var keyHash = crypto.createHash('md5')
    .update(clientId + "::" + clientKey)
    .digest('hex');

  // 3) Create auth key
  var authKey = crypto.createHash('md5')
    .update(nonce + clientId + keyHash)
    .digest('hex');

  // 4) Authenticate with client creds and options
  var creds = {
    client: clientId,
    key: authKey
  };
  if (this.params.version) {
    creds.$v = this.params.version;
  }
  if (this.params.session) {
    creds.$session = this.params.session;
  }
  if (this.params.route) {
    creds.$route = this.params.route;
  }

  // TODO: send local $ip address

  return this.server.request('auth', creds, callback);
};

/**
 * Close server connection
 *
 * @return void
 */
Client.prototype.close = function() {

  if (this.server) {
    this.server.close();
  }
};

/**
 * Client create/init helper
 *
 * @param  string clientId
 * @param  string clientKey
 * @param  object options
 * @param  function callback
 * @return Client
 */
Client.create = function(clientId, clientKey, options, callback) {
  return new Client(clientId, clientKey, options, callback);
};

/**
 * Create a resource from response data
 *
 * @param  string url
 * @param  mixed response
 * @param  Client client
 * @return Resource
 */
Client.createResource = function(url, response, client) {
  if (response && response.$data && 'count' in response.$data && response.$data.results) {
    return new Schema.Collection(url, response, client);
  }
  return new Schema.Record(url, response, client);
};

// Exports
module.exports = Schema;
module.exports.Client = Client;
module.exports.createClient = Client.create;
<commit_msg>Reject with error<commit_after>/**
 * Client
 */

var events = require('events');
var crypto = require('crypto');
var inherits = require('util').inherits;
var Promise = require('bluebird').Promise;

var Schema = require('./schema');
Schema.Connection = require('./connection').Connection;

var DEFAULT_HOST = 'api.schema.io';
var DEFAULT_PORT = 8443;
var DEFAULT_VERIFY_CERT = true;
var DEFAULT_VERSION = 1;

/**
 * Client constructor
 *
 * @param  string clientId
 * @param  string clientKey
 * @param  object options
 * @param  function callback
 */
var Client = function(clientId, clientKey, options, callback) {

  events.EventEmitter.call(this);

  this.server = null;

  if (clientId) {
    this.init(clientId, clientKey, options);
  }
  if (callback) {
    this.connect(callback);
  }
};

inherits(Client, events.EventEmitter);

/**
 * Initialize client parameters
 *
 * @param  string clientId
 * @param  string clientKey
 * @param  object options
 */
Client.prototype.init = function(clientId, clientKey, options) {

  options = options || {};

  if (typeof clientKey === 'object') {
    options = clientKey;
    clientKey = undefined;
  } else if (typeof clientId === 'object') {
    options = clientId;
    clientId = undefined;
  }

  this.params = {
    clientId: clientId || options.id,
    clientKey: clientKey || options.key,
    host: options.host || DEFAULT_HOST,
    port: options.port || DEFAULT_PORT,
    verifyCert: options.verifyCert !== undefined ? options.verifyCert : DEFAULT_VERIFY_CERT,
    version: options.version || DEFAULT_VERSION,
    session: options.session,
    route: options.route,
    routeClientId: options.route && options.route.client
  };

  if (!this.params.clientId) {
    throw new Error('Schema client `id` is required to initialize');
  }
  if (!this.params.clientKey) {
    throw new Error('Schema client `key` is required to initialize');
  }
};

/**
 * Connect to server
 *
 * @param  function callback
 */
Client.prototype.connect = function(callback) {

  var self = this;
  this.server = new Schema.Connection(
    this.params.host,
    this.params.port,
    {
      verifyCert: this.params.verifyCert
    },
    function() {
      callback && callback(self);
      self.emit('connect', self);
    }
  );
  this.server.on('close', function() {
    self.emit('close');
  });
  this.server.on('error', function(err) {
    if (events.EventEmitter.listenerCount(self, 'error')) {
      self.emit('error', 'Error: '+err);
    }
  });
  this.server.on('error.network', function(err) {
    if (events.EventEmitter.listenerCount(self, 'error')) {
      self.emit('error', 'Network Error: '+err, 'network');
    }
  });
  this.server.on('error.protocol', function(err) {
    if (events.EventEmitter.listenerCount(self, 'error')) {
      self.emit('error', 'Protocol Error: '+err, 'protocol');
    }
  });
  this.server.on('error.server', function(err) {
    if (events.EventEmitter.listenerCount(self, 'error')) {
      self.emit('error', 'Server Error: '+err, 'server');
    }
  });
};

/**
 * Client request handler
 *
 * @param  string method
 * @param  string url
 * @param  mixed data
 * @param  function callback
 */
Client.prototype.request = function(method, url, data, callback) {

  if (typeof data === 'function') {
    callback = data;
    data = null;
  }

  // TODO: implement $cached

  if (!this.server) {
    this.connect();
  }

  // Resolve data as promised
  var promises = this.promisifyData(data);
  if (promises.length) {
    return Promise.all(promises).bind(this).then(function() {
      this.request(method, url, data, callback);
    });
  }

  // Prepare url and data for request
  url = url && url.toString ? url.toString() : '';
  data = {$data: data};

  if (this.authed !== true) {
    data.$client = this.params.clientId;
    data.$key = this.params.clientKey;

    if (this.params.route) {
      data.$route = this.params.route;
    }
  }

  var self = this;
  return new Promise(function(resolve, reject) {
    var responder = function(err, data, response) {
      if (callback) {
        callback(err, data, response);
      }
      if (err) {
        reject(new Error(err));
      } else {
        resolve(data, response);
      }
    };
    self.server.request(method, url, data, function(response) {
      if (response.$auth) {
        if (response.$end) {
          // Connection ended, retry auth
          return self.request(method, url, data.$data, callback);
        } else {
          self.authed = true;
          return self.auth(response.$auth, function(response) {
            return self.respond(method, url, response, responder);
          });
        }
      } else {
        return self.respond(method, url, response, responder);
      }
    });
  });
};

/**
 * Resolve and return promises array from data
 * Only resolve top level object keys
 *
 * @param  object data
 * @return array
 */
Client.prototype.promisifyData = function(data) {

  if (!data) {
    return [];
  }

  function thenResolvePromisedValue(data, key) {
    data[key].then(function(val) {
      data[key] = val;
    });
  }

  var promises = [];
  if (typeof data === 'object') {
    var keys = Object.keys(data);
    for (var i = 0; i < keys.length; i++) {
      var key = keys[i];
      if (data[key] && data[key].then) {
        promises.push(data[key]);
        thenResolvePromisedValue(data, key);
      }
    }
  } else if (data instanceof Array) {
    for (var i = 0; i < data.length; i++) {
      if (data[i] && data[i].then) {
        promises.push(data[i]);
        thenResolvePromisedValue(data, i);
      }
    }
  }

  return promises;
};

/**
 * Client response handler
 *
 * @param  string method
 * @param  string url
 * @param  mixed response
 * @param  function callback
 */
Client.prototype.respond = function(method, url, response, callback) {

  var err = undefined;
  var data = undefined;

  if (response) {
    if (response.$error) {
      err = response.$error;
    }
    if (response.$data && (typeof response.$data === 'object')) {
      data = Client.createResource(response.$url || url, response, this);
    } else {
      data = response.$data;
    }
  } else {
    response = {$error: 'Empty response from server', $status: 500};
    err = response.$error;
  }
  return callback.call(this, err, data, response);
};

/**
 * Client GET request
 *
 * @param  string url
 * @param  object data
 * @param  function callback
 * @return mixed
 */
Client.prototype.get = function(url, data, callback) {
  return this.request('get', url, data, callback);
};

/**
 * Client PUT request
 */
Client.prototype.put = function(url, data, callback) {
  return this.request('put', url, data, callback);
};

/**
 * Client POST request
 */
Client.prototype.post = function(url, data, callback) {
  return this.request('post', url, data, callback);
};

/**
 * Client DELETE request
 */
Client.prototype.delete = function(url, data, callback) {
  return this.request('delete', url, data, callback);
};

/**
 * Client auth request
 *
 * @param  object params
 * @param  function callback
 * @return mixed;
 */
Client.prototype.auth = function(nonce, callback) {

  var self = this;
  var clientId = this.params.clientId;
  var clientKey = this.params.clientKey;

  if (typeof nonce === 'function') {
    callback = nonce;
    nonce = null;
  }

  if (!this.server) {
    this.connect();
  }

  // 1) Get nonce
  if (!nonce) {
    return this.server.request('auth', function(nonce) {
      self.auth(nonce, callback);
    });
  }

  // 2) Create key hash
  var keyHash = crypto.createHash('md5')
    .update(clientId + "::" + clientKey)
    .digest('hex');

  // 3) Create auth key
  var authKey = crypto.createHash('md5')
    .update(nonce + clientId + keyHash)
    .digest('hex');

  // 4) Authenticate with client creds and options
  var creds = {
    client: clientId,
    key: authKey
  };
  if (this.params.version) {
    creds.$v = this.params.version;
  }
  if (this.params.session) {
    creds.$session = this.params.session;
  }
  if (this.params.route) {
    creds.$route = this.params.route;
  }

  // TODO: send local $ip address

  return this.server.request('auth', creds, callback);
};

/**
 * Close server connection
 *
 * @return void
 */
Client.prototype.close = function() {

  if (this.server) {
    this.server.close();
  }
};

/**
 * Client create/init helper
 *
 * @param  string clientId
 * @param  string clientKey
 * @param  object options
 * @param  function callback
 * @return Client
 */
Client.create = function(clientId, clientKey, options, callback) {
  return new Client(clientId, clientKey, options, callback);
};

/**
 * Create a resource from response data
 *
 * @param  string url
 * @param  mixed response
 * @param  Client client
 * @return Resource
 */
Client.createResource = function(url, response, client) {
  if (response && response.$data && 'count' in response.$data && response.$data.results) {
    return new Schema.Collection(url, response, client);
  }
  return new Schema.Record(url, response, client);
};

// Exports
module.exports = Schema;
module.exports.Client = Client;
module.exports.createClient = Client.create;
<|endoftext|>


<commit_before>/*!
 *
 * Copyright (c) 2013 Sebastian Golasch
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

'use strict';

// ext. libs
var fs = require('fs');
var path = require('path');
var jsonxml = require('jsontoxml');

// int. global
var reporter = null;

/**
 * The jUnit reporter can produce a jUnit compatible file with the results of your testrun,
 * this reporter enables you to use daleks testresults within a CI environment like Jenkins.
 *
 * The reporter can be installed with the following command:
 *
 * ```bash
 * $ npm install dalek-reporter-junit --save-dev
 * ```
 *
 * The file will follow the jUnit XML format:
 *
 * ```html
 * <?xml version="1.0" encoding="utf-8"?>
 * <resource name="DalekJSTest">
 *     <testsuite start="1375125067" name="Click - DalekJS guinea pig [Phantomjs]" end="1375125067" totalTests="1">
 *         <testcase start="1375125067" name="Can click a select option (OK, jQuery style, no message)" end="1375125067" result="pass">
 *             <variation start="1375125067" name="val" end="1375125067">
 *                 <severity>pass</severity>
 *                 <description><![CDATA[David is the favourite]]></description>
 *                 <resource>DalekJSTest</resource>
 *             </variation>
 *             <variation start="1375125067" name="val" end="1375125067">
 *                 <severity>pass</severity>
 *                 <description><![CDATA[Matt is now my favourite, bow ties are cool]]></description>
 *                 <resource>DalekJSTest</resource>
 *             </variation>
 *         </testcase>
 *     </testsuite>
 *  </resource>
 * ```
 *
 * By default the file will be written to `report/dalek.xml`,
 * you can change this by adding a config option to the your Dalekfile
 *
 * ```javascript
 * "junit-reporter": {
 *   "dest": "your/folder/your_file.xml"
 * }
 * ```
 *
 * If you would like to use the reporter (in addition to the std. console reporter),
 * you can start dalek with a special command line argument
 *
 * ```bash
 * $ dalek your_test.js -r console,junit
 * ```
 *
 * or you can add it to your Dalekfile
 *
 * ```javascript
 * "reporter": ["console", "junit"]
 * ```
 *
 * @class Reporter
 * @constructor
 * @part JUnit
 * @api
 */

function Reporter (opts) {
  this.events = opts.events;
  this.config = opts.config;
  this.testCount = 0;
  this.testIdx = -1;
  this.variationCount = -1;
  this.data = {};
  this.data.tests = [];
  this.lastAssertion = {};
  this.browser = null;

  var defaultReportFolder = 'report';
  this.dest = this.config.get('junit-reporter') && this.config.get('junit-reporter').dest ? this.config.get('junit-reporter').dest : defaultReportFolder;

  // prepare base xml
  this.xml = [
    {
      name: 'resource',
      attrs: {
        name:'DalekJSTest'
      },
      children: []
    }
  ];

  this.startListening();
}

/**
 * @module Reporter
 */

module.exports = function (opts) {
  if (reporter === null) {
    reporter = new Reporter(opts);
  }

  return reporter;
};

Reporter.prototype = {

  /**
   * Connects to all the event listeners
   *
   * @method startListening
   * @chainable
   */

  startListening: function () {
    this.events.on('report:run:browser', this.runBrowser.bind(this));
    this.events.on('report:assertion', this.assertion.bind(this));
    this.events.on('report:test:started', this.testStarted.bind(this));
    this.events.on('report:test:finished', this.testFinished.bind(this));
    this.events.on('report:runner:finished', this.runnerFinished.bind(this));
    this.events.on('report:testsuite:started', this.testsuiteStarted.bind(this));
    this.events.on('report:testsuite:finished', this.testsuiteFinished.bind(this));
    return this;
  },

  /**
   * Stores the current browser name
   *
   * @method runBrowser
   * @param {string} browser Browser name
   * @chainable
   */

  runBrowser: function (browser) {
    this.browser = browser;
    return this;
  },

  /**
   * Generates XML skeleton for testsuites
   *
   * @method testsuiteStarted
   * @param {string} name Testsuite name
   * @chainable
   */

  testsuiteStarted: function (name) {
    this.testCount = 0;
    this.testIdx++;
    this.xml[0].children.push({
      name: 'testsuite',
      children: [],
      attrs: {
        start: Math.round(new Date().getTime() / 1000),
        name: name + ' [' + this.browser + ']',
        end: null,
        totalTests: 0
      }
    });
    return this;
  },

  /**
   * Finishes XML skeleton for testsuites
   *
   * @method testsuiteFinished
   * @chainable
   */

  testsuiteFinished: function () {
    var timestamp = Math.round(new Date().getTime() / 1000);
    this.xml[0].children[this.testIdx].children[this.testCount].attrs.end = timestamp;
    this.xml[0].children[this.testIdx].children[this.testCount].attrs.result = this.lastAssertion.success ? 'Passed' : 'Failed';

    if (this.variationCount > -1) {
      this.xml[0].children[this.testIdx].children[this.testCount].children[this.variationCount].attrs.end = timestamp;
    }

    this.xml[0].children[this.testIdx].attrs.end = timestamp;
    return this;
  },

  /**
   * Generates XML skeleton for an assertion
   *
   * @method assertion
   * @param {object} data Event data
   * @chainable
   */

  assertion: function (data) {
    var timestamp = Math.round(new Date().getTime() / 1000);
    this.xml[0].children[this.testIdx].children[this.testCount].children.push({
      name: 'variation',
      attrs: {
        start: timestamp,
        name: data.type,
        end: null
      },
      children: [
        {name: 'severity', text: (data.success ? 'pass' : 'fail') },
        {name: 'description', text: jsonxml.cdata((data.message ? data.message : 'Expected: ' + data.expected + 'Actual: ' + data.value)) },
        {name: 'resource', text: 'DalekJSTest'}
      ]
    });

    if (this.variationCount > -1 && this.xml[0].children[this.testIdx].children[this.testCount].children[this.variationCount]) {
      this.xml[0].children[this.testIdx].children[this.testCount].children[this.variationCount].attrs.end = timestamp;
    }

    this.variationCount++;
    return this;
  },

  /**
   * Generates XML skeleton for a testcase
   *
   * @method testStarted
   * @param {object} data Event data
   * @chainable
   */

  testStarted: function (data) {
    this.variationCount = -1;
    this.xml[0].children[this.testIdx].children.push({
      name: 'testcase',
      children: [],
      attrs: {
        start: Math.round(new Date().getTime() / 1000),
        name: data.name,
        end: null,
        result: null
      }
    });

    return this;
  },

  /**
   * Finishes XML skeleton for a testcase
   *
   * @method testFinished
   * @param {object} data Event data
   * @chainable
   */

  testFinished: function (data) {
    var timestamp = Math.round(new Date().getTime() / 1000);
    this.xml[0].children[this.testIdx].children[this.testCount].attrs.end = timestamp;
    this.xml[0].children[this.testIdx].children[this.testCount].attrs.result = data.status ? 'Passed' : 'Failed';

    if (this.variationCount > -1) {
      this.xml[0].children[this.testIdx].children[this.testCount].children[this.variationCount].attrs.end = timestamp;
    }

    this.testCount++;
    this.variationCount = -1;
    this.xml[0].children[this.testIdx].attrs.totalTests = this.testCount;
    return this;
  },

  /**
   * Finishes XML and writes file to the file system
   *
   * @method runnerFinished
   * @param {object} data Event data
   * @chainable
   */

  runnerFinished: function (data) {
    this.data.elapsedTime = data.elapsedTime;
    this.data.status = data.status;
    this.data.assertions = data.assertions;
    this.data.assertionsFailed = data.assertionsFailed;
    this.data.assertionsPassed = data.assertionsPassed;

    var contents = jsonxml(this.xml, {escape: true, removeIllegalNameCharacters: true, prettyPrint: true, xmlHeader: 'version="1.0" encoding="UTF-8"'});

    if (path.extname(this.dest) !== '.xml') {
      this.dest = this.dest + '/dalek.xml';
    }

    this.events.emit('report:written', {type: 'junit', dest: this.dest});
    this._recursiveMakeDirSync(path.dirname(this.dest.replace(path.basename(this.dest, ''))));
    fs.writeFileSync(this.dest, contents, 'utf8');
  },

  /**
   * Helper method to generate deeper nested directory structures
   *
   * @method _recursiveMakeDirSync
   * @param {string} path Path to create
   */

  _recursiveMakeDirSync: function (path) {
    var pathSep = require('path').sep;
    var dirs = path.split(pathSep);
    var root = '';

    while (dirs.length > 0) {
      var dir = dirs.shift();
      if (dir === '') {
        root = pathSep;
      }
      if (!fs.existsSync(root + dir)) {
        fs.mkdirSync(root + dir);
      }
      root += dir + pathSep;
    }
  }
};
<commit_msg>Save last assertion<commit_after>/*!
 *
 * Copyright (c) 2013 Sebastian Golasch
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */

'use strict';

// ext. libs
var fs = require('fs');
var path = require('path');
var jsonxml = require('jsontoxml');

// int. global
var reporter = null;

/**
 * The jUnit reporter can produce a jUnit compatible file with the results of your testrun,
 * this reporter enables you to use daleks testresults within a CI environment like Jenkins.
 *
 * The reporter can be installed with the following command:
 *
 * ```bash
 * $ npm install dalek-reporter-junit --save-dev
 * ```
 *
 * The file will follow the jUnit XML format:
 *
 * ```html
 * <?xml version="1.0" encoding="utf-8"?>
 * <resource name="DalekJSTest">
 *     <testsuite start="1375125067" name="Click - DalekJS guinea pig [Phantomjs]" end="1375125067" totalTests="1">
 *         <testcase start="1375125067" name="Can click a select option (OK, jQuery style, no message)" end="1375125067" result="pass">
 *             <variation start="1375125067" name="val" end="1375125067">
 *                 <severity>pass</severity>
 *                 <description><![CDATA[David is the favourite]]></description>
 *                 <resource>DalekJSTest</resource>
 *             </variation>
 *             <variation start="1375125067" name="val" end="1375125067">
 *                 <severity>pass</severity>
 *                 <description><![CDATA[Matt is now my favourite, bow ties are cool]]></description>
 *                 <resource>DalekJSTest</resource>
 *             </variation>
 *         </testcase>
 *     </testsuite>
 *  </resource>
 * ```
 *
 * By default the file will be written to `report/dalek.xml`,
 * you can change this by adding a config option to the your Dalekfile
 *
 * ```javascript
 * "junit-reporter": {
 *   "dest": "your/folder/your_file.xml"
 * }
 * ```
 *
 * If you would like to use the reporter (in addition to the std. console reporter),
 * you can start dalek with a special command line argument
 *
 * ```bash
 * $ dalek your_test.js -r console,junit
 * ```
 *
 * or you can add it to your Dalekfile
 *
 * ```javascript
 * "reporter": ["console", "junit"]
 * ```
 *
 * @class Reporter
 * @constructor
 * @part JUnit
 * @api
 */

function Reporter (opts) {
  this.events = opts.events;
  this.config = opts.config;
  this.testCount = 0;
  this.testIdx = -1;
  this.variationCount = -1;
  this.data = {};
  this.data.tests = [];
  this.lastAssertion = {};
  this.browser = null;

  var defaultReportFolder = 'report';
  this.dest = this.config.get('junit-reporter') && this.config.get('junit-reporter').dest ? this.config.get('junit-reporter').dest : defaultReportFolder;

  // prepare base xml
  this.xml = [
    {
      name: 'resource',
      attrs: {
        name:'DalekJSTest'
      },
      children: []
    }
  ];

  this.startListening();
}

/**
 * @module Reporter
 */

module.exports = function (opts) {
  if (reporter === null) {
    reporter = new Reporter(opts);
  }

  return reporter;
};

Reporter.prototype = {

  /**
   * Connects to all the event listeners
   *
   * @method startListening
   * @chainable
   */

  startListening: function () {
    this.events.on('report:run:browser', this.runBrowser.bind(this));
    this.events.on('report:assertion', this.assertion.bind(this));
    this.events.on('report:test:started', this.testStarted.bind(this));
    this.events.on('report:test:finished', this.testFinished.bind(this));
    this.events.on('report:runner:finished', this.runnerFinished.bind(this));
    this.events.on('report:testsuite:started', this.testsuiteStarted.bind(this));
    this.events.on('report:testsuite:finished', this.testsuiteFinished.bind(this));
    return this;
  },

  /**
   * Stores the current browser name
   *
   * @method runBrowser
   * @param {string} browser Browser name
   * @chainable
   */

  runBrowser: function (browser) {
    this.browser = browser;
    return this;
  },

  /**
   * Generates XML skeleton for testsuites
   *
   * @method testsuiteStarted
   * @param {string} name Testsuite name
   * @chainable
   */

  testsuiteStarted: function (name) {
    this.testCount = 0;
    this.testIdx++;
    this.xml[0].children.push({
      name: 'testsuite',
      children: [],
      attrs: {
        start: Math.round(new Date().getTime() / 1000),
        name: name + ' [' + this.browser + ']',
        end: null,
        totalTests: 0
      }
    });
    return this;
  },

  /**
   * Finishes XML skeleton for testsuites
   *
   * @method testsuiteFinished
   * @chainable
   */

  testsuiteFinished: function () {
    var timestamp = Math.round(new Date().getTime() / 1000);
    this.xml[0].children[this.testIdx].children[this.testCount].attrs.end = timestamp;
    this.xml[0].children[this.testIdx].children[this.testCount].attrs.result = this.lastAssertion.success ? 'Passed' : 'Failed';

    if (this.variationCount > -1) {
      this.xml[0].children[this.testIdx].children[this.testCount].children[this.variationCount].attrs.end = timestamp;
    }

    this.xml[0].children[this.testIdx].attrs.end = timestamp;
    return this;
  },

  /**
   * Generates XML skeleton for an assertion
   *
   * @method assertion
   * @param {object} data Event data
   * @chainable
   */

  assertion: function (data) {
    var timestamp = Math.round(new Date().getTime() / 1000);
    this.xml[0].children[this.testIdx].children[this.testCount].children.push({
      name: 'variation',
      attrs: {
        start: timestamp,
        name: data.type,
        end: null
      },
      children: [
        {name: 'severity', text: (data.success ? 'pass' : 'fail') },
        {name: 'description', text: jsonxml.cdata((data.message ? data.message : 'Expected: ' + data.expected + 'Actual: ' + data.value)) },
        {name: 'resource', text: 'DalekJSTest'}
      ]
    });

    if (this.variationCount > -1 && this.xml[0].children[this.testIdx].children[this.testCount].children[this.variationCount]) {
      this.xml[0].children[this.testIdx].children[this.testCount].children[this.variationCount].attrs.end = timestamp;
    }

    this.lastAssertion = data;
    this.variationCount++;
    return this;
  },

  /**
   * Generates XML skeleton for a testcase
   *
   * @method testStarted
   * @param {object} data Event data
   * @chainable
   */

  testStarted: function (data) {
    this.variationCount = -1;
    this.xml[0].children[this.testIdx].children.push({
      name: 'testcase',
      children: [],
      attrs: {
        start: Math.round(new Date().getTime() / 1000),
        name: data.name,
        end: null,
        result: null
      }
    });

    return this;
  },

  /**
   * Finishes XML skeleton for a testcase
   *
   * @method testFinished
   * @param {object} data Event data
   * @chainable
   */

  testFinished: function (data) {
    var timestamp = Math.round(new Date().getTime() / 1000);
    this.xml[0].children[this.testIdx].children[this.testCount].attrs.end = timestamp;
    this.xml[0].children[this.testIdx].children[this.testCount].attrs.result = data.status ? 'Passed' : 'Failed';

    if (this.variationCount > -1) {
      this.xml[0].children[this.testIdx].children[this.testCount].children[this.variationCount].attrs.end = timestamp;
    }

    this.testCount++;
    this.variationCount = -1;
    this.xml[0].children[this.testIdx].attrs.totalTests = this.testCount;
    return this;
  },

  /**
   * Finishes XML and writes file to the file system
   *
   * @method runnerFinished
   * @param {object} data Event data
   * @chainable
   */

  runnerFinished: function (data) {
    this.data.elapsedTime = data.elapsedTime;
    this.data.status = data.status;
    this.data.assertions = data.assertions;
    this.data.assertionsFailed = data.assertionsFailed;
    this.data.assertionsPassed = data.assertionsPassed;

    var contents = jsonxml(this.xml, {escape: true, removeIllegalNameCharacters: true, prettyPrint: true, xmlHeader: 'version="1.0" encoding="UTF-8"'});

    if (path.extname(this.dest) !== '.xml') {
      this.dest = this.dest + '/dalek.xml';
    }

    this.events.emit('report:written', {type: 'junit', dest: this.dest});
    this._recursiveMakeDirSync(path.dirname(this.dest.replace(path.basename(this.dest, ''))));
    fs.writeFileSync(this.dest, contents, 'utf8');
  },

  /**
   * Helper method to generate deeper nested directory structures
   *
   * @method _recursiveMakeDirSync
   * @param {string} path Path to create
   */

  _recursiveMakeDirSync: function (path) {
    var pathSep = require('path').sep;
    var dirs = path.split(pathSep);
    var root = '';

    while (dirs.length > 0) {
      var dir = dirs.shift();
      if (dir === '') {
        root = pathSep;
      }
      if (!fs.existsSync(root + dir)) {
        fs.mkdirSync(root + dir);
      }
      root += dir + pathSep;
    }
  }
};
<|endoftext|>


<commit_before>'use strict'

const ndjson = require('ndjson').parse
const {PassThrough} = require('stream')

const request = require('./lib/request')()

const isProd = process.env.NODE_ENV === 'production'
const isObj = o => 'object' === typeof o && !Array.isArray(o)

const stations = (query = {}) => {
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/stations', query, false)
}

const nearby = (query = {}) => {
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/stations/nearby', query)
}

const station = (id, query = {}) => {
	if (!isProd && 'string' !== typeof id || !id) {
		throw new Error('id must be a non-empty string.')
	}
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/stations/' + id, query)
}

const allStations = (query = {}) => {
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/stations/all', query)
}

const departures = (id, query = {}) => {
	if (!isProd && 'string' !== typeof id || !id) {
		throw new Error('id must be a non-empty string.')
	}

	if (!isProd && !isObj(query)) throw new Error('query must be an object.')
	if ('when' in query) {
		query.when = Math.round(query.when / 1000)
		if (!isProd && Number.isNaN(query.when)) {
			throw new Error('query.when must be a number of a Date.')
		}
	}
	if (
		!isProd && ('nextStation' in query) &&
		('string' !== typeof query.nextStation || !query.nextStation)
	) {
		throw new Error('query.nextStation must be a non-empty string.')
	}

	return request(`/stations/${id}/departures`, query)
	.then((deps) => {
		for (let dep of deps) {
			if (dep.when) dep.when = new Date(dep.when)
		}
		return deps
	})
}

const lines = (query = {}) => {
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/lines', query, ndjson())
}

const line = (id, query = {}) => {
	if (!isProd && 'string' !== typeof id || !id) {
		throw new Error('id must be a non-empty string.')
	}
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/lines/' + id, query)
}

const location = (loc, key, query) => {
	if ('string' === typeof loc) {
		if (!isProd && !loc) throw new Error(key + ' must not be empty.')
		query[key] = loc
		return query
	}
	if (isObj(loc)) {
		const isStation = loc.type === 'station'
		const isPoi = loc.type === 'location' && ('id' in loc)
		const isAddress = loc.type === 'location' && ('address' in loc)
		if (
			!isProd && (isStation || isPoi) &&
			('string' !== typeof loc.id || !loc.id)
		) throw new Error(key + '.id must be a non-empty string.')

		if (isStation) {
			query[key] = loc.id
			return query
		}
		if (isPoi || isAddress) {
			query[key + '.longitude'] = loc.longitude
			query[key + '.latitude'] = loc.latitude
			if (isPoi) {
				query[key + '.id'] = loc.id
				query[key + '.name'] = loc.name
			} else query[key + '.address'] = loc.address
			return query
		}
	}
	throw new Error('valid station, address or poi required.')
}

const journeys = (from, to, query = {}) => {
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')
	query = Object.assign({}, query)
	Object.assign(query, location(from, 'from', query), location(to, 'to', query))
	if ('when' in query) {
		query.when = Math.round(query.when / 1000)
		if (!isProd && Number.isNaN(query.when)) {
			throw new Error('query.when must be a number of a Date.')
		}
	}

	return request('/journeys', query)
	.then((journeys) => {
		for (let j of journeys) {
			if (j.departure) j.departure = new Date(j.departure)
			if (j.arrival) j.arrival = new Date(j.arrival)

			for (let leg of j.legs) {
				if (leg.departure) leg.departure = new Date(leg.departure)
				if (leg.arrival) leg.arrival = new Date(leg.arrival)

				if (Array.isArray(leg.passed)) {
					for (let s of leg.passed) {
						if (s.arrival) s.arrival = new Date(s.arrival)
						if (s.departure) s.departure = new Date(s.departure)
					}
				}
			}
		}
		return journeys
	})
}

const journeyLeg = (ref, query = {}) => {
	if (!isProd && 'string' !== typeof ref || !ref) {
		throw new Error('ref must be a non-empty string.')
	}
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/journeys/legs/' + ref, query)
}

const locations = (query, params = {}) => {
	if (!isProd && 'string' !== typeof query || !query) {
		throw new Error('query must be a non-empty string.')
	}
	if (!isProd && !isObj(params)) throw new Error('params must be an object.')

	params = Object.assign({}, params)
	params.query = query
	return request('/locations', params)
}

const map = (type, query = {}) => {
	if (!isProd && 'string' !== typeof type || !type) {
		throw new Error('type must be a non-empty string.')
	}
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/maps/' + type, query, new PassThrough())
}

const radar = (north, west, south, east, query = {}) => {
	if ('number' !== typeof north) throw new Error('north must be a number')
	if ('number' !== typeof west) throw new Error('west must be a number')
	if ('number' !== typeof south) throw new Error('south must be a number')
	if ('number' !== typeof east) throw new Error('east must be a number')
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	query = Object.assign({}, query, {north, west, south, east})
	return request('/radar', query)
}

module.exports = {
	stations, nearby, allStations,
	station, departures,
	lines, line,
	journeys,
	locations,
	map,
	radar
}
<commit_msg>Changes library to accept params<commit_after>'use strict'

const ndjson = require('ndjson').parse
const {PassThrough} = require('stream')

let request;

const isProd = process.env.NODE_ENV === 'production'
const isObj = o => 'object' === typeof o && !Array.isArray(o)

const configure = (config) => {
	request = require('./lib/request')(config);
	return {
		stations,
		nearby,
		allStations,
		station,
		departures,
		lines,
		line,
		journeys,
		locations,
		map,
		radar
	}
}

const stations = (query = {}) => {
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/stations', query, false)
}

const nearby = (query = {}) => {
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/stations/nearby', query)
}

const station = (id, query = {}) => {
	if (!isProd && 'string' !== typeof id || !id) {
		throw new Error('id must be a non-empty string.')
	}
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/stations/' + id, query)
}

const allStations = (query = {}) => {
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/stations/all', query)
}

const departures = (id, query = {}) => {
	if (!isProd && 'string' !== typeof id || !id) {
		throw new Error('id must be a non-empty string.')
	}

	if (!isProd && !isObj(query)) throw new Error('query must be an object.')
	if ('when' in query) {
		query.when = Math.round(query.when / 1000)
		if (!isProd && Number.isNaN(query.when)) {
			throw new Error('query.when must be a number of a Date.')
		}
	}
	if (
		!isProd && ('nextStation' in query) &&
		('string' !== typeof query.nextStation || !query.nextStation)
	) {
		throw new Error('query.nextStation must be a non-empty string.')
	}

	return request(`/stations/${id}/departures`, query)
	.then((deps) => {
		for (let dep of deps) {
			if (dep.when) dep.when = new Date(dep.when)
		}
		return deps
	})
}

const lines = (query = {}) => {
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/lines', query, ndjson())
}

const line = (id, query = {}) => {
	if (!isProd && 'string' !== typeof id || !id) {
		throw new Error('id must be a non-empty string.')
	}
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/lines/' + id, query)
}

const location = (loc, key, query) => {
	if ('string' === typeof loc) {
		if (!isProd && !loc) throw new Error(key + ' must not be empty.')
		query[key] = loc
		return query
	}
	if (isObj(loc)) {
		const isStation = loc.type === 'station'
		const isPoi = loc.type === 'location' && ('id' in loc)
		const isAddress = loc.type === 'location' && ('address' in loc)
		if (
			!isProd && (isStation || isPoi) &&
			('string' !== typeof loc.id || !loc.id)
		) throw new Error(key + '.id must be a non-empty string.')

		if (isStation) {
			query[key] = loc.id
			return query
		}
		if (isPoi || isAddress) {
			query[key + '.longitude'] = loc.longitude
			query[key + '.latitude'] = loc.latitude
			if (isPoi) {
				query[key + '.id'] = loc.id
				query[key + '.name'] = loc.name
			} else query[key + '.address'] = loc.address
			return query
		}
	}
	throw new Error('valid station, address or poi required.')
}

const journeys = (from, to, query = {}) => {
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')
	query = Object.assign({}, query)
	Object.assign(query, location(from, 'from', query), location(to, 'to', query))
	if ('when' in query) {
		query.when = Math.round(query.when / 1000)
		if (!isProd && Number.isNaN(query.when)) {
			throw new Error('query.when must be a number of a Date.')
		}
	}

	return request('/journeys', query)
	.then((journeys) => {
		for (let j of journeys) {
			if (j.departure) j.departure = new Date(j.departure)
			if (j.arrival) j.arrival = new Date(j.arrival)

			for (let leg of j.legs) {
				if (leg.departure) leg.departure = new Date(leg.departure)
				if (leg.arrival) leg.arrival = new Date(leg.arrival)

				if (Array.isArray(leg.passed)) {
					for (let s of leg.passed) {
						if (s.arrival) s.arrival = new Date(s.arrival)
						if (s.departure) s.departure = new Date(s.departure)
					}
				}
			}
		}
		return journeys
	})
}

const journeyLeg = (ref, query = {}) => {
	if (!isProd && 'string' !== typeof ref || !ref) {
		throw new Error('ref must be a non-empty string.')
	}
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/journeys/legs/' + ref, query)
}

const locations = (query, params = {}) => {
	if (!isProd && 'string' !== typeof query || !query) {
		throw new Error('query must be a non-empty string.')
	}
	if (!isProd && !isObj(params)) throw new Error('params must be an object.')

	params = Object.assign({}, params)
	params.query = query
	return request('/locations', params)
}

const map = (type, query = {}) => {
	if (!isProd && 'string' !== typeof type || !type) {
		throw new Error('type must be a non-empty string.')
	}
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	return request('/maps/' + type, query, new PassThrough())
}

const radar = (north, west, south, east, query = {}) => {
	if ('number' !== typeof north) throw new Error('north must be a number')
	if ('number' !== typeof west) throw new Error('west must be a number')
	if ('number' !== typeof south) throw new Error('south must be a number')
	if ('number' !== typeof east) throw new Error('east must be a number')
	if (!isProd && !isObj(query)) throw new Error('query must be an object.')

	query = Object.assign({}, query, {north, west, south, east})
	return request('/radar', query)
}

module.exports = configure
<|endoftext|>


<commit_before>/* global require, process, module  */
'use strict';

var fs = require('fs'),
	path = require('path');

function DotConfig(filepath) {

	var options = null;

	function readFile(filepath) {

		var rawData = '{}';

		try {
			rawData = fs.readFileSync(filepath);
		} catch(err) {
			if (err.code === 'ENOENT') {
				fs.writeFileSync(filepath, rawData);
			} else {
				throw err;
			}
		}

		try {
			var options = JSON.parse(rawData);
		} catch(err) {
			err.filepath = filepath;
			throw err;
		}

		return options;

	}

	this.set = function(key, value) {
		options = options || readFile(filepath);
		options[key] = value;
		fs.writeFileSync(filepath, JSON.stringify(options, null, 2));
		return this;
	};

	this.get = function(key) {
		options = options || readFile(filepath);
		if (key in options) {
			return options[key];
		} else {
			return null;
		}
	};

}

module.exports = module.exports || {};

module.exports.file = function(filename) {

	if (!filename || typeof filename !== typeof '') {
		throw new Error('Invalid filename');
	}

	var homedir = process.env.HOME || process.env.USERPROFILE,
		filepath = path.join(homedir, filename);

	return new DotConfig(filepath);
};<commit_msg>Add .unset(key) method<commit_after>/* global require, process, module  */
'use strict';

var fs = require('fs'),
	path = require('path');

function DotConfig(filepath) {

	var options = null;

	function readFile(filepath) {

		var rawData = '{}';

		try {
			rawData = fs.readFileSync(filepath);
		} catch(err) {
			if (err.code === 'ENOENT') {
				fs.writeFileSync(filepath, rawData);
			} else {
				throw err;
			}
		}

		try {
			var options = JSON.parse(rawData);
		} catch(err) {
			err.filepath = filepath;
			throw err;
		}

		return options;

	}

	this.set = function(key, value) {
		options = options || readFile(filepath);
		options[key] = value;
		fs.writeFileSync(filepath, JSON.stringify(options, null, 2));
		return this;
	};

	this.get = function(key) {
		options = options || readFile(filepath);
		if (key in options) {
			return options[key];
		} else {
			return null;
		}
	};

	this.unset = function(key) {
		options = options || readFile(filepath);
		return delete options[key];
	};

}

module.exports = module.exports || {};

module.exports.file = function(filename) {

	if (!filename || typeof filename !== typeof '') {
		throw new Error('Invalid filename');
	}

	var homedir = process.env.HOME || process.env.USERPROFILE,
		filepath = path.join(homedir, filename);

	return new DotConfig(filepath);
};<|endoftext|>


<commit_before>var cons = require('consolidate');
var utils = require('loader-utils');
var path = require('path');
var fs = require('fs');

module.exports = function(content) {
  this.cacheable && this.cacheable();

  var callback = this.async();
  var opt = utils.getOptions(this);

  function exportContent(content) {
    if (opt.raw) {
      callback(null, content);
    } else {
      callback(null, "module.exports = " + JSON.stringify(content));
    }
  }

  // with no engine given, use the file extension as engine
  if(!opt.engine) {
    opt.engine = path.extname(this.request).substr(1).toLowerCase();
  }

  if(!cons[opt.engine]) {
    throw new Error("Engine '"+ opt.engine +"' isn't available in Consolidate.js");
  }

  // for relative includes
  opt.filename = this.resourcePath;
  opt.dirname = path.dirname(this.resourcePath);

  if(opt.file && !/\.json$/.test(opt.file)) {
    opt.file = path.join(opt.dirname, opt.file + '.json');
    if(!fs.existsSync(opt.file)) {
      throw new Error("Data file '"+ opt.file +"' does not exist");
    }

    opt = Object.assign(require(opt.file), opt);
    delete opt.file;
  }

  cons[opt.engine].render(content, opt, function(err, html) {
    if(err) {
      throw err;
    }
    exportContent(html);
  });
};
<commit_msg>simplify fix for double processing<commit_after>var cons = require('consolidate');
var utils = require('loader-utils');
var path = require('path');
var fs = require('fs');

module.exports = function(content) {
  this.cacheable && this.cacheable();

  var callback = this.async();
  var opt = utils.getOptions(this);

  function exportContent(content) {
    if (opt.raw) {
      callback(null, content);
    } else {
      callback(null, "module.exports = " + JSON.stringify(content));
    }
  }

  // with no engine given, use the file extension as engine
  if(!opt.engine) {
    opt.engine = path.extname(this.request).substr(1).toLowerCase();
  }

  if(!cons[opt.engine]) {
    throw new Error("Engine '"+ opt.engine +"' isn't available in Consolidate.js");
  }

  // for relative includes
  opt.filename = this.resourcePath;
  opt.dirname = path.dirname(this.resourcePath);

  if(opt.file) {
    const file = path.join(opt.dirname, opt.file + '.json');
    if(!fs.existsSync(file)) {
      throw new Error("Data file '"+ file +"' does not exist");
    }

    opt = Object.assign(require(file), opt);    // ensure that opt takes precedence
    delete opt.file;
  }

  cons[opt.engine].render(content, opt, function(err, html) {
    if(err) {
      throw err;
    }
    exportContent(html);
  });
};
<|endoftext|>


<commit_before>express=require('express'),app=express();
http=require('http'),server=http.createServer(app);
socketio=require('socket.io'),io=socketio(server);
morgan=require('morgan');

app.use(morgan('combined'));
app.use(express.static('public'));

/* websocket handlers */

io.on('connection',function(socket)
{
	socket.on('chatmsg',function(msg)
	{
		io.emit('chatmsg',msg);
	});
});

server.listen(3000);

<commit_msg>only listen on localhost<commit_after>express=require('express'),app=express();
http=require('http'),server=http.createServer(app);
socketio=require('socket.io'),io=socketio(server);
morgan=require('morgan');

app.use(morgan('combined'));
app.use(express.static('public'));

/* websocket handlers */

io.on('connection',function(socket)
{
	socket.on('chatmsg',function(msg)
	{
		io.emit('chatmsg',msg);
	});
});

server.listen(3000,'localhost');

<|endoftext|>


<commit_before>#!/usr/bin/env node
"use strict";

const bleno = require("bleno");
const STATE = {
	STATE_CHANGE: "stateChange",
	AD_START: "advertisingStart",
	POWERED_ON: "poweredOn"
};
const PRIMARY_SERVICE_UUID = "fffffffffffffffffffffffffffffff0";

bleno.on(STATE.AD_START, adStart);
bleno.on(STATE.STATE_CHANGE, stateChanged);

function adStart (error) {
	const descriptorConfig = {
		uuid: "2901",
		value: "some descriptor val"
	};
	const descriptor = new bleno.Descriptor(descriptorConfig);
	const characteristicConfig = {
		uuid: "fff1",
		properties: [ "read", "write", "writeWithoutResponse" ],
		//value: "ff", // optional {Buffer}
		descriptors: [ descriptor ]
	};
	const characteristic = new bleno.Characteristic(characteristicConfig);
	const primaryServiceConfig = {
		uuid: PRIMARY_SERVICE_UUID,
		characteristics: [ characteristic ]
	};
	const services = new bleno.PrimaryService(primaryServiceConfig);
	bleno.setServices(services);
}

function stateChanged (state) {
	console.log(`BLE state changed ${state}`);
	if (state === STATE.POWERED_ON) {
		bleno.startAdvertising("myThing", PRIMARY_SERVICE_UUID);
	} else {
		bleno.stopAdvertising();
	}
}
<commit_msg>update advertised name and logs<commit_after>#!/usr/bin/env node
"use strict";

const bleno = require("bleno");
const STATE = {
	STATE_CHANGE: "stateChange",
	AD_START: "advertisingStart",
	POWERED_ON: "poweredOn"
};
const PRIMARY_SERVICE_UUID = "fffffffffffffffffffffffffffffff0";
const ADVERTISED_NAME = "garbage body rattle balls";

bleno.on(STATE.AD_START, adStart);
bleno.on(STATE.STATE_CHANGE, stateChanged);

function adStart (error) {
	const descriptorConfig = {
		uuid: "2901",
		value: "some descriptor val"
	};
	const descriptor = new bleno.Descriptor(descriptorConfig);
	const characteristicConfig = {
		uuid: "fff1",
		properties: [ "read", "write", "writeWithoutResponse" ],
		//value: "ff", // optional {Buffer}
		descriptors: [ descriptor ]
	};
	const characteristic = new bleno.Characteristic(characteristicConfig);
	const primaryServiceConfig = {
		uuid: PRIMARY_SERVICE_UUID,
		characteristics: [ characteristic ]
	};
	const services = new bleno.PrimaryService(primaryServiceConfig);
	bleno.setServices(services);
}

function stateChanged (state) {
	console.log(`BLE state changed ${state}`);
	if (state === STATE.POWERED_ON) {
		console.info(`Start advertising ${ADVERTISED_NAME}`);
		bleno.startAdvertising(ADVERTISED_NAME, PRIMARY_SERVICE_UUID);
	} else {
		console.info(`Stop advertising. State is not ${STATE.POWERED_ON}`);
		bleno.stopAdvertising();
	}
}
<|endoftext|>


<commit_before>const dgram = require("dgram");
const ip = require("ip");

const PORT = 1337;
//TODO improve argument parsing
const INTERFACES = (process.argv.slice(2).length > 0) ? process.argv.slice(2) : ["wlp3s0"];
const ALL_NODES_ADDRESS = "ff02::1"; // this has link scope not node scope

// map of sockets for each interface
// TODO maybe make a difference between listening and sending sockets
let servers = new Map();

// key aka destination is combinationn of address (ip) and port
// TODO does the port help in anyway?
let routingTable = new Map();

//TODO remove map?
let localAddresses = INTERFACES.map(function(interface){
  return ip.address(interface, "ipv6");
});

const createServer = function createServer(interface, includeInRoutingTable) {

  let server = dgram.createSocket("udp6");

  server.on("listening", function(){
    let address = server.address();
    console.info(`Listening on ${address.address}:${address.port}`);
    if(includeInRoutingTable) {
      //routingTable.set(`${address.address}:${address.port}`, { 'cost': 0 });
      let destination = {
        'cost': 0,
        'link': "lo",
        'time': null
      };
      routingTable.set(address.address, destination);
    }
  });
  server.on("message", function(message, remote){
    //do not handle messages from us
    //TODO maybe check port too?
    if(localAddresses.includes(remote.address)) {
      return;
    }
    let vector = new Map(JSON.parse(message));
    console.info(`Message arrived from ${remote.address}:${remote.port}`);
    console.info(vector);

    // TODO get link/interface and cost
    let linkCost = 1;

    vector.forEach(function(cost, destinationAddress){
      if(!routingTable.has(destinationAddress)){
        // new route
        let destination = {
          'cost': cost + linkCost,
          'link': interface,
          'time': null
        };
        routingTable.set(destinationAddress, destination);
      } else {
        let destination = routingTable.get(destinationAddress);
        //TODO understand second part of condition and why it seems broken
        //console.info(interface == destination.link);
        if((cost + linkCost) < destination.cost || interface == destination.link) {
        //if((cost + linkCost) < destination.cost) {
          destination.cost = cost + linkCost;
          destination.link = interface;
          destination.time = null;
        }
      }
    });
  });
  server.on("error", function(error) {
    console.error(error);
  });

  return server;
};

const sendVector = function sendVector(vector, interface){
  console.info(`Broadcasting over ${interface.address().address}:`);
  console.info(vector);
  interface.send(JSON.stringify(vector), PORT, ALL_NODES_ADDRESS); //TODO maybe add a call back
};

const sendVectors = function sendVectors(){

  let vector = [];

  console.info("Routing table:");
  console.info(routingTable);

  routingTable.forEach(function(destination, destinationAddress){
    vector.push([destinationAddress, destination.cost]);
  });

  INTERFACES.forEach(function(interface){
    sendVector(vector, servers.get(interface));
  });
};

setInterval(sendVectors, 1000);

// listen on multiple interfaces
INTERFACES.forEach(function(interface){
  //TODO there can be multiple addresses, which one should we use?
  let address = ip.address(interface, "ipv6");
  //TODO this is wrong!?!! address = ALL_NODES_ADDRESS;
  let server = createServer(interface, true);
  console.info(`Binding server to ${address} on ${interface}`);
  //TODO check if we need the interface here
  server.bind(PORT, `${address}%${interface}`);
  servers.set(interface, server);

  //TODO THINK!!!
  //bind to ALL_NODES_ADDRESS of interface
  server = createServer(interface);
  server.bind(PORT, `${ALL_NODES_ADDRESS}%${interface}`);
  //TODO add server/socket to set of sockets
});
<commit_msg>remove dead code<commit_after>const dgram = require("dgram");
const ip = require("ip");

const PORT = 1337;
//TODO improve argument parsing
const INTERFACES = (process.argv.slice(2).length > 0) ? process.argv.slice(2) : ["wlp3s0"];
const ALL_NODES_ADDRESS = "ff02::1"; // this has link scope not node scope

// map of sockets for each interface
// TODO maybe make a difference between listening and sending sockets
let servers = new Map();

// key aka destination is combinationn of address (ip) and port
// TODO does the port help in anyway?
let routingTable = new Map();

//TODO remove map?
let localAddresses = INTERFACES.map(function(interface){
  return ip.address(interface, "ipv6");
});

const createServer = function createServer(interface, includeInRoutingTable) {

  let server = dgram.createSocket("udp6");

  server.on("listening", function(){
    let address = server.address();
    console.info(`Listening on ${address.address}:${address.port}`);
    if(includeInRoutingTable) {
      //routingTable.set(`${address.address}:${address.port}`, { 'cost': 0 });
      let destination = {
        'cost': 0,
        'link': "lo",
        'time': null
      };
      routingTable.set(address.address, destination);
    }
  });
  server.on("message", function(message, remote){
    //do not handle messages from us
    //TODO maybe check port too?
    if(localAddresses.includes(remote.address)) {
      return;
    }
    let vector = new Map(JSON.parse(message));
    console.info(`Message arrived from ${remote.address}:${remote.port}`);
    console.info(vector);

    // TODO get link/interface and cost
    let linkCost = 1;

    vector.forEach(function(cost, destinationAddress){
      if(!routingTable.has(destinationAddress)){
        // new route
        let destination = {
          'cost': cost + linkCost,
          'link': interface,
          'time': null
        };
        routingTable.set(destinationAddress, destination);
      } else {
        let destination = routingTable.get(destinationAddress);
        if((cost + linkCost) < destination.cost || interface == destination.link) {
          destination.cost = cost + linkCost;
          destination.link = interface;
          destination.time = null;
        }
      }
    });
  });
  server.on("error", function(error) {
    console.error(error);
  });

  return server;
};

const sendVector = function sendVector(vector, interface){
  console.info(`Broadcasting over ${interface.address().address}:`);
  console.info(vector);
  interface.send(JSON.stringify(vector), PORT, ALL_NODES_ADDRESS); //TODO maybe add a call back
};

const sendVectors = function sendVectors(){

  let vector = [];

  console.info("Routing table:");
  console.info(routingTable);

  routingTable.forEach(function(destination, destinationAddress){
    vector.push([destinationAddress, destination.cost]);
  });

  INTERFACES.forEach(function(interface){
    sendVector(vector, servers.get(interface));
  });
};

setInterval(sendVectors, 1000);

// listen on multiple interfaces
INTERFACES.forEach(function(interface){
  //TODO there can be multiple addresses, which one should we use?
  let address = ip.address(interface, "ipv6");
  //TODO this is wrong!?!! address = ALL_NODES_ADDRESS;
  let server = createServer(interface, true);
  console.info(`Binding server to ${address} on ${interface}`);
  //TODO check if we need the interface here
  server.bind(PORT, `${address}%${interface}`);
  servers.set(interface, server);

  //TODO THINK!!!
  //bind to ALL_NODES_ADDRESS of interface
  server = createServer(interface);
  server.bind(PORT, `${ALL_NODES_ADDRESS}%${interface}`);
  //TODO add server/socket to set of sockets
});
<|endoftext|>


<commit_before>#!/usr/bin/env node
/*!
 * -----------
 * ASPDB®-Data
 * -----------
 * Data of ASPDB — A manually curated dedicated acid shock proteins database.
 * ___________________________________________________________________________
 *
 * CCPE ——————— Centre for Computational Protein Engineering.
 *
 * Architecture and Code Handcrafted by Prabhat Kumar.
 * Architectuur en Code handgemaakt door Prabhat Kumar.
 * @author    : Prabhat Kumar [http://prabhatkumar.org/].
 * @copyright : Prabhat Kumar [http://prabhatkumar.org/].
 * @copyright : Sequømics Research [http://research.sequomics.com/].
 * @copyright : Sequømics Corporation [http://sequomics.com/].
 * ___________________________________________________________________________
 *
 * @date      : 10-Dec-2016
 * @license   : Apache, version 2.0
 * @purpose   : To create Data System(s) with Node.js® and NPM.
 * @require   : Node.js®
 * @require   : NPM
 * @build     : SEED™ — Skövde
 *              └────── A Sequømics Product — http://seed.sequomics.com/.
 * ___________________________________________________________________________
 *
 * --/The Heart of Data System/-- of "ASPDB®-Data".
 * ___________________________________________________________________________
 */

// global __dirname: true
// global require: true
<commit_msg>Initial commit<commit_after>#!/usr/bin/env node
/*!
 * -----------
 * ASPDB®-Data
 * -----------
 * Data of ASPDB — A manually curated dedicated acid shock proteins database.
 * ___________________________________________________________________________
 *
 * CCPE ——————— Centre for Computational Protein Engineering.
 *
 * Architecture and Code Handcrafted by Prabhat Kumar.
 * Architectuur en Code handgemaakt door Prabhat Kumar.
 * @author    : Prabhat Kumar [http://prabhatkumar.org/].
 * @copyright : Prabhat Kumar [http://prabhatkumar.org/].
 * @copyright : Sequømics Research [http://research.sequomics.com/].
 * @copyright : Sequømics Corporation [http://sequomics.com/].
 * ___________________________________________________________________________
 *
 * @date      : 10-Dec-2016
 * @license   : Apache, version 2.0
 * @purpose   : To create Data System(s) with Node.js® and NPM.
 * @require   : Node.js®
 * @require   : NPM
 * @build     : SEED™ — Skövde
 *              └────── A Sequømics Product — http://seed.sequomics.com/.
 * ___________________________________________________________________________
 *
 * --/The Heart of Data System/-- of "ASPDB®-Data".
 * ___________________________________________________________________________
 */

// global __dirname: true
// global require: true

// # Usage: $ node -v
// # Usage: $ npm -v
<|endoftext|>