<commit_before>/*
 * @fileOverview
 * @author: Mike Grabowski (@grabbou)
 */

'use strict';

var CoreObject = require('core-object');
var chalk = require('chalk');
var Promise = require('bluebird');
var lstatAsync = Promise.promisify(require('fs').lstat);
var _ = require('lodash');

module.exports = Task;

function Task() {
  CoreObject.apply(this, arguments);
}

Task.__proto__ = CoreObject;
Task.prototype.constructor = Task;

// @TODO Move this config elsewhere and add .esterc
Task.prototype.getConfig = function() {
  return {
    rootFolder: 'src/client',
    serverFolder: 'src/server',
    libFolder: 'src/lib'
  };
};

/**
 * Private method for running tasks
 * @private
 */
Task.prototype._run = function() {
  throw new Error('Task needs to have run() defined.');
};

/**
 * Checks if the current directory is Este.js project
 * @private
 * @returns {Promise}
 */
Task.prototype._ensureEsteProject = function() {
  var options = this.getConfig();
  var foldersToCheck = [options.rootFolder, options.serverFolder];

  return Promise
    .each(foldersToCheck, function(folder) {
      return lstatAsync(folder);
    })
    .catch(function(err) {
      throw new Error('Not an Este.js project');
    });
};

/**
 * Starts a task and returns a promise
 * Outputs additional messages when the stuff is done
 * @returns {Promise}
 */
Task.prototype.start = function() {
  var args = arguments;
  var beforeRun = this.skipEsteCheck ? Promise.resolve() : this._ensureEsteProject();

  return beforeRun
    .then(function runTask() {
      return this._run.apply(this, args);
    }.bind(this))
    .then(function() {
      console.log(chalk.blue('Task finished successfully'));
    })
    .catch(function(err) {
      console.log(chalk.bold.red(err.message || err));
    });
};<commit_msg>err not required, lets switch to proper branch<commit_after>/*
 * @fileOverview
 * @author: Mike Grabowski (@grabbou)
 */

'use strict';

var CoreObject = require('core-object');
var chalk = require('chalk');
var Promise = require('bluebird');
var lstatAsync = Promise.promisify(require('fs').lstat);
var _ = require('lodash');

module.exports = Task;

function Task() {
  CoreObject.apply(this, arguments);
}

Task.__proto__ = CoreObject;
Task.prototype.constructor = Task;

// @TODO Move this config elsewhere and add .esterc
Task.prototype.getConfig = function() {
  return {
    rootFolder: 'src/client',
    serverFolder: 'src/server',
    libFolder: 'src/lib'
  };
};

/**
 * Private method for running tasks
 * @private
 */
Task.prototype._run = function() {
  throw new Error('Task needs to have run() defined.');
};

/**
 * Checks if the current directory is Este.js project
 * @private
 * @returns {Promise}
 */
Task.prototype._ensureEsteProject = function() {
  var options = this.getConfig();
  var foldersToCheck = [options.rootFolder, options.serverFolder];

  return Promise
    .each(foldersToCheck, function(folder) {
      return lstatAsync(folder);
    })
    .catch(function() {
      throw new Error('Not an Este.js project');
    });
};

/**
 * Starts a task and returns a promise
 * Outputs additional messages when the stuff is done
 * @returns {Promise}
 */
Task.prototype.start = function() {
  var args = arguments;
  var beforeRun = this.skipEsteCheck ? Promise.resolve() : this._ensureEsteProject();

  return beforeRun
    .then(function runTask() {
      return this._run.apply(this, args);
    }.bind(this))
    .then(function() {
      console.log(chalk.blue('Task finished successfully'));
    })
    .catch(function(err) {
      console.log(chalk.bold.red(err.message || err));
    });
};<|endoftext|>


<commit_before>'use babel'

jQuery = require('jquery')

export default class MoieClient {
  constructor(servername, port) {
    port = (port === undefined) ? 9001 : port
    servername = (servername === undefined) ? 'localhost' : servername
    this.baseAddress = "http://" + servername + ":" + port + "/" + "moie/"
    this.mimeType = 'application/json'
  }

  connect(projectInfo, onSuccess, onError) {
    const self = this
    const json = (typeof projectInfo === 'string') ? projectInfo : JSON.stringify(projectInfo)
    jQuery.ajax({
      method: 'POST',
      url: this.baseAddress + 'connect',
      contentType: this.mimeType,
      data: json,
      success: (data, status, xhr) => {
        console.log(data)
        self.projectId = data
        onSuccess()
      },
      error: (xhr, status, error) => {
        console.log(error)
        onError(status,error)
      }
    })
  }

  disconnect(onSuccess) {
    jQuery.ajax({
      method: 'POST',
      url: this.baseAddress + 'disconnect?project-id='+this.projectId,
      success: onSuccess,
      error: (xhr, status, error) => {
        console.log("error disconnect: "+error)
      }
    })
  }

  compile(onSuccess, onError) {
    jQuery.ajax({
      method: 'POST',
      dataType: this.mimeType,
      url: this.baseAddress + 'compile?project-id='+this.projectId,
      success: onSuccess,
      error: onError
    })
  }
}
<commit_msg>Only disconnect if actually connected<commit_after>'use babel'

jQuery = require('jquery')

export default class MoieClient {
  constructor(servername, port) {
    port = (port === undefined) ? 9001 : port
    servername = (servername === undefined) ? 'localhost' : servername
    this.baseAddress = "http://" + servername + ":" + port + "/" + "moie/"
    this.mimeType = 'application/json'
    this.connected = false
  }

  connect(projectInfo, onSuccess, onError) {
    const self = this
    const json = (typeof projectInfo === 'string') ? projectInfo : JSON.stringify(projectInfo)
    jQuery.ajax({
      method: 'POST',
      url: this.baseAddress + 'connect',
      contentType: this.mimeType,
      data: json,
      success: (data, status, xhr) => {
        console.log(data)
        self.projectId = data
        self.connected = true
        onSuccess()
      },
      error: (xhr, status, error) => {
        console.log(error)
        onError(status,error)
      }
    })
  }

  disconnect(onSuccess) {
    if(this.connected) {
      jQuery.ajax({
        method: 'POST',
        url: this.baseAddress + 'disconnect?project-id='+this.projectId,
        success: onSuccess,
        error: (xhr, status, error) => {
          console.log("error disconnect: "+error)
        }
      })
    } else onSuccess()
  }

  compile(onSuccess, onError) {
    jQuery.ajax({
      method: 'POST',
      dataType: this.mimeType,
      url: this.baseAddress + 'compile?project-id='+this.projectId,
      success: onSuccess,
      error: onError
    })
  }
}
<|endoftext|>


<commit_before>'use strict';
var Promise = require('es6-promise').Promise;
var mv = require('mv');

module.exports = function (value) {
  return new Promise(function (resolve, reject) {
    if (!value) {
      reject(new TypeError('requires value[source], value[dest]'));
      return;
    }
    if ([value['source'], value['dest']].some(function (key) {
        return key === null || key === '' || key === '.' || key === '/';
      })) {
      reject(new ArgumentError('input is not allowed'));
      return;
    }

    mv(value['source'], value['dest'], { mkdirp: true }, function (err) {
      if (err) {
        reject(err);
        return;
      }
      resolve();
    });
  });
};
<commit_msg>chore(errors): use common errors<commit_after>'use strict';
var Promise = require('es6-promise').Promise;
var mv = require('mv');
var errors = require('common-errors');

module.exports = function (value) {
  return new Promise(function (resolve, reject) {
    if (!value) {
      reject(new errors.TypeError('requires value[source], value[dest]'));
      return;
    }
    if ([value['source'], value['dest']].some(function (key) {
        return key === null || key === '' || key === '.' || key === '/';
      })) {
      reject(new errors.ArgumentError('input is not allowed'));
      return;
    }

    mv(value['source'], value['dest'], { mkdirp: true }, function (err) {
      if (err) {
        reject(err);
        return;
      }
      resolve();
    });
  });
};
<|endoftext|>


<commit_before>/* Copyright (c) 2010-2014 Michele Capra, MIT License */
'use strict';

var neo4j = require('neo4j');
var typeis = require('type-of-is');

var name = "neo4j-store";

module.exports = function (opts) {
    var seneca = this;
    var desc;
    var dbinst = null;
    var specifications = null;

    function createCollectionName(entity) {
        var canon = entity.canon$({object: true});
        return (canon.base ? canon.base + '_' : '') + canon.name;
    }

    function error(args, err, cb) {
        if (err) {
            seneca.log.error('entity', err, {store: name});
            return true;
        }
        return false;
    }

    function composeConnectionstring(conf) {
        var connectionString = 'http://';
        var port = ':7474';
        var password = '';
        var username = '';
        var host = 'localhost';

        if (conf.username) {
            username = conf.username;
            password = '@';
        }
        if (conf.password) {
            password= ':' + conf.password + '@';
        }
        if(conf.host){
            host = conf.host;
        }
        if(conf.port){
            port =':'+ conf.port;
        }

        connectionString += username + password + host + port;

        return connectionString;
    }

    function configure(spec, cb) {
        specifications = spec;

        var conf = 'string' === typeof(spec) ? null : spec;

        var dbopts = seneca.util.deepextend({
            native_parser: false,
            auto_reconnect: true,
            w: 1
        }, conf.options);

        var connectionstring = composeConnectionstring(conf);

        dbinst = new neo4j.GraphDatabase(connectionstring);

        //check the state of the connection after opening it
        seneca.log.debug('init', 'db open', dbopts);

        cb(null);
    }

    function generateUUID() {
        var d = new Date().getTime();
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = (d + Math.random() * 16) % 16 | 0;
            d = Math.floor(d / 16);
            return (c === 'x' ? r : (r & 0x7 | 0x8)).toString(16);
        });
    }

    function composeWhereClause(q) {
        var j = 0;
        var whereclause = '';
        var fieldName = '';
        if (!q.all$ || q.all$ !== true) {
            for (var field in q) {
                if (j === 0) {
                    whereclause += ' WHERE ';
                }
                if (field.toLowerCase() === 'id') {
                    fieldName = 'custom_id';
                }
                else {
                    fieldName = field;
                }

                if (typeis.string(q[field]) === 'String') {
                    whereclause += ' ent.' + fieldName + ' = \'' + q[field] + '\'';
                }
                else {
                    whereclause += ' ent.' + fieldName + ' = ' + q[field];
                }

                j++;

                if (j !== 0 && j < Object.keys(q).length) {
                    whereclause += " AND ";
                }

            }

        }
        return whereclause;
    }

    function deserializeObject(entp, index) {
        for (var p in entp[index].ent.data) {
            if (typeis.string(entp[index].ent.data[p]) === 'String') {
                //everytime i try do deserialize strings
                try {
                    entp[index].ent.data[p] = JSON.parse(entp[index].ent.data[p]);
                }
                catch (exc) {

                }
            }
        }
    }

    function createSenecaObjectsFromNodes(entp, qent) {
        var senecaObj = null;
        var results = [];
        for (var i = 0; i < entp.length; i++) {
            deserializeObject(entp, i);

            //create seneca object
            senecaObj = qent.make$(entp[i].ent.data);
            senecaObj.id = entp[i].ent.data.custom_id;

            results.push(senecaObj);
        }
        return results;
    }

    var store = {
        name: name,

        close: function (args, cb) {
            return cb();
        },

        save: function (args, cb) {
            var ent = args.ent;

            var query = '';
            var collectionname = createCollectionName(ent);
            var fields = ent.fields$();

            //making the query on custom_id and not on id
            if (ent.id) {
                query = [
                    'MATCH ent ',
                        'WHERE ent.custom_id= \'' + ent.custom_id + '\'',
                    'RETURN ent'
                ].join('\n');
            }
            else {
                query = [
                        'create (ent:' + collectionname ,
                        '{ custom_id:\'' + generateUUID() + '\'}',
                    ')',
                    'RETURN ent'
                ].join('\n');
            }
            dbinst.query(query, null, function (err, results) {

                if (!error(args, err, cb)) {
                    seneca.log.debug('save/insert', results, desc);

                    //only for custom id changed by user
                    if (results[0].ent.data && ent.id$ && ent.id$ !== results[0].ent.data.custom_id) {
                        results[0].ent.data.custom_id = ent.id$;
                    }

                    //serializing objects
                    fields.forEach(function (field) {
                        if (typeis.string(ent[field]) === 'Object') {
                            results[0].ent.data[field] = JSON.stringify(ent[field]);
                        }
                        else {
                            results[0].ent.data[field] = ent[field];
                        }
                    });

                    //saving node with properties
                    results[0].ent.save(function (err, result) {
                        if (!error(args, err, cb)) {
                            ent.id = result.data.custom_id;
                            cb(null, ent);
                        }
                    });

                }
                else {
                    cb(null, null);
                }
            });

        },

        load: function (args, cb) {
            var qent = args.qent;
            var q = args.q;

            qent.custom_id = qent.id;

            var query = [
                'MATCH ent',
                    'WHERE ent.custom_id = \'' + q.id + '\'',
                'RETURN ent'
            ].join('\n');

            dbinst.query(query, null, function (err, entp) {
                if (!error(args, err, cb)) {
                    var senecaObj = null;
                    if (entp[0]) {
                        senecaObj = qent.make$(entp[0].ent.data);

                        deserializeObject(entp, 0);
                        senecaObj.id = entp[0].ent.data.custom_id;
                    }

                    seneca.log.debug('load', q, senecaObj, desc);
                    cb(null, senecaObj);
                }
            });
        },

        list: function (args, cb) {
            var qent = args.qent;
            var q = args.q;

            var query = [
                    'MATCH (ent:' + createCollectionName(qent) + ') ',
                composeWhereClause(q),
                'RETURN ent'
            ].join('\n');

            dbinst.query(query, null, function (err, entp) {
                if (!error(args, err, cb)) {
                    var results = createSenecaObjectsFromNodes(entp, qent);
                    seneca.log.debug('list', q, results, desc);
                    cb(null, results);
                }
            });
        },

        remove: function (args, cb) {
            var qent = args.qent;
            var q = args.q;

            var query = [
                    'MATCH (ent:' + createCollectionName(qent) + ') ',
                composeWhereClause(q),
                'delete ent'
            ].join('\n');

            dbinst.query(query, null, function (err, entp) {
                if (!error(args, err, cb)) {
                    seneca.log.debug('remove', q, [], desc);
                    cb(null, []);
                }
            });

        },

        native: function (args, done) {
            var qent = args.qent;
            var q = args.q;

            var query = [
                    'MATCH (ent:' + createCollectionName(qent) + ') ',
                composeWhereClause(q),
                'return ent'
            ].join('\n');

            dbinst.query(query, null, function (err, entp) {
                if (!error(args, err, done)) {
                    var results = createSenecaObjectsFromNodes(entp, qent);
                    seneca.log.debug('list', q, results, desc);
                    done(null, results);
                }
            });
        }
    };

    var meta = seneca.store.init(seneca, opts, store);
    desc = meta.desc;

    seneca.add({init: store.name, tag: meta.tag}, function (args, done) {
        configure(opts, function (err) {
            if (err) {
                return seneca.die('store', err, {store: store.name, desc: desc});
            }
            return done();
        });
    });

    return {name: store.name, tag: meta.tag};
};

<commit_msg>Added native api to use the db connection<commit_after>/* Copyright (c) 2010-2014 Michele Capra, MIT License */
'use strict';

var neo4j = require('neo4j');
var typeis = require('type-of-is');

var name = "neo4j-store";

module.exports = function (opts) {
    var seneca = this;
    var desc;
    var dbinst = null;
    var specifications = null;

    function createCollectionName(entity) {
        var canon = entity.canon$({object: true});
        return (canon.base ? canon.base + '_' : '') + canon.name;
    }

    function error(args, err, cb) {
        if (err) {
            seneca.log.error('entity', err, {store: name});
            return true;
        }
        return false;
    }

    function composeConnectionstring(conf) {
        var connectionString = 'http://';
        var port = ':7474';
        var password = '';
        var username = '';
        var host = 'localhost';

        if (conf.username) {
            username = conf.username;
            password = '@';
        }
        if (conf.password) {
            password= ':' + conf.password + '@';
        }
        if(conf.host){
            host = conf.host;
        }
        if(conf.port){
            port =':'+ conf.port;
        }

        connectionString += username + password + host + port;

        return connectionString;
    }

    function configure(spec, cb) {
        specifications = spec;

        var conf = 'string' === typeof(spec) ? null : spec;

        var dbopts = seneca.util.deepextend({
            native_parser: false,
            auto_reconnect: true,
            w: 1
        }, conf.options);

        var connectionstring = composeConnectionstring(conf);

        dbinst = new neo4j.GraphDatabase(connectionstring);

        //check the state of the connection after opening it
        seneca.log.debug('init', 'db open', dbopts);

        cb(null);
    }

    function generateUUID() {
        var d = new Date().getTime();
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = (d + Math.random() * 16) % 16 | 0;
            d = Math.floor(d / 16);
            return (c === 'x' ? r : (r & 0x7 | 0x8)).toString(16);
        });
    }

    function composeWhereClause(q) {
        var j = 0;
        var whereclause = '';
        var fieldName = '';
        if (!q.all$ || q.all$ !== true) {
            for (var field in q) {
                if (j === 0) {
                    whereclause += ' WHERE ';
                }
                if (field.toLowerCase() === 'id') {
                    fieldName = 'custom_id';
                }
                else {
                    fieldName = field;
                }

                if (typeis.string(q[field]) === 'String') {
                    whereclause += ' ent.' + fieldName + ' = \'' + q[field] + '\'';
                }
                else {
                    whereclause += ' ent.' + fieldName + ' = ' + q[field];
                }

                j++;

                if (j !== 0 && j < Object.keys(q).length) {
                    whereclause += " AND ";
                }

            }

        }
        return whereclause;
    }

    function deserializeObject(entp, index) {
        for (var p in entp[index].ent.data) {
            if (typeis.string(entp[index].ent.data[p]) === 'String') {
                //everytime i try do deserialize strings
                try {
                    entp[index].ent.data[p] = JSON.parse(entp[index].ent.data[p]);
                }
                catch (exc) {

                }
            }
        }
    }

    function createSenecaObjectsFromNodes(entp, qent) {
        var senecaObj = null;
        var results = [];
        for (var i = 0; i < entp.length; i++) {
            deserializeObject(entp, i);

            //create seneca object
            senecaObj = qent.make$(entp[i].ent.data);
            senecaObj.id = entp[i].ent.data.custom_id;

            results.push(senecaObj);
        }
        return results;
    }

    var store = {
        name: name,

        /**
         * close the connection
         *
         * params
         * cb - callback
         */
        close: function (args, cb) {
            dbinst = null;
        },

        /**
         * save the data as specified in the ent attribute of the args object
         * params
         * args -
         * cb - callback
         */
        save: function (args, cb) {
            var ent = args.ent;

            var query = '';
            var collectionname = createCollectionName(ent);
            var fields = ent.fields$();

            //making the query on custom_id and not on id
            if (ent.id) {
                query = [
                    'MATCH ent ',
                        'WHERE ent.custom_id= \'' + ent.custom_id + '\'',
                    'RETURN ent'
                ].join('\n');
            }
            else {
                query = [
                        'create (ent:' + collectionname ,
                        '{ custom_id:\'' + generateUUID() + '\'}',
                    ')',
                    'RETURN ent'
                ].join('\n');
            }
            dbinst.query(query, null, function (err, results) {

                if (!error(args, err, cb)) {
                    seneca.log.debug('save/insert', results, desc);

                    //only for custom id changed by user
                    if (results[0].ent.data && ent.id$ && ent.id$ !== results[0].ent.data.custom_id) {
                        results[0].ent.data.custom_id = ent.id$;
                    }

                    //serializing objects
                    fields.forEach(function (field) {
                        if (typeis.string(ent[field]) === 'Object') {
                            results[0].ent.data[field] = JSON.stringify(ent[field]);
                        }
                        else {
                            results[0].ent.data[field] = ent[field];
                        }
                    });

                    //saving node with properties
                    results[0].ent.save(function (err, result) {
                        if (!error(args, err, cb)) {
                            ent.id = result.data.custom_id;
                            cb(null, ent);
                        }
                    });

                }
                else {
                    cb(null, null);
                }
            });

        },

        /**
         * load first matching item based on matching property values
         * in the q attribute of the args object
         * params
         * args -
         * cb - callback
         */
        load: function (args, cb) {
            var qent = args.qent;
            var q = args.q;

            qent.custom_id = qent.id;

            var query = [
                'MATCH ent',
                    'WHERE ent.custom_id = \'' + q.id + '\'',
                'RETURN ent'
            ].join('\n');

            dbinst.query(query, null, function (err, entp) {
                if (!error(args, err, cb)) {
                    var senecaObj = null;
                    if (entp[0]) {
                        senecaObj = qent.make$(entp[0].ent.data);

                        deserializeObject(entp, 0);
                        senecaObj.id = entp[0].ent.data.custom_id;
                    }

                    seneca.log.debug('load', q, senecaObj, desc);
                    cb(null, senecaObj);
                }
            });
        },

        /**
         * return a list of object based on the supplied query, if no query is supplied
         * then all items are selected
         *
         * params
         * args -
         * cb - callback
         */
        list: function (args, cb) {
            var qent = args.qent;
            var q = args.q;

            var query = [
                    'MATCH (ent:' + createCollectionName(qent) + ') ',
                composeWhereClause(q),
                'RETURN ent'
            ].join('\n');

            dbinst.query(query, null, function (err, entp) {
                if (!error(args, err, cb)) {
                    var results = createSenecaObjectsFromNodes(entp, qent);
                    seneca.log.debug('list', q, results, desc);
                    cb(null, results);
                }
            });
        },

        /**
         * delete an item
         *
         * params
         * args -
         * cb - callback
         */
        remove: function (args, cb) {
            var qent = args.qent;
            var q = args.q;

            var query = [
                    'MATCH (ent:' + createCollectionName(qent) + ') ',
                composeWhereClause(q),
                'delete ent'
            ].join('\n');

            dbinst.query(query, null, function (err, entp) {
                if (!error(args, err, cb)) {
                    seneca.log.debug('remove', q, [], desc);
                    cb(null, []);
                }
            });

        },

        /**
         * return the underlying native connection object
         *
         * params
         * cb - callback
         */
        native: function (args, cb) {
            if(dbinst){
                cb(null, dbinst);
            }else{
                cb(new Error());
            }

        }
    };

    var meta = seneca.store.init(seneca, opts, store);
    desc = meta.desc;

    seneca.add({init: store.name, tag: meta.tag}, function (args, done) {
        configure(opts, function (err) {
            if (err) {
                return seneca.die('store', err, {store: store.name, desc: desc});
            }
            return done();
        });
    });

    return {name: store.name, tag: meta.tag};
};

<|endoftext|>


<commit_before>'use strict'

// order of entries in this map *does* matter for the resolved labels
// earlier entries override later entries
const subSystemLabelsMap = new Map([
  /* src subsystems */
  [/^src\/async-wrap/, ['c++', 'async_wrap']],
  [/^src\/(?:base64|node_buffer|string_)/, ['c++', 'buffer']],
  [/^src\/cares/, ['c++', 'cares']],
  [/^src\/(?:process_wrap|spawn_)/, ['c++', 'child_process']],
  [/^src\/node_crypto/, ['c++', 'crypto']],
  [/^src\/(?:debug-|node_debug)/, ['c++', 'debugger']],
  [/^src\/udp_/, ['c++', 'dgram']],
  [/^src\/(?:fs_|node_file|node_stat_watcher)/, ['c++', 'fs']],
  [/^src\/node_http_parser/, ['c++', 'http_parser']],
  [/^src\/node_i18n/, ['c++', 'intl']],
  [/^src\/uv\./, ['c++', 'libuv']],
  [/^src\/(?:connect(?:ion)?|pipe|tcp)_/, ['c++', 'net']],
  [/^src\/node_os/, ['c++', 'os']],
  [/^src\/(?:node_main|signal_)/, ['c++', 'process']],
  [/^src\/timer_/, ['c++', 'timers']],
  [/^src\/(?:CNNICHashWhitelist|node_root_certs|tls_)/, ['c++', 'tls']],
  [/^src\/tty_/, ['c++', 'tty']],
  [/^src\/node_url/, ['c++', 'url-whatwg']],
  [/^src\/node_util/, ['c++', 'util']],
  [/^src\/(?:node_v8|v8abbr)/, ['c++', 'V8 Engine']],
  [/^src\/node_contextify/, ['c++', 'vm']],
  [/^src\/.*win32.*/, ['c++', 'windows']],
  [/^src\/node_zlib/, ['c++', 'zlib']],
  [/^src\/tracing/, ['c++', 'tracing']],
  [/^src\/node_api/, ['c++', 'n-api']],
  [/^src\/node_http2/, ['c++', 'http2', 'dont-land-on-v6.x']],
  [/^src\/node_report/, ['c++', 'report']],
  [/^src\/node_wasi/, ['c++', 'wasi']],
  [/^src\/node_worker/, ['c++', 'worker']],

  // don't label python files as c++
  [/^src\/.+\.py$/, 'lib / src'],

  // properly label changes to v8 inspector integration-related files
  [/^src\/inspector_/, ['c++', 'inspector']],

  // don't want to label it a c++ update when we're "only" bumping the Node.js version
  [/^src\/(?!node_version\.h)/, 'c++'],
  // BUILDING.md should be marked as 'build' in addition to 'doc'
  [/^BUILDING\.md$/, ['build', 'doc']],
  // meta is a very specific label for things that are policy and or meta-info related
  [/^([A-Z]+$|CODE_OF_CONDUCT|ROADMAP|WORKING_GROUPS|GOVERNANCE|CHANGELOG|\.mail|\.git.+)/, 'meta'],
  // things that edit top-level .md files are always a doc change
  [/^\w+\.md$/, 'doc'],
  // different variants of *Makefile and build files
  [/^(tools\/)?(Makefile|BSDmakefile|create_android_makefiles|\.travis\.yml)$/, 'build'],
  [/^tools\/(install\.py|genv8constants\.py|getnodeversion\.py|js2c\.py|utils\.py|configure\.d\/.*)$/, 'build'],
  [/^vcbuild\.bat$/, ['build', 'windows']],
  [/^(android-)?configure|node\.gyp|common\.gypi$/, 'build'],
  // more specific tools
  [/^tools\/gyp/, ['tools', 'build']],
  [/^tools\/doc\//, ['tools', 'doc']],
  [/^tools\/icu\//, ['tools', 'intl']],
  [/^tools\/(?:osx-pkg\.pmdoc|pkgsrc)\//, ['tools', 'macos', 'install']],
  [/^tools\/(?:(?:mac)?osx-)/, ['tools', 'macos']],
  [/^tools\/test-npm/, ['tools', 'test', 'npm']],
  [/^tools\/test/, ['tools', 'test']],
  [/^tools\/(?:certdata|mkssldef|mk-ca-bundle)/, ['tools', 'openssl', 'tls']],
  [/^tools\/msvs\//, ['tools', 'windows', 'install']],
  [/^tools\/[^/]+\.bat$/, ['tools', 'windows']],
  [/^tools\/make-v8/, ['tools', 'V8 Engine']],
  // all other tool changes should be marked as such
  [/^tools\//, 'tools'],
  [/^\.eslint|\.remark|\.editorconfig/, 'tools'],

  /* Dependencies */
  // libuv needs an explicit mapping, as the ordinary /deps/ mapping below would
  // end up as libuv changes labeled with "uv" (which is a non-existing label)
  [/^deps\/uv\//, 'libuv'],
  [/^deps\/v8\/tools\/gen-postmortem-metadata\.py/, ['V8 Engine', 'post-mortem']],
  [/^deps\/v8\//, 'V8 Engine'],
  [/^deps\/uvwasi\//, 'wasi'],
  [/^deps\/nghttp2\/nghttp2\.gyp/, ['build', 'http2', 'dont-land-on-v6.x']],
  [/^deps\/nghttp2\//, ['http2', 'dont-land-on-v6.x']],
  [/^deps\/([^/]+)/, '$1'],

  /* JS subsystems */
  // Oddities first
  [/^lib\/(punycode|\w+\/freelist|sys\.js)/, ''], // TODO: ignore better?
  [/^lib\/constants\.js$/, 'lib / src'],
  [/^lib\/_(debug_agent|debugger)\.js$/, 'debugger'],
  [/^lib(\/\w+)?\/(_)?link(ed)?list/, 'timers'],
  [/^lib\/\w+\/bootstrap_node/, 'lib / src'],
  [/^lib\/\w+\/v8_prof_/, 'tools'],
  [/^lib\/\w+\/socket_list/, 'net'],
  [/^lib\/\w+\/streams$/, 'stream'],
  [/^lib\/.*http2/, ['http2', 'dont-land-on-v6.x']],
  [/^lib\/worker_threads.js$/, ['worker']],
  [/^lib\/internal\/url\.js$/, ['url-whatwg']],
  [/^lib\/internal\/modules\/esm/, 'ES Modules'],
  // All other lib/ files map directly
  [/^lib\/_(\w+)_\w+\.js?$/, '$1'], // e.g. _(stream)_wrap
  [/^lib(\/internal)?\/(\w+)\.js?$/, '$2'], // other .js files
  [/^lib\/internal\/(\w+)(?:\/|$)/, '$1'] // internal subfolders
])

const jsSubsystemList = [
  'debugger', 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster',
  'console', 'crypto', 'dgram', 'dns', 'domain', 'events', 'esm', 'fs', 'http',
  'https', 'http2', 'module', 'net', 'os', 'path', 'process', 'querystring',
  'readline', 'repl', 'report', 'stream', 'string_decoder', 'timers', 'tls',
  'tty', 'url', 'util', 'v8', 'vm', 'wasi', 'worker', 'zlib'
]

const exclusiveLabelsMap = new Map([
  // more specific tests
  [/^test\/addons\//, ['test', 'addons']],
  [/^test\/debugger\//, ['test', 'debugger']],
  [/^test\/doctool\//, ['test', 'doc', 'tools']],
  [/^test\/timers\//, ['test', 'timers']],
  [/^test\/pseudo-tty\//, ['test', 'tty']],
  [/^test\/inspector\//, ['test', 'inspector']],
  [/^test\/cctest\/test_inspector/, ['test', 'inspector']],
  [/^test\/cctest\/test_url/, ['test', 'url-whatwg']],
  [/^test\/addons-napi\//, ['test', 'n-api']],
  [/^test\/async-hooks\//, ['test', 'async_hooks']],
  [/^test\/report\//, ['test', 'report']],
  [/^test\/fixtures\/es-module/, ['test', 'ES Modules']],
  [/^test\/es-module\//, ['test', 'ES Modules']],

  [/^test\//, 'test'],

  // specific map for modules.md as it should be labeled 'module' not 'modules'
  [/^doc\/api\/modules.md$/, ['doc', 'module']],
  // specific map for esm.md as it should be labeled 'ES Modules' not 'esm'
  [/^doc\/api\/esm.md$/, ['doc', 'ES Modules']],
  // n-api is treated separately since it is not a JS core module but is still
  // considered a subsystem of sorts
  [/^doc\/api\/n-api.md$/, ['doc', 'n-api']],
  // add worker label to PRs that affect doc/api/worker_threads.md
  [/^doc\/api\/worker_threads.md$/, ['doc', 'worker']],
  // automatically tag JS subsystem-specific API doc changes
  [/^doc\/api\/(\w+)\.md$/, ['doc', '$1']],
  // add deprecations label to PRs that affect doc/api/deprecations.md
  [/^doc\/api\/deprecations.md$/, ['doc', 'deprecations']],

  [/^doc\//, 'doc'],

  // more specific benchmarks
  [/^benchmark\/buffers\//, ['benchmark', 'buffer']],
  [/^benchmark\/(?:arrays|es)\//, ['benchmark', 'V8 Engine']],
  [/^benchmark\/_http/, ['benchmark', 'http']],
  [/^benchmark\/(?:misc|fixtures)\//, 'benchmark'],
  [/^benchmark\/streams\//, ['benchmark', 'stream']],
  [/^benchmark\/([^/]+)\//, ['benchmark', '$1']],

  [/^benchmark\//, 'benchmark']
])

function resolveLabels (filepathsChanged, baseBranch, limitLabels = true) {
  const exclusiveLabels = matchExclusiveSubSystem(filepathsChanged)

  if (typeof baseBranch !== 'string') {
    if (typeof baseBranch === 'boolean') {
      limitLabels = baseBranch
    }
    baseBranch = ''
  }

  const labels = (exclusiveLabels.length > 0)
    ? exclusiveLabels
    : matchAllSubSystem(filepathsChanged, limitLabels)

  // Add version labels if PR is made against a version branch
  const m = /^(v\d+\.(?:\d+|x))(?:-staging|$)/.exec(baseBranch)
  if (m) {
    labels.push(m[1])
  }

  return labels
}

function hasAllSubsystems (arr) {
  return arr.every((val) => {
    return jsSubsystemList.includes(val)
  })
}

// This function is needed to help properly identify when a PR should always
// (just) be labeled as 'doc' when it is all changes in doc/api/ that do not
// match subsystem names (e.g. _toc.md, all.md)
function hasAllDocChanges (arr) {
  return arr.every((val) => {
    return /^doc\//.test(val)
  })
}

function hasAllTestChanges (arr) {
  return arr.every((val) => {
    return /^test\//.test(val)
  })
}

function matchExclusiveSubSystem (filepathsChanged) {
  const isExclusive = filepathsChanged.every(matchesAnExclusiveLabel)
  var labels = matchSubSystemsByRegex(exclusiveLabelsMap, filepathsChanged)
  var nonMetaLabels = labels.filter((label) => {
    return !/^dont-/.test(label)
  })

  // if there are multiple API doc changes, do not apply subsystem tags for now
  if (isExclusive &&
    nonMetaLabels.includes('doc') &&
    nonMetaLabels.length > 2 &&
    !hasAllTestChanges(filepathsChanged)) {
    const nonDocLabels = nonMetaLabels.filter((val) => {
      return val !== 'doc'
    })
    if (hasAllSubsystems(nonDocLabels) || hasAllDocChanges(filepathsChanged)) {
      labels = ['doc']
    } else {
      labels = []
    }
  }
  return isExclusive ? labels : []
}

function matchAllSubSystem (filepathsChanged, limitLabels) {
  return matchSubSystemsByRegex(
    subSystemLabelsMap, filepathsChanged, limitLabels)
}

function matchSubSystemsByRegex (rxLabelsMap, filepathsChanged, limitLabels) {
  const labelCount = []
  // by putting matched labels into a map, we avoid duplicate labels
  const labelsMap = filepathsChanged.reduce((map, filepath) => {
    const mappedSubSystems = mappedSubSystemsForFile(rxLabelsMap, filepath)

    if (!mappedSubSystems) {
      // short-circuit
      return map
    }

    for (var i = 0; i < mappedSubSystems.length; ++i) {
      const mappedSubSystem = mappedSubSystems[i]
      if (limitLabels && hasLibOrSrcChanges(filepathsChanged)) {
        if (labelCount.length >= 4) {
          for (const label of labelCount) {
            // don't delete the c++ label as we always want that if it has matched
            if (label !== 'c++') delete map[label]
          }
          map['lib / src'] = true
          // short-circuit
          return map
        } else {
          labelCount.push(mappedSubSystem)
        }
      }

      map[mappedSubSystem] = true
    }

    return map
  }, {})

  return Object.keys(labelsMap)
}

function hasLibOrSrcChanges (filepathsChanged) {
  return filepathsChanged.some((filepath) => filepath.startsWith('lib/') || filepath.startsWith('src/'))
}

function mappedSubSystemsForFile (labelsMap, filepath) {
  for (const [regex, label] of labelsMap) {
    const matches = regex.exec(filepath)

    if (matches === null) {
      continue
    }

    const ret = []
    const labels = Array.isArray(label) ? label : [label]
    labels.forEach((label) => {
      // label names starting with $ means we want to extract a matching
      // group from the regex we've just matched against
      if (label.startsWith('$')) {
        const wantedMatchGroup = label.substr(1)
        label = matches[wantedMatchGroup]
      }
      if (!label) {
        return
      }
      // use label name as is when label doesn't look like a regex matching group
      ret.push(label)
    })
    return ret
  }
}

function matchesAnExclusiveLabel (filepath) {
  return mappedSubSystemsForFile(exclusiveLabelsMap, filepath) !== undefined
}

exports.resolveLabels = resolveLabels
<commit_msg>labels: add quic label (#260)<commit_after>'use strict'

// order of entries in this map *does* matter for the resolved labels
// earlier entries override later entries
const subSystemLabelsMap = new Map([
  /* src subsystems */
  [/^src\/async-wrap/, ['c++', 'async_wrap']],
  [/^src\/(?:base64|node_buffer|string_)/, ['c++', 'buffer']],
  [/^src\/cares/, ['c++', 'cares']],
  [/^src\/(?:process_wrap|spawn_)/, ['c++', 'child_process']],
  [/^src\/node_crypto/, ['c++', 'crypto']],
  [/^src\/(?:debug-|node_debug)/, ['c++', 'debugger']],
  [/^src\/udp_/, ['c++', 'dgram']],
  [/^src\/(?:fs_|node_file|node_stat_watcher)/, ['c++', 'fs']],
  [/^src\/node_http_parser/, ['c++', 'http_parser']],
  [/^src\/node_i18n/, ['c++', 'intl']],
  [/^src\/uv\./, ['c++', 'libuv']],
  [/^src\/(?:connect(?:ion)?|pipe|tcp)_/, ['c++', 'net']],
  [/^src\/node_os/, ['c++', 'os']],
  [/^src\/(?:node_main|signal_)/, ['c++', 'process']],
  [/^src\/timer_/, ['c++', 'timers']],
  [/^src\/(?:CNNICHashWhitelist|node_root_certs|tls_)/, ['c++', 'tls']],
  [/^src\/tty_/, ['c++', 'tty']],
  [/^src\/node_url/, ['c++', 'url-whatwg']],
  [/^src\/node_util/, ['c++', 'util']],
  [/^src\/(?:node_v8|v8abbr)/, ['c++', 'V8 Engine']],
  [/^src\/node_contextify/, ['c++', 'vm']],
  [/^src\/.*win32.*/, ['c++', 'windows']],
  [/^src\/node_zlib/, ['c++', 'zlib']],
  [/^src\/tracing/, ['c++', 'tracing']],
  [/^src\/node_api/, ['c++', 'n-api']],
  [/^src\/node_http2/, ['c++', 'http2', 'dont-land-on-v6.x']],
  [/^src\/node_report/, ['c++', 'report']],
  [/^src\/node_wasi/, ['c++', 'wasi']],
  [/^src\/node_worker/, ['c++', 'worker']],
  [/^src\/quic\/*/, ['c++', 'quic']],
  [/^src\/node_bob*/, ['c++', 'quic']],

  // don't label python files as c++
  [/^src\/.+\.py$/, 'lib / src'],

  // properly label changes to v8 inspector integration-related files
  [/^src\/inspector_/, ['c++', 'inspector']],

  // don't want to label it a c++ update when we're "only" bumping the Node.js version
  [/^src\/(?!node_version\.h)/, 'c++'],
  // BUILDING.md should be marked as 'build' in addition to 'doc'
  [/^BUILDING\.md$/, ['build', 'doc']],
  // meta is a very specific label for things that are policy and or meta-info related
  [/^([A-Z]+$|CODE_OF_CONDUCT|ROADMAP|WORKING_GROUPS|GOVERNANCE|CHANGELOG|\.mail|\.git.+)/, 'meta'],
  // things that edit top-level .md files are always a doc change
  [/^\w+\.md$/, 'doc'],
  // different variants of *Makefile and build files
  [/^(tools\/)?(Makefile|BSDmakefile|create_android_makefiles|\.travis\.yml)$/, 'build'],
  [/^tools\/(install\.py|genv8constants\.py|getnodeversion\.py|js2c\.py|utils\.py|configure\.d\/.*)$/, 'build'],
  [/^vcbuild\.bat$/, ['build', 'windows']],
  [/^(android-)?configure|node\.gyp|common\.gypi$/, 'build'],
  // more specific tools
  [/^tools\/gyp/, ['tools', 'build']],
  [/^tools\/doc\//, ['tools', 'doc']],
  [/^tools\/icu\//, ['tools', 'intl']],
  [/^tools\/(?:osx-pkg\.pmdoc|pkgsrc)\//, ['tools', 'macos', 'install']],
  [/^tools\/(?:(?:mac)?osx-)/, ['tools', 'macos']],
  [/^tools\/test-npm/, ['tools', 'test', 'npm']],
  [/^tools\/test/, ['tools', 'test']],
  [/^tools\/(?:certdata|mkssldef|mk-ca-bundle)/, ['tools', 'openssl', 'tls']],
  [/^tools\/msvs\//, ['tools', 'windows', 'install']],
  [/^tools\/[^/]+\.bat$/, ['tools', 'windows']],
  [/^tools\/make-v8/, ['tools', 'V8 Engine']],
  // all other tool changes should be marked as such
  [/^tools\//, 'tools'],
  [/^\.eslint|\.remark|\.editorconfig/, 'tools'],

  /* Dependencies */
  // libuv needs an explicit mapping, as the ordinary /deps/ mapping below would
  // end up as libuv changes labeled with "uv" (which is a non-existing label)
  [/^deps\/uv\//, 'libuv'],
  [/^deps\/v8\/tools\/gen-postmortem-metadata\.py/, ['V8 Engine', 'post-mortem']],
  [/^deps\/v8\//, 'V8 Engine'],
  [/^deps\/uvwasi\//, 'wasi'],
  [/^deps\/nghttp2\/nghttp2\.gyp/, ['build', 'http2', 'dont-land-on-v6.x']],
  [/^deps\/nghttp2\//, ['http2', 'dont-land-on-v6.x']],
  [/^deps\/ngtcp2\//, 'quic'],
  [/^deps\/nghttp3\//, 'quic'],
  [/^deps\/([^/]+)/, '$1'],

  /* JS subsystems */
  // Oddities first
  [/^lib\/(punycode|\w+\/freelist|sys\.js)/, ''], // TODO: ignore better?
  [/^lib\/constants\.js$/, 'lib / src'],
  [/^lib\/_(debug_agent|debugger)\.js$/, 'debugger'],
  [/^lib(\/\w+)?\/(_)?link(ed)?list/, 'timers'],
  [/^lib\/\w+\/bootstrap_node/, 'lib / src'],
  [/^lib\/\w+\/v8_prof_/, 'tools'],
  [/^lib\/\w+\/socket_list/, 'net'],
  [/^lib\/\w+\/streams$/, 'stream'],
  [/^lib\/.*http2/, ['http2', 'dont-land-on-v6.x']],
  [/^lib\/worker_threads.js$/, ['worker']],
  [/^lib\/internal\/url\.js$/, ['url-whatwg']],
  [/^lib\/internal\/modules\/esm/, 'ES Modules'],
  [/^lib\/internal\/quic\/*/, ['quic']],
  // All other lib/ files map directly
  [/^lib\/_(\w+)_\w+\.js?$/, '$1'], // e.g. _(stream)_wrap
  [/^lib(\/internal)?\/(\w+)\.js?$/, '$2'], // other .js files
  [/^lib\/internal\/(\w+)(?:\/|$)/, '$1'] // internal subfolders
])

const jsSubsystemList = [
  'debugger', 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster',
  'console', 'crypto', 'dgram', 'dns', 'domain', 'events', 'esm', 'fs', 'http',
  'https', 'http2', 'module', 'net', 'os', 'path', 'process', 'querystring',
  'quic', 'readline', 'repl', 'report', 'stream', 'string_decoder', 'timers',
  'tls', 'tty', 'url', 'util', 'v8', 'vm', 'wasi', 'worker', 'zlib'
]

const exclusiveLabelsMap = new Map([
  // more specific tests
  [/^test\/addons\//, ['test', 'addons']],
  [/^test\/debugger\//, ['test', 'debugger']],
  [/^test\/doctool\//, ['test', 'doc', 'tools']],
  [/^test\/timers\//, ['test', 'timers']],
  [/^test\/pseudo-tty\//, ['test', 'tty']],
  [/^test\/inspector\//, ['test', 'inspector']],
  [/^test\/cctest\/test_inspector/, ['test', 'inspector']],
  [/^test\/cctest\/test_url/, ['test', 'url-whatwg']],
  [/^test\/addons-napi\//, ['test', 'n-api']],
  [/^test\/async-hooks\//, ['test', 'async_hooks']],
  [/^test\/report\//, ['test', 'report']],
  [/^test\/fixtures\/es-module/, ['test', 'ES Modules']],
  [/^test\/es-module\//, ['test', 'ES Modules']],

  [/^test\//, 'test'],

  // specific map for modules.md as it should be labeled 'module' not 'modules'
  [/^doc\/api\/modules.md$/, ['doc', 'module']],
  // specific map for esm.md as it should be labeled 'ES Modules' not 'esm'
  [/^doc\/api\/esm.md$/, ['doc', 'ES Modules']],
  // n-api is treated separately since it is not a JS core module but is still
  // considered a subsystem of sorts
  [/^doc\/api\/n-api.md$/, ['doc', 'n-api']],
  // quic
  [/^doc\/api\/quic.md$/, ['doc', 'quic']],
  // add worker label to PRs that affect doc/api/worker_threads.md
  [/^doc\/api\/worker_threads.md$/, ['doc', 'worker']],
  // automatically tag JS subsystem-specific API doc changes
  [/^doc\/api\/(\w+)\.md$/, ['doc', '$1']],
  // add deprecations label to PRs that affect doc/api/deprecations.md
  [/^doc\/api\/deprecations.md$/, ['doc', 'deprecations']],

  [/^doc\//, 'doc'],

  // more specific benchmarks
  [/^benchmark\/buffers\//, ['benchmark', 'buffer']],
  [/^benchmark\/(?:arrays|es)\//, ['benchmark', 'V8 Engine']],
  [/^benchmark\/_http/, ['benchmark', 'http']],
  [/^benchmark\/(?:misc|fixtures)\//, 'benchmark'],
  [/^benchmark\/streams\//, ['benchmark', 'stream']],
  [/^benchmark\/([^/]+)\//, ['benchmark', '$1']],

  [/^benchmark\//, 'benchmark']
])

function resolveLabels (filepathsChanged, baseBranch, limitLabels = true) {
  const exclusiveLabels = matchExclusiveSubSystem(filepathsChanged)

  if (typeof baseBranch !== 'string') {
    if (typeof baseBranch === 'boolean') {
      limitLabels = baseBranch
    }
    baseBranch = ''
  }

  const labels = (exclusiveLabels.length > 0)
    ? exclusiveLabels
    : matchAllSubSystem(filepathsChanged, limitLabels)

  // Add version labels if PR is made against a version branch
  const m = /^(v\d+\.(?:\d+|x))(?:-staging|$)/.exec(baseBranch)
  if (m) {
    labels.push(m[1])
  }

  return labels
}

function hasAllSubsystems (arr) {
  return arr.every((val) => {
    return jsSubsystemList.includes(val)
  })
}

// This function is needed to help properly identify when a PR should always
// (just) be labeled as 'doc' when it is all changes in doc/api/ that do not
// match subsystem names (e.g. _toc.md, all.md)
function hasAllDocChanges (arr) {
  return arr.every((val) => {
    return /^doc\//.test(val)
  })
}

function hasAllTestChanges (arr) {
  return arr.every((val) => {
    return /^test\//.test(val)
  })
}

function matchExclusiveSubSystem (filepathsChanged) {
  const isExclusive = filepathsChanged.every(matchesAnExclusiveLabel)
  var labels = matchSubSystemsByRegex(exclusiveLabelsMap, filepathsChanged)
  var nonMetaLabels = labels.filter((label) => {
    return !/^dont-/.test(label)
  })

  // if there are multiple API doc changes, do not apply subsystem tags for now
  if (isExclusive &&
    nonMetaLabels.includes('doc') &&
    nonMetaLabels.length > 2 &&
    !hasAllTestChanges(filepathsChanged)) {
    const nonDocLabels = nonMetaLabels.filter((val) => {
      return val !== 'doc'
    })
    if (hasAllSubsystems(nonDocLabels) || hasAllDocChanges(filepathsChanged)) {
      labels = ['doc']
    } else {
      labels = []
    }
  }
  return isExclusive ? labels : []
}

function matchAllSubSystem (filepathsChanged, limitLabels) {
  return matchSubSystemsByRegex(
    subSystemLabelsMap, filepathsChanged, limitLabels)
}

function matchSubSystemsByRegex (rxLabelsMap, filepathsChanged, limitLabels) {
  const labelCount = []
  // by putting matched labels into a map, we avoid duplicate labels
  const labelsMap = filepathsChanged.reduce((map, filepath) => {
    const mappedSubSystems = mappedSubSystemsForFile(rxLabelsMap, filepath)

    if (!mappedSubSystems) {
      // short-circuit
      return map
    }

    for (var i = 0; i < mappedSubSystems.length; ++i) {
      const mappedSubSystem = mappedSubSystems[i]
      if (limitLabels && hasLibOrSrcChanges(filepathsChanged)) {
        if (labelCount.length >= 4) {
          for (const label of labelCount) {
            // don't delete the c++ label as we always want that if it has matched
            if (label !== 'c++') delete map[label]
          }
          map['lib / src'] = true
          // short-circuit
          return map
        } else {
          labelCount.push(mappedSubSystem)
        }
      }

      map[mappedSubSystem] = true
    }

    return map
  }, {})

  return Object.keys(labelsMap)
}

function hasLibOrSrcChanges (filepathsChanged) {
  return filepathsChanged.some((filepath) => filepath.startsWith('lib/') || filepath.startsWith('src/'))
}

function mappedSubSystemsForFile (labelsMap, filepath) {
  for (const [regex, label] of labelsMap) {
    const matches = regex.exec(filepath)

    if (matches === null) {
      continue
    }

    const ret = []
    const labels = Array.isArray(label) ? label : [label]
    labels.forEach((label) => {
      // label names starting with $ means we want to extract a matching
      // group from the regex we've just matched against
      if (label.startsWith('$')) {
        const wantedMatchGroup = label.substr(1)
        label = matches[wantedMatchGroup]
      }
      if (!label) {
        return
      }
      // use label name as is when label doesn't look like a regex matching group
      ret.push(label)
    })
    return ret
  }
}

function matchesAnExclusiveLabel (filepath) {
  return mappedSubSystemsForFile(exclusiveLabelsMap, filepath) !== undefined
}

exports.resolveLabels = resolveLabels
<|endoftext|>


<commit_before>/** @babel */

import chokidar from 'chokidar'
import elasticlunr from 'elasticlunr'
import fs from 'fs-plus'
import path from 'path'
import matter from 'gray-matter'

let {EventEmitter} = require('events')

/**
  * @typedef DocumentItem
  * @type {Object}
  * @property {string} title - Title of the note.
  * @property {string} fileName - Name of the file where note is stored.
  * @property {string} filePath - Path to the file where note is stored.
  * @property {string} body - Body of the note.
  * @property {Array} keywords - Keywords of the note.
  * @property {Array} abstract - Summary of the note.
  * @property {Date} modifiedAt - Date and time of last modification to the note.
  */

/**
 * Builds a document object to hold state about a note document.
 * @param  {String} filePath Full path to the document.
 * @return {DocumentItem}
 */
function createDocument (filePath) {
  let fileStats = fs.statSync(filePath)
  let fileName = path.basename(filePath)
  let title = path.basename(fileName, path.extname(fileName))
  let body = fs.readFileSync(filePath, {encoding: 'utf8'})
  let meta = matter(body).data
  let keywords = []
  let abstract = null

  if (meta !== undefined && meta.keywords !== undefined) {
    keywords = meta.keywords
  }

  if (meta !== undefined && meta.abstract !== undefined) {
    abstract = meta.abstract
  }

  return {
    filePath: filePath,
    fileName: fileName,
    title: title,
    modifiedAt: fileStats.mtime,
    body: body,
    keywords: keywords,
    abstract: abstract
  }
}

class NotesStore extends EventEmitter {
  /**
   * Document storage for notes.
   * @param {String}   directoryPath Full path to where notes are stored.
   * @param {String[]} extensions    File extensions of notes.
   * @param {Object}   [index=null]  Serialized elasticlunr index to load.
   */
  constructor (directoryPath, extensions, index = null) {
    super()
    this.extensions = extensions
    this._documents = {}

    if (index) {
      console.log('atom-notes: loading...')
      this.loaded = true
      this.index = elasticlunr.Index.load(index)
    } else {
      console.log('atom-notes: indexing...')
      this.loaded = false
      this.index = elasticlunr(function () {
        this.addField('title')
        this.addField('body')
        this.addField('keywords')
        this.addField('abstract')
      })
    }

    this.watcher = chokidar.watch(null, {
      depth: undefined, // recursive
      useFsEvents: false, // avoid native module issues on macOS
      persistent: true,
      ignored: (watchedPath, fileStats) => {
        if (!fileStats) return false
        if (fileStats.isDirectory()) return false
        return !(this.extensions.indexOf(path.extname(watchedPath)) > -1)
      }
    })

    this.watcher
      .on('add', filePath => this.addDocument(createDocument(filePath)))
      .on('change', filePath => this.updateDocument(createDocument(filePath)))
      .on('unlink', filePath => this.removeDocument(this._documents[filePath]))
      .on('ready', () => this.emit('ready'))
    this.watcher.add(fs.normalize(directoryPath))
  }

  /**
   * Adds the given document to our index.
   * @param {DocumentItem} doc
   */
  addDocument (doc) {
    this._documents[doc.filePath] = doc
    let data = {
      id: doc.filePath,
      title: doc.title,
      body: doc.body,
      abstract: doc.abstract
    }
    if (doc.keywords) {
      data.keywords = doc.keywords.join(', ')
    }
    this.index.addDoc(data)
    this.emit('added', doc)
  }

  /**
   * Updates the given document, identified by filePath, in our index.
   * @param {DocumentItem} doc
   */
  updateDocument (doc) {
    this._documents[doc.filePath] = doc
    let data = {
      id: doc.filePath,
      title: doc.title,
      body: doc.body,
      abstract: doc.abstract
    }
    if (doc.keywords) {
      data.keywords = doc.keywords.join(', ')
    }
    this.index.updateDoc(data)
    this.emit('updated', doc)
  }

  /**
   * Removes the given document, identified by filePath, from our index.
   * @param {DocumentItem} doc
   */
  removeDocument (doc) {
    delete this._documents[doc.filePath]
    let data = {
      id: doc.filePath,
      title: doc.title,
      body: doc.body,
      keywords: doc.keywords.join(', '),
      abstract: doc.abstract
    }
    if (doc.keywords) {
      data.keywords = doc.keywords.join(', ')
    }
    this.index.removeDoc(data)
    this.emit('removed', doc)
  }

  /**
   * Search for notes in our document store.
   * @param  {String} query Some text to use for matching indexed documents.
   * @return {DocumentItem[]}
   */
  search (query) {
    const config = {
      fields: {
        keywords: { boost: 20, bool: 'AND' },
        title: { boost: 10, bool: 'AND' },
        abstract: { boost: 5, bool: 'AND' },
        body: { boost: 1 }
      },
      bool: 'OR',
      expand: true
    }
    return this.index.search(query, config).map((result) => {
      return this._documents[result.ref]
    })
  }

  /**
   * Fetch all documents in our index.
   * @return {DocumentItem[]}
   */
  get documents () {
    var documents = []
    for (let key in this._documents) {
      documents.push(this._documents[key])
    }
    return documents.sort((a, b) => {
      if (a.modifiedAt < b.modifiedAt) return 1
      if (a.modifiedAt > b.modifiedAt) return -1
      return 0
    })
  }
}

module.exports = NotesStore
<commit_msg>Guard against keywords not being an array?<commit_after>/** @babel */

import chokidar from 'chokidar'
import elasticlunr from 'elasticlunr'
import fs from 'fs-plus'
import path from 'path'
import matter from 'gray-matter'

let {EventEmitter} = require('events')

/**
  * @typedef DocumentItem
  * @type {Object}
  * @property {string} title - Title of the note.
  * @property {string} fileName - Name of the file where note is stored.
  * @property {string} filePath - Path to the file where note is stored.
  * @property {string} body - Body of the note.
  * @property {Array} keywords - Keywords of the note.
  * @property {Array} abstract - Summary of the note.
  * @property {Date} modifiedAt - Date and time of last modification to the note.
  */

/**
 * Builds a document object to hold state about a note document.
 * @param  {String} filePath Full path to the document.
 * @return {DocumentItem}
 */
function createDocument (filePath) {
  let fileStats = fs.statSync(filePath)
  let fileName = path.basename(filePath)
  let title = path.basename(fileName, path.extname(fileName))
  let body = fs.readFileSync(filePath, {encoding: 'utf8'})
  let meta = matter(body).data
  let keywords = []
  let abstract = null

  if (meta !== undefined && meta.keywords !== undefined) {
    keywords = meta.keywords
  }

  if (meta !== undefined && meta.abstract !== undefined) {
    abstract = meta.abstract
  }

  return {
    filePath: filePath,
    fileName: fileName,
    title: title,
    modifiedAt: fileStats.mtime,
    body: body,
    keywords: keywords,
    abstract: abstract
  }
}

class NotesStore extends EventEmitter {
  /**
   * Document storage for notes.
   * @param {String}   directoryPath Full path to where notes are stored.
   * @param {String[]} extensions    File extensions of notes.
   * @param {Object}   [index=null]  Serialized elasticlunr index to load.
   */
  constructor (directoryPath, extensions, index = null) {
    super()
    this.extensions = extensions
    this._documents = {}

    if (index) {
      console.log('atom-notes: loading...')
      this.loaded = true
      this.index = elasticlunr.Index.load(index)
    } else {
      console.log('atom-notes: indexing...')
      this.loaded = false
      this.index = elasticlunr(function () {
        this.addField('title')
        this.addField('body')
        this.addField('keywords')
        this.addField('abstract')
      })
    }

    this.watcher = chokidar.watch(null, {
      depth: undefined, // recursive
      useFsEvents: false, // avoid native module issues on macOS
      persistent: true,
      ignored: (watchedPath, fileStats) => {
        if (!fileStats) return false
        if (fileStats.isDirectory()) return false
        return !(this.extensions.indexOf(path.extname(watchedPath)) > -1)
      }
    })

    this.watcher
      .on('add', filePath => this.addDocument(createDocument(filePath)))
      .on('change', filePath => this.updateDocument(createDocument(filePath)))
      .on('unlink', filePath => this.removeDocument(this._documents[filePath]))
      .on('ready', () => this.emit('ready'))
    this.watcher.add(fs.normalize(directoryPath))
  }

  /**
   * Adds the given document to our index.
   * @param {DocumentItem} doc
   */
  addDocument (doc) {
    this._documents[doc.filePath] = doc
    let data = {
      id: doc.filePath,
      title: doc.title,
      body: doc.body,
      abstract: doc.abstract
    }
    if (doc.keywords && Array.isArray(doc.keywords)) {
      data.keywords = doc.keywords.join(', ')
    }
    this.index.addDoc(data)
    this.emit('added', doc)
  }

  /**
   * Updates the given document, identified by filePath, in our index.
   * @param {DocumentItem} doc
   */
  updateDocument (doc) {
    this._documents[doc.filePath] = doc
    let data = {
      id: doc.filePath,
      title: doc.title,
      body: doc.body,
      abstract: doc.abstract
    }
    if (doc.keywords && Array.isArray(doc.keywords)) {
      data.keywords = doc.keywords.join(', ')
    }
    this.index.updateDoc(data)
    this.emit('updated', doc)
  }

  /**
   * Removes the given document, identified by filePath, from our index.
   * @param {DocumentItem} doc
   */
  removeDocument (doc) {
    delete this._documents[doc.filePath]
    let data = {
      id: doc.filePath,
      title: doc.title,
      body: doc.body,
      abstract: doc.abstract
    }
    if (doc.keywords && Array.isArray(doc.keywords)) {
      data.keywords = doc.keywords.join(', ')
    }
    this.index.removeDoc(data)
    this.emit('removed', doc)
  }

  /**
   * Search for notes in our document store.
   * @param  {String} query Some text to use for matching indexed documents.
   * @return {DocumentItem[]}
   */
  search (query) {
    const config = {
      fields: {
        keywords: { boost: 20, bool: 'AND' },
        title: { boost: 10, bool: 'AND' },
        abstract: { boost: 5, bool: 'AND' },
        body: { boost: 1 }
      },
      bool: 'OR',
      expand: true
    }
    return this.index.search(query, config).map((result) => {
      return this._documents[result.ref]
    })
  }

  /**
   * Fetch all documents in our index.
   * @return {DocumentItem[]}
   */
  get documents () {
    var documents = []
    for (let key in this._documents) {
      documents.push(this._documents[key])
    }
    return documents.sort((a, b) => {
      if (a.modifiedAt < b.modifiedAt) return 1
      if (a.modifiedAt > b.modifiedAt) return -1
      return 0
    })
  }
}

module.exports = NotesStore
<|endoftext|>


<commit_before>/* @flow */

import { CompositeDisposable } from 'atom'
import Delegate from './delegate'
import PanelDock from './dock'
import type { LinterMessage } from '../types'

class Panel {
  panel: ?PanelDock;
  element: HTMLElement;
  delegate: Delegate;
  messages: Array<LinterMessage>;
  deactivating: boolean;
  subscriptions: CompositeDisposable;
  showPanelConfig: boolean;
  hidePanelWhenEmpty: boolean;
  showPanelStateMessages: boolean;
  activationTimer: number;
  constructor() {
    this.panel = null
    this.element = document.createElement('div')
    this.delegate = new Delegate()
    this.messages = []
    this.deactivating = false
    this.subscriptions = new CompositeDisposable()
    this.showPanelStateMessages = false

    this.subscriptions.add(this.delegate)
    this.subscriptions.add(atom.config.observe('linter-ui-default.hidePanelWhenEmpty', (hidePanelWhenEmpty) => {
      this.hidePanelWhenEmpty = hidePanelWhenEmpty
      this.refresh()
    }))
    this.subscriptions.add(atom.workspace.onDidDestroyPaneItem(({ item: paneItem }) => {
      if (paneItem instanceof PanelDock && !this.deactivating) {
        this.panel = null
        atom.config.set('linter-ui-default.showPanel', false)
      }
    }))
    this.subscriptions.add(atom.config.observe('linter-ui-default.showPanel', (showPanel) => {
      this.showPanelConfig = showPanel
      this.refresh()
    }))
    this.subscriptions.add(atom.workspace.observeActivePaneItem(() => {
      this.showPanelStateMessages = !!this.delegate.filteredMessages.length
      this.refresh()
    }))
    this.activationTimer = window.requestIdleCallback(() => {
      this.activate()
    })
  }
  async activate() {
    if (this.panel) {
      return
    }
    this.panel = new PanelDock(this.delegate)
    await atom.workspace.open(this.panel, {
      activatePane: false,
      activateItem: false,
      searchAllPanes: true,
    })
    this.update()
    this.refresh()
  }
  update(newMessages: ?Array<LinterMessage> = null): void {
    if (newMessages) {
      this.messages = newMessages
    }
    this.delegate.update(this.messages)
    this.showPanelStateMessages = !!this.delegate.filteredMessages.length
    this.refresh()
  }
  async refresh() {
    if (this.panel === null) {
      if (this.showPanelConfig) {
        await this.activate()
      }
      return
    }
    const paneContainer = atom.workspace.paneContainerForItem(this.panel)
    if (!paneContainer || paneContainer.location !== 'bottom') {
      return
    }
    if (
      (this.showPanelConfig) &&
      (!this.hidePanelWhenEmpty || this.showPanelStateMessages)
    ) {
      paneContainer.show()
    } else {
      paneContainer.hide()
    }
  }
  dispose() {
    this.deactivating = true
    if (this.panel) {
      this.panel.dispose()
    }
    this.subscriptions.dispose()
    window.cancelIdleCallback(this.activationTimer)
  }
}

module.exports = Panel
<commit_msg>:bug: Only hide/show panel when linter dock is active<commit_after>/* @flow */

import { CompositeDisposable } from 'atom'
import Delegate from './delegate'
import PanelDock from './dock'
import type { LinterMessage } from '../types'

class Panel {
  panel: ?PanelDock;
  element: HTMLElement;
  delegate: Delegate;
  messages: Array<LinterMessage>;
  deactivating: boolean;
  subscriptions: CompositeDisposable;
  showPanelConfig: boolean;
  hidePanelWhenEmpty: boolean;
  showPanelStateMessages: boolean;
  activationTimer: number;
  constructor() {
    this.panel = null
    this.element = document.createElement('div')
    this.delegate = new Delegate()
    this.messages = []
    this.deactivating = false
    this.subscriptions = new CompositeDisposable()
    this.showPanelStateMessages = false

    this.subscriptions.add(this.delegate)
    this.subscriptions.add(atom.config.observe('linter-ui-default.hidePanelWhenEmpty', (hidePanelWhenEmpty) => {
      this.hidePanelWhenEmpty = hidePanelWhenEmpty
      this.refresh()
    }))
    this.subscriptions.add(atom.workspace.onDidDestroyPaneItem(({ item: paneItem }) => {
      if (paneItem instanceof PanelDock && !this.deactivating) {
        this.panel = null
        atom.config.set('linter-ui-default.showPanel', false)
      }
    }))
    this.subscriptions.add(atom.config.observe('linter-ui-default.showPanel', (showPanel) => {
      this.showPanelConfig = showPanel
      this.refresh()
    }))
    this.subscriptions.add(atom.workspace.observeActivePaneItem(() => {
      this.showPanelStateMessages = !!this.delegate.filteredMessages.length
      this.refresh()
    }))
    this.activationTimer = window.requestIdleCallback(() => {
      this.activate()
    })
  }
  async activate() {
    if (this.panel) {
      return
    }
    this.panel = new PanelDock(this.delegate)
    await atom.workspace.open(this.panel, {
      activatePane: false,
      activateItem: false,
      searchAllPanes: true,
    })
    this.update()
    this.refresh()
  }
  update(newMessages: ?Array<LinterMessage> = null): void {
    if (newMessages) {
      this.messages = newMessages
    }
    this.delegate.update(this.messages)
    this.showPanelStateMessages = !!this.delegate.filteredMessages.length
    this.refresh()
  }
  async refresh() {
    if (this.panel === null) {
      if (this.showPanelConfig) {
        await this.activate()
      }
      return
    }
    const paneContainer = atom.workspace.paneContainerForItem(this.panel)
    if (!paneContainer || paneContainer.location !== 'bottom' || paneContainer.getActivePaneItem() !== this.panel) {
      return
    }
    if (
      (this.showPanelConfig) &&
      (!this.hidePanelWhenEmpty || this.showPanelStateMessages)
    ) {
      paneContainer.show()
    } else {
      paneContainer.hide()
    }
  }
  dispose() {
    this.deactivating = true
    if (this.panel) {
      this.panel.dispose()
    }
    this.subscriptions.dispose()
    window.cancelIdleCallback(this.activationTimer)
  }
}

module.exports = Panel
<|endoftext|>


<commit_before>'use strict';

var util = require('util');
var _ = require('lodash');

var Partitioner = function () {};

var DefaultPartitioner = function () {};
util.inherits(DefaultPartitioner, Partitioner);

DefaultPartitioner.prototype.getPartition = function (partitions) {
  if (partitions && _.isArray(partitions) && partitions.length > 0) {
    return partitions[0];
  } else {
    return 0;
  }
};

var CyclicPartitioner = function () {
  this.c = 0;
};
util.inherits(CyclicPartitioner, Partitioner);

CyclicPartitioner.prototype.getPartition = function (partitions) {
  if (_.isEmpty(partitions)) return 0;
  return partitions[ this.c++ % partitions.length ];
};

var RandomPartitioner = function () {};
util.inherits(RandomPartitioner, Partitioner);

RandomPartitioner.prototype.getPartition = function (partitions) {
  return partitions[Math.floor(Math.random() * partitions.length)];
};

var KeyedPartitioner = function () {};
util.inherits(KeyedPartitioner, Partitioner);

// Taken from oid package (Dan Bornstein)
// Copyright The Obvious Corporation.
KeyedPartitioner.prototype.hashCode = function (string) {
  var hash = 0;
  var length = string.length;

  for (var i = 0; i < length; i++) {
    hash = ((hash * 31) + string.charCodeAt(i)) & 0x7fffffff;
  }

  return (hash === 0) ? 1 : hash;
};

KeyedPartitioner.prototype.getPartition = function (partitions, key) {
  key = key || '';

  var index = this.hashCode(key) % partitions.length;
  return partitions[index];
};

var CustomPartitioner = function (partitioner) {
  this.getPartition = partitioner;
};
util.inherits(CustomPartitioner, Partitioner);

module.exports.DefaultPartitioner = DefaultPartitioner;
module.exports.CyclicPartitioner = CyclicPartitioner;
module.exports.RandomPartitioner = RandomPartitioner;
module.exports.KeyedPartitioner = KeyedPartitioner;
module.exports.CustomPartitioner = CustomPartitioner;
<commit_msg>Cast key to string in hashCode function for KeyedPartitioner (#870)<commit_after>'use strict';

var util = require('util');
var _ = require('lodash');

var Partitioner = function () { };

var DefaultPartitioner = function () { };
util.inherits(DefaultPartitioner, Partitioner);

DefaultPartitioner.prototype.getPartition = function (partitions) {
  if (partitions && _.isArray(partitions) && partitions.length > 0) {
    return partitions[0];
  } else {
    return 0;
  }
};

var CyclicPartitioner = function () {
  this.c = 0;
};
util.inherits(CyclicPartitioner, Partitioner);

CyclicPartitioner.prototype.getPartition = function (partitions) {
  if (_.isEmpty(partitions)) return 0;
  return partitions[this.c++ % partitions.length];
};

var RandomPartitioner = function () { };
util.inherits(RandomPartitioner, Partitioner);

RandomPartitioner.prototype.getPartition = function (partitions) {
  return partitions[Math.floor(Math.random() * partitions.length)];
};

var KeyedPartitioner = function () { };
util.inherits(KeyedPartitioner, Partitioner);

// Taken from oid package (Dan Bornstein)
// Copyright The Obvious Corporation.
KeyedPartitioner.prototype.hashCode = function (string) {
  var hash = 0;
  if (string) {
    var length = string.toString().length;

    for (var i = 0; i < length; i++) {
      hash = ((hash * 31) + string.charCodeAt(i)) & 0x7fffffff;
    }
  }

  return (hash === 0) ? 1 : hash;
};

KeyedPartitioner.prototype.getPartition = function (partitions, key) {
  key = key || '';

  var index = this.hashCode(key) % partitions.length;
  return partitions[index];
};

var CustomPartitioner = function (partitioner) {
  this.getPartition = partitioner;
};
util.inherits(CustomPartitioner, Partitioner);

module.exports.DefaultPartitioner = DefaultPartitioner;
module.exports.CyclicPartitioner = CyclicPartitioner;
module.exports.RandomPartitioner = RandomPartitioner;
module.exports.KeyedPartitioner = KeyedPartitioner;
module.exports.CustomPartitioner = CustomPartitioner;
<|endoftext|>


<commit_before>// General storage interface.
// Can turn any polyclay model into a persistable model.

var
	_ = require('lodash'),
	assert = require('assert'),
	async = require('async'),
	path = require('path'),
	querystring = require('querystring'),
	util = require('util'),
	putil = require('./util')
	;

//-----------------------------------------------------------------

function persist(modelfunc, keyfield)
{
	/*jshint newcap:false */
	assert(typeof modelfunc === 'function');

	if (!modelfunc.prototype.__properties && !modelfunc.prototype.serialize)
		throw(new Error('persist only accepts polyclay models'));

	// We must supply either a keyfield parameter or already have a 'key' property.
	var keyprop = Object.getOwnPropertyDescriptor(modelfunc.prototype, 'key');
	if (!keyprop)
	{
		if (!keyfield)
			keyfield = '_id';

		modelfunc.prototype.__defineGetter__('key', function()
		{
			return this[keyfield];
		});
		modelfunc.prototype.__defineSetter__('key', function(v)
		{
			this[keyfield] = v;
		});
	}

	modelfunc.name = "change me";

	// methods on the model class

	modelfunc.defineAttachment = function(name, mimetype)
	{
		modelfunc.prototype.__types[name] = mimetype;

		modelfunc.prototype['fetch_' + name] = function(callback)
		{
			var self = this;
			if (self.__attachments[name] && self.__attachments[name].body)
				return callback(null, self.__attachments[name].body);
			if (!self.__attachments[name])
				self.__attachments[name] = {};
			modelfunc.adapter.attachment(self.key, name, function(err, body)
			{
				if (err) return callback(err);
				if (modelfunc.prototype.__types[name].indexOf('text') === 0)
					body = body ? body.toString('utf8') : '';
				self.__attachments[name].body = body;
				callback(null, body);
			});
		};

		modelfunc.prototype['set_' + name] = function(data)
		{
			if (!this.__attachments[name])
				this.__attachments[name] = {};

			this.__attachments[name].body = data;
			this.__attachments[name].length = putil.dataLength(data);
			this.__attachments[name].stub = false;
			this.__attachments[name].__dirty = true;
			this.__attachments[name].content_type = this.__types[name];
		};
		modelfunc.prototype.__defineSetter__(name, modelfunc.prototype['set_' + name]);

		modelfunc.prototype.__defineGetter__(name, function()
		{
			return this.__attachments[name] ? this.__attachments[name].body : '';
		});
	};

	modelfunc.setStorage = function(options, Adapter)
	{
		modelfunc.adapter = new Adapter();
		modelfunc.adapter.configure(options, modelfunc);
	};

	modelfunc.get = function(key, callback)
	{
		if (Array.isArray(key))
			return modelfunc.getBatch(key, callback);

		modelfunc.adapter.get(key, function(err, object)
		{
			callback(err, object);
		});
	};

	modelfunc.getBatch = function(keys, callback)
	{
		var object, results = [];
		modelfunc.adapter.getBatch(keys, function(err, results)
		{
			callback(err, results);
		});
	};

	modelfunc.all = function(callback)
	{
		var results = [], ids = [];
		modelfunc.adapter.all(function (err, ids)
		{
			if (err) return callback(err);
			modelfunc.getBatch(ids, callback);
		});
	};

	modelfunc.provision = function(callback)
	{
		modelfunc.adapter.provision(callback);
	};

	modelfunc.constructMany = function(documents, callback)
	{
		if (!documents || documents.length === 0)
			return callback(null, []);

		var results = _.map(documents, function(doc)
		{
			return modelfunc.adapter.inflate(doc);
		});
		callback(null, results);
	};

	modelfunc.destroyMany = function(objects, callback)
	{
		if (!objects || !Array.isArray(objects))
			return callback(null);

		modelfunc.adapter.destroyMany(objects, callback);
	};

	// methods on model objects

	modelfunc.prototype._serializeAttachments = function()
	{
		var result = _.clone(this.__attachments);
		var body, name;
		var keys = Object.keys(this.__attachments);

		for (var i = 0; i < keys.length; i++)
		{
			name = keys[i];

			if (this.__attachments[name].__dirty)
			{
				if (this.__attachments[name].body === null)
					body = '';
				else if (this.__attachments[name].body instanceof Buffer)
					body = this.__attachments[name].body.toString('base64');
				else
					body = new Buffer(this.__attachments[name].body).toString('base64');

				result[name].stub = false;
				result[name].data = body;
				result[name].content_type = this.__types[name];
			}
		}

		return result;
	};

	modelfunc.prototype.save = function(callback)
	{
		var self = this;

		if (self.beforeSave)
			self.beforeSave();

		var serialized = self.serialize();
		serialized._attachments = self._serializeAttachments();

		if (self.__new)
		{
			modelfunc.adapter.save(self, serialized, function(err, response)
			{
				if (err) return callback(err);
				self.clearDirty();
				self.__new = false;

				if (self.afterSave)
					self.afterSave();

				callback(null, response);
			});
		}
		else
		{
			if (!self.isDirty() && !serialized._attachments) return callback(null, 'OK');
			modelfunc.adapter.update(self, serialized, function(err, response)
			{
				if (err) return callback(err);
				self.clearDirty();
				if (self.afterSave)
					self.afterSave();

				callback(null, 'OK');
			});
		}
	};

	modelfunc.prototype.destroy = function(callback)
	{
		var self = this;
		if (!self.key)
			return callback(new Error('cannot destroy object without an id'));
		if (self.destroyed)
			return callback(new Error('object already destroyed'));

		if (self.beforeDestroy)
			self.beforeDestroy();

		modelfunc.adapter.remove(self, function(err, response)
		{
			if (err) return callback(err, false);
			self.destroyed = true;
			callback(null, true);
		});
	};

	modelfunc.prototype.initFromStorage = function(struct)
	{
		if (struct._attachments)
		{
			this.handleAttachments(struct._attachments);
			delete struct._attachments;
		}

		this.update(struct);
		this.__new = false;
		this.destroyed = false;
		this.clearDirty();

		if (this.afterLoad)
			this.afterLoad();
	};

	modelfunc.prototype.handleAttachments = function(attachments)
	{
		var name, attach;
		if (!attachments || (typeof attachments !== 'object'))
			return;

		var names = Object.keys(attachments);
		for (var i = 0; i < names.length; i++)
		{
			name = names[i];
			attach = attachments[name];
			this.__attachments[name] = attach;
		}
	};

	modelfunc.prototype.saveAttachment = function(name, callback)
	{
		var self = this;
		var attach = _.clone(this.__attachments[name]);
		delete attach.__dirty;
		delete attach.data;
		delete attach.stub;
		attach.name = name;

		modelfunc.adapter.saveAttachment(self, attach, function(err, response)
		{
			if (err) return callback(err);
			self.__attachments[name].__dirty = false;
			callback(null, response);
		});
	};

	modelfunc.prototype.removeAttachment = function(name, callback)
	{
		var self = this;
		delete this.__attachments[name];

		modelfunc.adapter.removeAttachment(this, name, function(err, response)
		{
			callback(err, !err);
		});
	};

	modelfunc.prototype.merge = function(properties, callback)
	{
		var self = this;
		self.update(properties);
		modelfunc.adapter.merge(self.key, properties, function(err, response)
		{
			// TODO error handling
			self.clearDirty();
			callback(err, response);
		});
	};

	modelfunc.prototype.isDirty = function()
	{
		var result = false;
		result |= this.__dirty;
		if (this.__attachments)
		{
			_.each(this.__attachments, function(value, key)
			{
				result |= value.__dirty;
			});
		}

		return result === 1;
	};

	modelfunc.prototype.clearDirty = function()
	{
		this.__dirty = false;
		this.__attributesPrev = {};
		if (this.__attachments)
		{
			_.each(this.__attachments, function(value, key)
			{
				value.__dirty = false;
			});
		}
	};

}

exports.persist = persist;
<commit_msg>Don't attempt to fetch a non-existent id.<commit_after>// General storage interface.
// Can turn any polyclay model into a persistable model.

var
	_ = require('lodash'),
	assert = require('assert'),
	async = require('async'),
	path = require('path'),
	querystring = require('querystring'),
	util = require('util'),
	putil = require('./util')
	;

//-----------------------------------------------------------------

function persist(modelfunc, keyfield)
{
	/*jshint newcap:false */
	assert(typeof modelfunc === 'function');

	if (!modelfunc.prototype.__properties && !modelfunc.prototype.serialize)
		throw(new Error('persist only accepts polyclay models'));

	// We must supply either a keyfield parameter or already have a 'key' property.
	var keyprop = Object.getOwnPropertyDescriptor(modelfunc.prototype, 'key');
	if (!keyprop)
	{
		if (!keyfield)
			keyfield = '_id';

		modelfunc.prototype.__defineGetter__('key', function()
		{
			return this[keyfield];
		});
		modelfunc.prototype.__defineSetter__('key', function(v)
		{
			this[keyfield] = v;
		});
	}

	modelfunc.name = "change me";

	// methods on the model class

	modelfunc.defineAttachment = function(name, mimetype)
	{
		modelfunc.prototype.__types[name] = mimetype;

		modelfunc.prototype['fetch_' + name] = function(callback)
		{
			var self = this;
			if (self.__attachments[name] && self.__attachments[name].body)
				return callback(null, self.__attachments[name].body);
			if (!self.__attachments[name])
				self.__attachments[name] = {};
			modelfunc.adapter.attachment(self.key, name, function(err, body)
			{
				if (err) return callback(err);
				if (modelfunc.prototype.__types[name].indexOf('text') === 0)
					body = body ? body.toString('utf8') : '';
				self.__attachments[name].body = body;
				callback(null, body);
			});
		};

		modelfunc.prototype['set_' + name] = function(data)
		{
			if (!this.__attachments[name])
				this.__attachments[name] = {};

			this.__attachments[name].body = data;
			this.__attachments[name].length = putil.dataLength(data);
			this.__attachments[name].stub = false;
			this.__attachments[name].__dirty = true;
			this.__attachments[name].content_type = this.__types[name];
		};
		modelfunc.prototype.__defineSetter__(name, modelfunc.prototype['set_' + name]);

		modelfunc.prototype.__defineGetter__(name, function()
		{
			return this.__attachments[name] ? this.__attachments[name].body : '';
		});
	};

	modelfunc.setStorage = function(options, Adapter)
	{
		modelfunc.adapter = new Adapter();
		modelfunc.adapter.configure(options, modelfunc);
	};

	modelfunc.get = function(key, callback)
	{
		if (!key || !key.length)
			return callback(null, null);

		if (Array.isArray(key))
			return modelfunc.getBatch(key, callback);

		modelfunc.adapter.get(key, function(err, object)
		{
			callback(err, object);
		});
	};

	modelfunc.getBatch = function(keys, callback)
	{
		var object, results = [];
		modelfunc.adapter.getBatch(keys, function(err, results)
		{
			callback(err, results);
		});
	};

	modelfunc.all = function(callback)
	{
		var results = [], ids = [];
		modelfunc.adapter.all(function (err, ids)
		{
			if (err) return callback(err);
			modelfunc.getBatch(ids, callback);
		});
	};

	modelfunc.provision = function(callback)
	{
		modelfunc.adapter.provision(callback);
	};

	modelfunc.constructMany = function(documents, callback)
	{
		if (!documents || documents.length === 0)
			return callback(null, []);

		var results = _.map(documents, function(doc)
		{
			return modelfunc.adapter.inflate(doc);
		});
		callback(null, results);
	};

	modelfunc.destroyMany = function(objects, callback)
	{
		if (!objects || !Array.isArray(objects))
			return callback(null);

		modelfunc.adapter.destroyMany(objects, callback);
	};

	// methods on model objects

	modelfunc.prototype._serializeAttachments = function()
	{
		var result = _.clone(this.__attachments);
		var body, name;
		var keys = Object.keys(this.__attachments);

		for (var i = 0; i < keys.length; i++)
		{
			name = keys[i];

			if (this.__attachments[name].__dirty)
			{
				if (this.__attachments[name].body === null)
					body = '';
				else if (this.__attachments[name].body instanceof Buffer)
					body = this.__attachments[name].body.toString('base64');
				else
					body = new Buffer(this.__attachments[name].body).toString('base64');

				result[name].stub = false;
				result[name].data = body;
				result[name].content_type = this.__types[name];
			}
		}

		return result;
	};

	modelfunc.prototype.save = function(callback)
	{
		var self = this;

		if (self.beforeSave)
			self.beforeSave();

		var serialized = self.serialize();
		serialized._attachments = self._serializeAttachments();

		if (self.__new)
		{
			modelfunc.adapter.save(self, serialized, function(err, response)
			{
				if (err) return callback(err);
				self.clearDirty();
				self.__new = false;

				if (self.afterSave)
					self.afterSave();

				callback(null, response);
			});
		}
		else
		{
			if (!self.isDirty() && !serialized._attachments) return callback(null, 'OK');
			modelfunc.adapter.update(self, serialized, function(err, response)
			{
				if (err) return callback(err);
				self.clearDirty();
				if (self.afterSave)
					self.afterSave();

				callback(null, 'OK');
			});
		}
	};

	modelfunc.prototype.destroy = function(callback)
	{
		var self = this;
		if (!self.key)
			return callback(new Error('cannot destroy object without an id'));
		if (self.destroyed)
			return callback(new Error('object already destroyed'));

		if (self.beforeDestroy)
			self.beforeDestroy();

		modelfunc.adapter.remove(self, function(err, response)
		{
			if (err) return callback(err, false);
			self.destroyed = true;
			callback(null, true);
		});
	};

	modelfunc.prototype.initFromStorage = function(struct)
	{
		if (struct._attachments)
		{
			this.handleAttachments(struct._attachments);
			delete struct._attachments;
		}

		this.update(struct);
		this.__new = false;
		this.destroyed = false;
		this.clearDirty();

		if (this.afterLoad)
			this.afterLoad();
	};

	modelfunc.prototype.handleAttachments = function(attachments)
	{
		var name, attach;
		if (!attachments || (typeof attachments !== 'object'))
			return;

		var names = Object.keys(attachments);
		for (var i = 0; i < names.length; i++)
		{
			name = names[i];
			attach = attachments[name];
			this.__attachments[name] = attach;
		}
	};

	modelfunc.prototype.saveAttachment = function(name, callback)
	{
		var self = this;
		var attach = _.clone(this.__attachments[name]);
		delete attach.__dirty;
		delete attach.data;
		delete attach.stub;
		attach.name = name;

		modelfunc.adapter.saveAttachment(self, attach, function(err, response)
		{
			if (err) return callback(err);
			self.__attachments[name].__dirty = false;
			callback(null, response);
		});
	};

	modelfunc.prototype.removeAttachment = function(name, callback)
	{
		var self = this;
		delete this.__attachments[name];

		modelfunc.adapter.removeAttachment(this, name, function(err, response)
		{
			callback(err, !err);
		});
	};

	modelfunc.prototype.merge = function(properties, callback)
	{
		var self = this;
		self.update(properties);
		modelfunc.adapter.merge(self.key, properties, function(err, response)
		{
			// TODO error handling
			self.clearDirty();
			callback(err, response);
		});
	};

	modelfunc.prototype.isDirty = function()
	{
		var result = false;
		result |= this.__dirty;
		if (this.__attachments)
		{
			_.each(this.__attachments, function(value, key)
			{
				result |= value.__dirty;
			});
		}

		return result === 1;
	};

	modelfunc.prototype.clearDirty = function()
	{
		this.__dirty = false;
		this.__attributesPrev = {};
		if (this.__attachments)
		{
			_.each(this.__attachments, function(value, key)
			{
				value.__dirty = false;
			});
		}
	};

}

exports.persist = persist;
<|endoftext|>


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

var Cord = Backbone.Cord;

Cord.View.prototype.createSVGElement = Cord.createSVGElement = function(tagIdClasses, attrs) {
	var svgAttrs = {};
	if(!(typeof attrs === 'string' || attrs.nodeType === 1)) {
		for(var attr in attrs)
			if(attrs.hasOwnProperty(attr))
				svgAttrs[(attr === 'href') ? 'xlink:' + attr : attr] = attrs[attr];
	}
	var args = Array.prototype.slice.call(arguments);
	args.splice(0, 2, 'svg:' + tagIdClasses, svgAttrs);
	return this.createElement.apply(this, args);
};

var SVG_NS = 'http://www.w3.org/2000/svg';
var XLINK_NS = 'http://www.w3.org/1999/xlink';

Cord.plugins.push({
	name: 'svg',
	tag: function(context, tag) {
		if(tag.substr(0, 4) === 'svg:') {
			tag = tag.substr(4);
			return document.createElementNS(SVG_NS, tag);
		}
	},
	attrs: function(context, attrs) {
		var el = context.el;
		if(el.namespaceURI === SVG_NS) {
			for(var attr in attrs) {
				if(attrs.hasOwnProperty(attr)) {
					if(attr.substr(0, 6) === 'xlink:') {
						el.setAttributeNS(XLINK_NS, attr.substr(6), attrs[attr]);
						delete attrs[attr];
					}
					else if(attr.substr(0, 4) === 'xml:') {
						el.setAttributeNS(SVG_NS, attr.substr(4), attrs[attr]);
						delete attrs[attr];
					}
				}
			}
		}
	}
});

})(((typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global)).Backbone || require('backbone'));
<commit_msg>Updated createSVGElement to be used as unbound h function.<commit_after>;(function(Backbone) {
'use strict';

var Cord = Backbone.Cord;

Cord.View.prototype.createSVGElement = Cord.createSVGElement = function(tagIdClasses, attrs) {
	if(Cord._viewContext && this !== Cord._viewContext)
		return Cord.createSVGElement.apply(Cord._viewContext, arguments);
	var svgAttrs = {};
	if(!(typeof attrs === 'string' || attrs.nodeType === 1)) {
		for(var attr in attrs)
			if(attrs.hasOwnProperty(attr))
				svgAttrs[(attr === 'href') ? 'xlink:' + attr : attr] = attrs[attr];
	}
	var args = Array.prototype.slice.call(arguments);
	args.splice(0, 2, 'svg:' + tagIdClasses, svgAttrs);
	return this.createElement.apply(this, args);
};

var SVG_NS = 'http://www.w3.org/2000/svg';
var XLINK_NS = 'http://www.w3.org/1999/xlink';

Cord.plugins.push({
	name: 'svg',
	tag: function(context, tag) {
		if(tag.substr(0, 4) === 'svg:') {
			tag = tag.substr(4);
			return document.createElementNS(SVG_NS, tag);
		}
	},
	attrs: function(context, attrs) {
		var el = context.el;
		if(el.namespaceURI === SVG_NS) {
			for(var attr in attrs) {
				if(attrs.hasOwnProperty(attr)) {
					if(attr.substr(0, 6) === 'xlink:') {
						el.setAttributeNS(XLINK_NS, attr.substr(6), attrs[attr]);
						delete attrs[attr];
					}
					else if(attr.substr(0, 4) === 'xml:') {
						el.setAttributeNS(SVG_NS, attr.substr(4), attrs[attr]);
						delete attrs[attr];
					}
				}
			}
		}
	}
});

})(((typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global)).Backbone || require('backbone'));
<|endoftext|>


<commit_before>//
// Copy plugins from original scimgateway package to current installation folder
//
var fs = require('fs')

function fsExistsSync(f) {
  try {
    fs.accessSync(f)
    return true
  } catch (e) {
      return false
  }
}

if (fsExistsSync('./node_modules')) return true // global package - quit - no postinstall

if (!fsExistsSync('../../config')) fs.mkdirSync('../../config')
if (!fsExistsSync('../../config/certs')) fs.mkdirSync('../../config/certs')
if (!fsExistsSync('../../config/wsdls')) fs.mkdirSync('../../config/wsdls')
if (!fsExistsSync('../../config/docker')) fs.mkdirSync('../../config/docker')
if (!fsExistsSync('../../lib')) fs.mkdirSync('../../lib')

fs.writeFileSync('../../config/plugin-loki.json', fs.readFileSync('./config/plugin-loki.json'))
fs.writeFileSync('../../config/plugin-restful.json', fs.readFileSync('./config/plugin-restful.json'))
fs.writeFileSync('../../config/plugin-forwardinc.json', fs.readFileSync('./config/plugin-forwardinc.json'))
fs.writeFileSync('../../config/plugin-mssql.json', fs.readFileSync('./config/plugin-mssql.json'))
fs.writeFileSync('../../config/plugin-saphana.json', fs.readFileSync('./config/plugin-saphana.json'))
fs.writeFileSync('../../config/plugin-api.json', fs.readFileSync('./config/plugin-api.json'))
fs.writeFileSync('../../config/plugin-azure-ad.json', fs.readFileSync('./config/plugin-azure-ad.json'))

fs.writeFileSync('../../lib/plugin-loki.js', fs.readFileSync('./lib/plugin-loki.js'))
fs.writeFileSync('../../lib/plugin-restful.js', fs.readFileSync('./lib/plugin-restful.js'))
fs.writeFileSync('../../lib/plugin-forwardinc.js', fs.readFileSync('./lib/plugin-forwardinc.js'))
fs.writeFileSync('../../lib/plugin-mssql.js', fs.readFileSync('./lib/plugin-mssql.js'))
fs.writeFileSync('../../lib/plugin-saphana.js', fs.readFileSync('./lib/plugin-saphana.js'))
fs.writeFileSync('../../lib/plugin-api.js', fs.readFileSync('./lib/plugin-api.js'))
fs.writeFileSync('../../lib/plugin-azure-ad.js', fs.readFileSync('./lib/plugin-azure-ad.js'))

if (!fsExistsSync('../../config/wsdls/GroupService.wsdl'))
  fs.writeFileSync('../../config/wsdls/GroupService.wsdl', fs.readFileSync('./config/wsdls/GroupService.wsdl'))
if (!fsExistsSync('../../config/wsdls/UserService.wsdl'))
  fs.writeFileSync('../../config/wsdls/UserService.wsdl', fs.readFileSync('./config/wsdls/UserService.wsdl'))

fs.writeFileSync('../../config/docker/docker-compose.yml', fs.readFileSync('./config/docker/docker-compose.yml'))
fs.writeFileSync('../../config/docker/Dockerfile', fs.readFileSync('./config/docker/Dockerfile'))
fs.writeFileSync('../../config/docker/DataDockerfile', fs.readFileSync('./config/docker/DataDockerfile'))
fs.writeFileSync('../../config/docker/docker-compose-debug.yml', fs.readFileSync('./config/docker/docker-compose-debug.yml'))

fs.writeFileSync('../../README.html', fs.readFileSync('./README.html'))
fs.writeFileSync('../../LICENSE', fs.readFileSync('./LICENSE'))
if (!fsExistsSync('../../index.js')) fs.writeFileSync('../../index.js', fs.readFileSync('./index.js'))
<commit_msg>keep existing plugin-azure-ad.json<commit_after>//
// Copy plugins from original scimgateway package to current installation folder
//
var fs = require('fs')

function fsExistsSync(f) {
  try {
    fs.accessSync(f)
    return true
  } catch (e) {
      return false
  }
}

if (fsExistsSync('./node_modules')) return true // global package - quit - no postinstall

if (!fsExistsSync('../../config')) fs.mkdirSync('../../config')
if (!fsExistsSync('../../config/certs')) fs.mkdirSync('../../config/certs')
if (!fsExistsSync('../../config/wsdls')) fs.mkdirSync('../../config/wsdls')
if (!fsExistsSync('../../config/docker')) fs.mkdirSync('../../config/docker')
if (!fsExistsSync('../../lib')) fs.mkdirSync('../../lib')

fs.writeFileSync('../../config/plugin-loki.json', fs.readFileSync('./config/plugin-loki.json'))
fs.writeFileSync('../../config/plugin-restful.json', fs.readFileSync('./config/plugin-restful.json'))
fs.writeFileSync('../../config/plugin-forwardinc.json', fs.readFileSync('./config/plugin-forwardinc.json'))
fs.writeFileSync('../../config/plugin-mssql.json', fs.readFileSync('./config/plugin-mssql.json'))
fs.writeFileSync('../../config/plugin-saphana.json', fs.readFileSync('./config/plugin-saphana.json'))
fs.writeFileSync('../../config/plugin-api.json', fs.readFileSync('./config/plugin-api.json'))
if (!fsExistsSync('../../config/plugin-azure-ad.json')) fs.writeFileSync('../../config/plugin-azure-ad.json', fs.readFileSync('./config/plugin-azure-ad.json')) // keep existing

fs.writeFileSync('../../lib/plugin-loki.js', fs.readFileSync('./lib/plugin-loki.js'))
fs.writeFileSync('../../lib/plugin-restful.js', fs.readFileSync('./lib/plugin-restful.js'))
fs.writeFileSync('../../lib/plugin-forwardinc.js', fs.readFileSync('./lib/plugin-forwardinc.js'))
fs.writeFileSync('../../lib/plugin-mssql.js', fs.readFileSync('./lib/plugin-mssql.js'))
fs.writeFileSync('../../lib/plugin-saphana.js', fs.readFileSync('./lib/plugin-saphana.js'))
fs.writeFileSync('../../lib/plugin-api.js', fs.readFileSync('./lib/plugin-api.js'))
fs.writeFileSync('../../lib/plugin-azure-ad.js', fs.readFileSync('./lib/plugin-azure-ad.js'))

if (!fsExistsSync('../../config/wsdls/GroupService.wsdl'))
  fs.writeFileSync('../../config/wsdls/GroupService.wsdl', fs.readFileSync('./config/wsdls/GroupService.wsdl'))
if (!fsExistsSync('../../config/wsdls/UserService.wsdl'))
  fs.writeFileSync('../../config/wsdls/UserService.wsdl', fs.readFileSync('./config/wsdls/UserService.wsdl'))

fs.writeFileSync('../../config/docker/docker-compose.yml', fs.readFileSync('./config/docker/docker-compose.yml'))
fs.writeFileSync('../../config/docker/Dockerfile', fs.readFileSync('./config/docker/Dockerfile'))
fs.writeFileSync('../../config/docker/DataDockerfile', fs.readFileSync('./config/docker/DataDockerfile'))
fs.writeFileSync('../../config/docker/docker-compose-debug.yml', fs.readFileSync('./config/docker/docker-compose-debug.yml'))

fs.writeFileSync('../../README.html', fs.readFileSync('./README.html'))
fs.writeFileSync('../../LICENSE', fs.readFileSync('./LICENSE'))
if (!fsExistsSync('../../index.js')) fs.writeFileSync('../../index.js', fs.readFileSync('./index.js')) // keep existing
<|endoftext|>