<commit_before>var ad = require('../config/ad.json');

var ldapClient = require('../lib/ldap-client');

var mongoose = require('mongoose');
var User = mongoose.model('User');

var auth = require('../lib/auth');

var Roles = ['manager', 'admin'];

function updateUserProfile(user, res) {
  var searchFilter = ad.searchFilter.replace('_id', user._id);
  var opts = {
    filter: searchFilter,
    attributes: ad.objAttributes,
    scope: 'sub'
  };
  ldapClient.search(ad.searchBase, opts, false, function (err, result) {
    if (err) {
      return res.json(500, err);
    }
    if (result.length === 0) {
      return res.json(500, {
        error: user._id + ' is not found!'
      });
    }
    if (result.length > 1) {
      return res.json(500, {
        error: user._id + ' is not unique!'
      });
    }
    user.update({
      name: result[0].displayName,
      email: result[0].mail,
      office: result[0].physicalDeliveryOfficeName,
      phone: result[0].telephoneNumber,
      mobile: result[0].mobile
    }, function (err) {
      if (err) {
        return res.json(500, err);
      }
      res.send(204);
    });
  });
}

function addUser(req, res) {
  var nameFilter = ad.nameFilter.replace('_name', req.body.name);
  var opts = {
    filter: nameFilter,
    attributes: ad.objAttributes,
    scope: 'sub'
  };

  ldapClient.search(ad.searchBase, opts, false, function (err, result) {
    if (err) {
      console.error(err.name + ' : ' + err.message);
      return res.json(500, err);
    }

    if (result.length === 0) {
      return res.send(404, req.body.name + ' is not found in AD!');
    }

    if (result.length > 1) {
      return res.send(400, req.body.name + ' is not unique!');
    }
    var roles = [];
    if (req.body.manager) {
      roles.push('manager');
    }
    if (req.body.admin) {
      roles.push('admin');
    }
    var user = new User({
      _id: result[0].sAMAccountName.toLowerCase(),
      name: result[0].displayName,
      email: result[0].mail,
      office: result[0].physicalDeliveryOfficeName,
      phone: result[0].telephoneNumber,
      mobile: result[0].mobile,
      roles: roles
    });

    user.save(function (err, newUser) {
      if (err) {
        console.error(err.msg);
        return res.send(500, err.msg);
      }

      var url = req.protocol + '://' + req.get('host') + '/users/' + newUser._id;
      res.set('Location', url);
      res.send(201, 'The new user is at ' + url);
    });

  });
}

module.exports = function (app) {

  app.get('/users/', auth.ensureAuthenticated, function (req, res) {
    if (req.session.roles === undefined || req.session.roles.indexOf('admin') === -1) {
      return res.send(403, 'only admin allowed');
    }
    return res.render('users', {
      prefix: req.proxied ? req.proxied_prefix : ''
    });
  });

  app.get('/usernames/:name', auth.ensureAuthenticated, function (req, res) {
    User.findOne({
      name: req.params.name
    }).lean().exec(function (err, user) {
      if (err) {
        console.error(err.msg);
        return res.send(500, err.msg);
      }
      if (user) {
        return res.render('user', {
          user: user,
          myRoles: req.session.roles,
          prefix: req.proxied ? req.proxied_prefix : ''
        });
      }
      return res.send(404, req.params.name + ' not found');
    });
  });

  app.post('/users/', auth.ensureAuthenticated, function (req, res) {

    if (req.session.roles === undefined || req.session.roles.indexOf('admin') === -1) {
      return res.send(403, 'only admin allowed');
    }

    if (!req.body.name) {
      return res.send(400, 'need to know name');
    }

    // check if already in db
    User.findOne({
      name: req.body.name
    }).lean().exec(function (err, user) {
      if (err) {
        return res.send(500, err.msg);
      }
      if (user) {
        var url = req.protocol + '://' + req.get('host') + '/users/' + user._id;
        return res.send(200, 'The user is at ' + url);
      }
      addUser(req, res);
    });

  });

  app.get('/users/json', auth.ensureAuthenticated, function (req, res) {
    if (req.session.roles === undefined || req.session.roles.indexOf('admin') === -1) {
      return res.send(403, "You are not authorized to access this resource. ");
    }
    User.find().lean().exec(function (err, users) {
      if (err) {
        console.error(err.msg);
        return res.json(500, {
          error: err.msg
        });
      }
      res.json(users);
    });
  });

  app.get('/users/:id', auth.ensureAuthenticated, function (req, res) {
    User.findOne({
      _id: req.params.id
    }).lean().exec(function (err, user) {
      if (err) {
        console.error(err.msg);
        return res.send(500, err.msg);
      }
      if (user) {
        return res.render('user', {
          user: user,
          myRoles: req.session.roles,
          prefix: req.proxied ? req.proxied_prefix : ''
        });
      }
      return res.send(404, req.params.id + ' has never logged into the application.');
    });
  });

  app.put('/users/:id', auth.ensureAuthenticated, function (req, res) {
    if (req.session.roles === undefined || req.session.roles.indexOf('admin') === -1) {
      return res.send(403, "You are not authorized to access this resource. ");
    }
    if (!req.is('json')) {
      return res.json(415, {
        error: 'json request expected.'
      });
    }
    User.findOneAndUpdate({
      _id: req.params.id
    }, req.body).lean().exec(function (err, user) {
      if (err) {
        console.error(err.msg);
        return res.json(500, {
          error: err.msg
        });
      }
      res.send(204);
    });
  });

  // get from the db not ad
  app.get('/users/:id/json', auth.ensureAuthenticated, function (req, res) {
    User.findOne({
      _id: req.params.id
    }).lean().exec(function (err, user) {
      if (err) {
        console.error(err.msg);
        return res.json(500, {
          error: err.mesage
        });
      }
      return res.json(user);
    });
  });

  app.get('/users/:id/refresh', auth.ensureAuthenticated, function (req, res) {
    if (req.session.roles === undefined || req.session.roles.indexOf('admin') === -1) {
      return res.send(403, "You are not authorized to access this resource. ");
    }
    User.findOne({
      _id: req.params.id
    }).exec(function (err, user) {
      if (err) {
        console.error(err.msg);
        return res.send(500, err.msg);
      }
      if (user) {
        updateUserProfile(user, res);
      } else {
        return res.send(404, req.params.id + ' is not in the application.');
      }
    });
  });

  // resource /adusers

  app.get('/adusers/:id', auth.ensureAuthenticated, function (req, res) {

    var searchFilter = ad.searchFilter.replace('_id', req.params.id);
    var opts = {
      filter: searchFilter,
      attributes: ad.objAttributes,
      scope: 'sub'
    };
    ldapClient.search(ad.searchBase, opts, false, function (err, result) {
      if (err) {
        return res.json(500, err);
      }
      if (result.length === 0) {
        return res.json(500, {
          error: req.params.id + ' is not found!'
        });
      }
      if (result.length > 1) {
        return res.json(500, {
          error: req.params.id + ' is not unique!'
        });
      }

      return res.json(result[0]);
    });

  });

  app.get('/adusers/:id/photo', auth.ensureAuthenticated, function (req, res) {

    var searchFilter = ad.searchFilter.replace('_id', req.params.id);
    var opts = {
      filter: searchFilter,
      attributes: ad.rawAttributes,
      scope: 'sub'
    };
    ldapClient.search(ad.searchBase, opts, true, function (err, result) {
      if (err) {
        return res.json(500, err);
      }
      if (result.length === 0) {
        return res.json(500, {
          error: req.params.id + ' is not found!'
        });
      }
      if (result.length > 1) {
        return res.json(500, {
          error: req.params.id + ' is not unique!'
        });
      }
      res.set('Content-Type', 'image/jpeg');
      return res.send(result[0].thumbnailPhoto);
    });

  });

  app.get('/adusernames', auth.ensureAuthenticated, function (req, res) {
    // var query = req.param('term');
    var query = req.query.term;
    var nameFilter, opts;
    if (query && query.length > 0) {
      nameFilter = ad.nameFilter.replace('_name', query + '*');
    } else {
      nameFilter = ad.nameFilter.replace('_name', '*');
    }
    opts = {
      filter: nameFilter,
      attributes: ['displayName'],
      scope: 'sub'
    };
    ldapClient.search(ad.searchBase, opts, false, function (err, result) {
      if (err) {
        return res.json(500, JSON.stringify(err));
      }
      if (result.length === 0) {
        return res.json(500, {
          error: 'Names starting with ' + query + ' are not found!'
        });
      }
      return res.json(result);
    });
  });
};
<commit_msg>update proxied url in user route<commit_after>var ad = require('../config/ad.json');

var ldapClient = require('../lib/ldap-client');

var mongoose = require('mongoose');
var User = mongoose.model('User');

var auth = require('../lib/auth');
var authConfig = require('../config/auth.json');

var Roles = ['manager', 'admin'];

function updateUserProfile(user, res) {
  var searchFilter = ad.searchFilter.replace('_id', user._id);
  var opts = {
    filter: searchFilter,
    attributes: ad.objAttributes,
    scope: 'sub'
  };
  ldapClient.search(ad.searchBase, opts, false, function (err, result) {
    if (err) {
      return res.json(500, err);
    }
    if (result.length === 0) {
      return res.json(500, {
        error: user._id + ' is not found!'
      });
    }
    if (result.length > 1) {
      return res.json(500, {
        error: user._id + ' is not unique!'
      });
    }
    user.update({
      name: result[0].displayName,
      email: result[0].mail,
      office: result[0].physicalDeliveryOfficeName,
      phone: result[0].telephoneNumber,
      mobile: result[0].mobile
    }, function (err) {
      if (err) {
        return res.json(500, err);
      }
      res.send(204);
    });
  });
}

function addUser(req, res) {
  var nameFilter = ad.nameFilter.replace('_name', req.body.name);
  var opts = {
    filter: nameFilter,
    attributes: ad.objAttributes,
    scope: 'sub'
  };

  ldapClient.search(ad.searchBase, opts, false, function (err, result) {
    if (err) {
      console.error(err.name + ' : ' + err.message);
      return res.json(500, err);
    }

    if (result.length === 0) {
      return res.send(404, req.body.name + ' is not found in AD!');
    }

    if (result.length > 1) {
      return res.send(400, req.body.name + ' is not unique!');
    }
    var roles = [];
    if (req.body.manager) {
      roles.push('manager');
    }
    if (req.body.admin) {
      roles.push('admin');
    }
    var user = new User({
      _id: result[0].sAMAccountName.toLowerCase(),
      name: result[0].displayName,
      email: result[0].mail,
      office: result[0].physicalDeliveryOfficeName,
      phone: result[0].telephoneNumber,
      mobile: result[0].mobile,
      roles: roles
    });

    user.save(function (err, newUser) {
      if (err) {
        console.error(err.msg);
        return res.send(500, err.msg);
      }

      // var url = req.protocol + '://' + req.get('host') + '/users/' + newUser._id;
      var url = (req.proxied ? authConfig.proxied_service : authConfig.service) + '/users/' + newUser._id;
      res.set('Location', url);
      res.send(201, 'The new user is at ' + url);
    });

  });
}

module.exports = function (app) {

  app.get('/users/', auth.ensureAuthenticated, function (req, res) {
    if (req.session.roles === undefined || req.session.roles.indexOf('admin') === -1) {
      return res.send(403, 'only admin allowed');
    }
    return res.render('users', {
      prefix: req.proxied ? req.proxied_prefix : ''
    });
  });

  app.get('/usernames/:name', auth.ensureAuthenticated, function (req, res) {
    User.findOne({
      name: req.params.name
    }).lean().exec(function (err, user) {
      if (err) {
        console.error(err.msg);
        return res.send(500, err.msg);
      }
      if (user) {
        return res.render('user', {
          user: user,
          myRoles: req.session.roles,
          prefix: req.proxied ? req.proxied_prefix : ''
        });
      }
      return res.send(404, req.params.name + ' not found');
    });
  });

  app.post('/users/', auth.ensureAuthenticated, function (req, res) {

    if (req.session.roles === undefined || req.session.roles.indexOf('admin') === -1) {
      return res.send(403, 'only admin allowed');
    }

    if (!req.body.name) {
      return res.send(400, 'need to know name');
    }

    // check if already in db
    User.findOne({
      name: req.body.name
    }).lean().exec(function (err, user) {
      if (err) {
        return res.send(500, err.msg);
      }
      if (user) {
        // var url = req.protocol + '://' + req.get('host') + '/users/' + user._id;
        var url = (req.proxied ? authConfig.proxied_service : authConfig.service) + '/users/' + user._id;
        return res.send(200, 'The user is at ' + url);
      }
      addUser(req, res);
    });

  });

  app.get('/users/json', auth.ensureAuthenticated, function (req, res) {
    if (req.session.roles === undefined || req.session.roles.indexOf('admin') === -1) {
      return res.send(403, "You are not authorized to access this resource. ");
    }
    User.find().lean().exec(function (err, users) {
      if (err) {
        console.error(err.msg);
        return res.json(500, {
          error: err.msg
        });
      }
      res.json(users);
    });
  });

  app.get('/users/:id', auth.ensureAuthenticated, function (req, res) {
    User.findOne({
      _id: req.params.id
    }).lean().exec(function (err, user) {
      if (err) {
        console.error(err.msg);
        return res.send(500, err.msg);
      }
      if (user) {
        return res.render('user', {
          user: user,
          myRoles: req.session.roles,
          prefix: req.proxied ? req.proxied_prefix : ''
        });
      }
      return res.send(404, req.params.id + ' has never logged into the application.');
    });
  });

  app.put('/users/:id', auth.ensureAuthenticated, function (req, res) {
    if (req.session.roles === undefined || req.session.roles.indexOf('admin') === -1) {
      return res.send(403, "You are not authorized to access this resource. ");
    }
    if (!req.is('json')) {
      return res.json(415, {
        error: 'json request expected.'
      });
    }
    User.findOneAndUpdate({
      _id: req.params.id
    }, req.body).lean().exec(function (err, user) {
      if (err) {
        console.error(err.msg);
        return res.json(500, {
          error: err.msg
        });
      }
      res.send(204);
    });
  });

  // get from the db not ad
  app.get('/users/:id/json', auth.ensureAuthenticated, function (req, res) {
    User.findOne({
      _id: req.params.id
    }).lean().exec(function (err, user) {
      if (err) {
        console.error(err.msg);
        return res.json(500, {
          error: err.mesage
        });
      }
      return res.json(user);
    });
  });

  app.get('/users/:id/refresh', auth.ensureAuthenticated, function (req, res) {
    if (req.session.roles === undefined || req.session.roles.indexOf('admin') === -1) {
      return res.send(403, "You are not authorized to access this resource. ");
    }
    User.findOne({
      _id: req.params.id
    }).exec(function (err, user) {
      if (err) {
        console.error(err.msg);
        return res.send(500, err.msg);
      }
      if (user) {
        updateUserProfile(user, res);
      } else {
        return res.send(404, req.params.id + ' is not in the application.');
      }
    });
  });

  // resource /adusers

  app.get('/adusers/:id', auth.ensureAuthenticated, function (req, res) {

    var searchFilter = ad.searchFilter.replace('_id', req.params.id);
    var opts = {
      filter: searchFilter,
      attributes: ad.objAttributes,
      scope: 'sub'
    };
    ldapClient.search(ad.searchBase, opts, false, function (err, result) {
      if (err) {
        return res.json(500, err);
      }
      if (result.length === 0) {
        return res.json(500, {
          error: req.params.id + ' is not found!'
        });
      }
      if (result.length > 1) {
        return res.json(500, {
          error: req.params.id + ' is not unique!'
        });
      }

      return res.json(result[0]);
    });

  });

  app.get('/adusers/:id/photo', auth.ensureAuthenticated, function (req, res) {

    var searchFilter = ad.searchFilter.replace('_id', req.params.id);
    var opts = {
      filter: searchFilter,
      attributes: ad.rawAttributes,
      scope: 'sub'
    };
    ldapClient.search(ad.searchBase, opts, true, function (err, result) {
      if (err) {
        return res.json(500, err);
      }
      if (result.length === 0) {
        return res.json(500, {
          error: req.params.id + ' is not found!'
        });
      }
      if (result.length > 1) {
        return res.json(500, {
          error: req.params.id + ' is not unique!'
        });
      }
      res.set('Content-Type', 'image/jpeg');
      return res.send(result[0].thumbnailPhoto);
    });

  });

  app.get('/adusernames', auth.ensureAuthenticated, function (req, res) {
    // var query = req.param('term');
    var query = req.query.term;
    var nameFilter, opts;
    if (query && query.length > 0) {
      nameFilter = ad.nameFilter.replace('_name', query + '*');
    } else {
      nameFilter = ad.nameFilter.replace('_name', '*');
    }
    opts = {
      filter: nameFilter,
      attributes: ['displayName'],
      scope: 'sub'
    };
    ldapClient.search(ad.searchBase, opts, false, function (err, result) {
      if (err) {
        return res.json(500, JSON.stringify(err));
      }
      if (result.length === 0) {
        return res.json(500, {
          error: 'Names starting with ' + query + ' are not found!'
        });
      }
      return res.json(result);
    });
  });
};
<|endoftext|>


<commit_before>/**
 * Modified ESLint Possible Errors.
 * https://github.com/yannickcr/eslint-plugin-react
 */
module.exports = {
    "plugins": ["react"],
    "parserOptions": {
        "ecmaFeatures": { "jsx": true }
    },
    "rules": {
        "react/display-name": "off",
        "react/forbid-component-props": "off",
        "react/forbid-elements": "off",
        "react/forbid-prop-types": ["error", { "forbid": ["any", "array", "object"] }],
        "react/forbid-foreign-prop-types": "error",
        "react/no-array-index-key": "error",
        "react/no-children-prop": "error",
        "react/no-danger": "off",
        "react/no-danger-with-children": "error",
        "react/no-deprecated": "error",
        "react/no-did-mount-set-state": "error",
        "react/no-did-update-set-state": "error",
        "react/no-direct-mutation-state": "error",
        "react/no-find-dom-node": "error",
        "react/no-is-mounted": "error",
        "react/no-multi-comp": ["error", { "ignoreStateless": true }],
        "react/no-render-return-value": "error",
        "react/no-set-state": "off",
        "react/no-string-refs": "error",
        "react/no-unescaped-entities": "error",
        "react/no-unknown-property": "error",
        "react/no-unused-prop-types": ["warn", { "customValidators": [], "skipShapeProps": true }],
        "react/no-will-update-set-state": "error",
        "react/prefer-es6-class": ["error", "always"],
        "react/prefer-stateless-function": ["error", { "ignorePureComponents": true }],
        "react/prop-types": ["error", { "ignore": ["children"], "customValidators": [] }],
        "react/react-in-jsx-scope": "error",
        "react/require-default-props": "error",
        "react/require-optimization": "off",
        "react/require-render-return": "error",
        "react/self-closing-comp": "error",
        "react/sort-comp": ["error", {
            "order": [
                "static-methods",
                "lifecycle",
                "/^on.+$/",
                "/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/",
                "everything-else",
                "/^render.+$/",
                "render"
            ]
        }],
        "react/sort-prop-types": "off",
        "react/style-prop-object": "error",
        "react/void-dom-elements-no-children": "error",

        "react/jsx-boolean-value": ["error", "never"],
        "react/jsx-closing-bracket-location": ["error", "line-aligned"],
        "react/jsx-curly-spacing": ["error", "never", { "allowMultiline": true }],
        "react/jsx-equals-spacing": ["error", "never"],
        "react/jsx-filename-extension": ["error", { "extensions": [".js", ".jsx"] }],
        "react/jsx-first-prop-new-line": ["error", "multiline"],
        "react/jsx-handler-names": "off",
        "react/jsx-indent": ["error", 4],
        "react/jsx-indent-props": ["error", 4],
        "react/jsx-key": "off",
        "react/jsx-max-props-per-line": "off",
        "react/jsx-no-bind": ["error", {
            "ignoreRefs": true,
            "allowArrowFunctions": true,
            "allowBind": false
        }],
        "react/jsx-no-comment-textnodes": "error",
        "react/jsx-no-duplicate-props": "error",
        "react/jsx-no-literals": "off",
        "react/jsx-no-target-blank": "error",
        "react/jsx-no-undef": "error",
        "react/jsx-pascal-case": "error",
        "react/jsx-sort-props": "off",
        "react/jsx-space-before-closing": ["error", "always"],
        "react/jsx-tag-spacing": ["error", {
            "closingSlash": "never",
            "beforeSelfClosing": "always",
            "afterOpening": "never"
        }],
        "react/jsx-uses-react": "error",
        "react/jsx-uses-vars": "error",
        "react/jsx-wrap-multilines": ["error", {
            "declaration": true,
            "assignment": true,
            "return": true
        }]
    }
};
<commit_msg>update for eslint-plugin-react v7.0.0-rc.1<commit_after>/**
 * Modified ESLint Possible Errors.
 * https://github.com/yannickcr/eslint-plugin-react
 */
module.exports = {
    "plugins": ["react"],
    "parserOptions": {
        "ecmaFeatures": { "jsx": true }
    },
    "rules": {
        "react/display-name": "off",
        "react/forbid-component-props": "off",
        "react/forbid-elements": "off",
        "react/forbid-prop-types": ["error", { "forbid": ["any", "array", "object"] }],
        "react/forbid-foreign-prop-types": "error",
        "react/no-array-index-key": "error",
        "react/no-children-prop": "error",
        "react/no-danger": "off",
        "react/no-danger-with-children": "error",
        "react/no-deprecated": "error",
        "react/no-did-mount-set-state": "error",
        "react/no-did-update-set-state": "error",
        "react/no-direct-mutation-state": "error",
        "react/no-find-dom-node": "error",
        "react/no-is-mounted": "error",
        "react/no-multi-comp": ["error", { "ignoreStateless": true }],
        "react/no-render-return-value": "error",
        "react/no-set-state": "off",
        "react/no-string-refs": "error",
        "react/no-unescaped-entities": "error",
        "react/no-unknown-property": "error",
        "react/no-unused-prop-types": ["warn", { "customValidators": [], "skipShapeProps": true }],
        "react/no-will-update-set-state": "error",
        "react/prefer-es6-class": ["error", "always"],
        "react/prefer-stateless-function": ["error", { "ignorePureComponents": true }],
        "react/prop-types": ["error", { "ignore": ["children"], "customValidators": [] }],
        "react/react-in-jsx-scope": "error",
        "react/require-default-props": "error",
        "react/require-optimization": "off",
        "react/require-render-return": "error",
        "react/self-closing-comp": "error",
        "react/sort-comp": ["error", {
            "order": [
                "static-methods",
                "lifecycle",
                "/^on.+$/",
                "/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/",
                "everything-else",
                "/^render.+$/",
                "render"
            ]
        }],
        "react/sort-prop-types": "off",
        "react/style-prop-object": "error",
        "react/void-dom-elements-no-children": "error",

        "react/jsx-boolean-value": ["error", "never"],
        "react/jsx-closing-bracket-location": ["error", "line-aligned"],
        "react/jsx-curly-spacing": ["error", "never", { "allowMultiline": true }],
        "react/jsx-equals-spacing": ["error", "never"],
        "react/jsx-filename-extension": ["error", { "extensions": [".js", ".jsx"] }],
        "react/jsx-first-prop-new-line": ["error", "multiline"],
        "react/jsx-handler-names": "off",
        "react/jsx-indent": ["error", 4],
        "react/jsx-indent-props": ["error", 4],
        "react/jsx-key": "off",
        "react/jsx-max-props-per-line": "off",
        "react/jsx-no-bind": ["error", {
            "ignoreRefs": true,
            "allowArrowFunctions": true,
            "allowBind": false
        }],
        "react/jsx-no-comment-textnodes": "error",
        "react/jsx-no-duplicate-props": "error",
        "react/jsx-no-literals": "off",
        "react/jsx-no-target-blank": "error",
        "react/jsx-no-undef": "error",
        "react/jsx-pascal-case": "error",
        "react/jsx-sort-props": "off",
        "react/jsx-tag-spacing": ["error", {
            "closingSlash": "never",
            "beforeSelfClosing": "always",
            "afterOpening": "never"
        }],
        "react/jsx-uses-react": "error",
        "react/jsx-uses-vars": "error",
        "react/jsx-wrap-multilines": ["error", {
            "arrow": true,
            "assignment": true,
            "declaration": true,
            "return": true
        }]
    }
};
<|endoftext|>


<commit_before>module.exports = {
  rules: {
    // disallow trailing commas in object literals
    'comma-dangle': [2, 'never'],
    // require function expressions to have a name
    'func-names': 0,
    // require or disallow use of semicolons instead of ASI
    'semi': [2, 'never'],
    // require or disallow space before function opening parenthesis
    'space-before-function-paren': [2, 'never'],
    // require or disallow Yoda conditions
    'yoda': [2, 'never']
  }
}
<commit_msg>feat(rules): Change comma-dangle to always-multiline<commit_after>module.exports = {
  rules: {
    // require trailing comma in multilines
    'comma-dangle': [2, 'always-multiline'],
    // require function expressions to have a name
    'func-names': 0,
    // require or disallow use of semicolons instead of ASI
    'semi': [2, 'never'],
    // require or disallow space before function opening parenthesis
    'space-before-function-paren': [2, 'never'],
    // require or disallow Yoda conditions
    'yoda': [2, 'never']
  }
}
<|endoftext|>


<commit_before>
// eslint - Stylistic issues
'use strict';

module.exports = {
    'rules': {
        // enforce spacing inside array brackets
        'array-bracket-spacing': [2, 'never'],
        // disallow or enforce spaces inside of single line blocks
        'block-spacing': 2,
        // enforce one true brace style
        'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
        // require camel case names
        'camelcase': 2,
        // enforce spacing before and after comma
        'comma-spacing': [2, { 'before': false, 'after': true }],
        // enforce one true comma style
        'comma-style': [2, 'last'],
        // disallow padding inside computed properties
        'computed-property-spacing': [2, 'never'],
        // enforces consistent naming when capturing the current execution context
        'consistent-this': [2, 'that'],
        // enforce newline at the end of file, with no multiple empty lines
        'eol-last': 2,
        // require function expressions to have a name
        'func-names': 0,
        // enforces use of function declarations or expressions
        'func-style': 0,
        // this option enforces minimum and maximum identifier lengths (variable names, property names etc.)
        'id-length': 0,
        // require identifiers to match the provided regular expression
        'id-match': 0,
        // this option sets a specific tab width for your code
        'indent': [2, 4, { 'SwitchCase': 0, 'VariableDeclarator': 1 }],
        // specify whether double or single quotes should be used in JSX attributes
        'jsx-quotes': [2, 'prefer-double'],
        // enforces spacing between keys and values in object literal properties
        'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
        // enforce spacing before and after keywords
        'keyword-spacing': 2,
        // disallow mixed 'LF' and 'CRLF' as linebreaks
        'linebreak-style': [2, 'unix'],
        // enforces empty lines around comments
        // comment: I would like to use this but the rule is very restrictive.. it lacks some flexibility
        'lines-around-comment': 0,
        // specify the maximum depth that blocks can be nested
        'max-depth': [1, 6],
        // specify the maximum length of a line in your program
        'max-len': [1, 140, 4],
        // specify the maximum depth callbacks can be nested
        'max-nested-callbacks': [2, 6],
        // limits the number of parameters that can be used in the function declaration.
        'max-params': [1, 6],
        // specify the maximum number of statement allowed in a function
        'max-statements': [1, 140],
        // enforce a maximum number of statements allowed per line
        'max-statements-per-line': [1, { 'max': 1 }],
        // require a capital letter for constructors
        'new-cap': [2, { 'newIsCap': true }],
        // disallow the omission of parentheses when invoking a constructor with no arguments
        'new-parens': 2,
        // allow/disallow an empty newline after var statement
        'newline-after-var': [2, 'always'],
        // require newline before return statement
        'newline-before-return': 0,
        // enforce newline after each call when chaining the calls
        'newline-per-chained-call': 0,
        // disallow use of the Array constructor
        'no-array-constructor': 2,
        // no-bitwise - disallow use of bitwise operators
        'no-bitwise': 2,
        // disallow use of the continue statement
        'no-continue': 0,
        // disallow comments inline after code
        'no-inline-comments': 0,
        // disallow if as the only statement in an else block
        'no-lonely-if': 2,
        // disallow mixed spaces and tabs for indentation
        'no-mixed-spaces-and-tabs': 2,
        // disallow multiple empty lines and only one newline at the end
        'no-multiple-empty-lines': [2, { 'max': 2, 'maxEOF': 1 }],
        // disallow negated conditions
        'no-negated-condition': 0,
        // disallow nested ternary expressions
        'no-nested-ternary': 2,
        // disallow use of the Object constructor
        'no-new-object': 2,
        // disallow use of unary operators, ++ and --
        'no-plusplus': 2,
        // disallow use of certain syntax in code
        'no-restricted-syntax': 0,
        // disallow space between function identifier and application
        'no-spaced-func': 2,
        // disallow the use of ternary operators
        'no-ternary': 0,
        // disallow trailing whitespace at the end of lines
        'no-trailing-spaces': 2,
        // disallow dangling underscores in identifiers
        'no-underscore-dangle': 0,
        // disallow the use of Boolean literals in conditional expressions
        'no-unneeded-ternary': 2,
        // disallow whitespace before properties
        'no-whitespace-before-property': 2,
        // require padding inside curly braces
        'object-curly-spacing': [2, 'always'],
        // allow just one var statement per function
        'one-var': [2, 'never'],
        // require or disallow an newline around variable declarations
        'one-var-declaration-per-line': [2, 'always'],
        // require assignment operator shorthand where possible or prohibit it entirely
        'operator-assignment': 0,
        // enforce operators to be placed before or after line breaks
        'operator-linebreak': 0,
        // enforce padding within blocks
        'padded-blocks': [2, 'never'],
        // require quotes around object literal property names
        'quote-props': [2, 'as-needed'],
        // specify whether double or single quotes should be used
        'quotes': [2, 'single', 'avoid-escape'],
        // require JSDoc comment
        'require-jsdoc': 0,
        // enforce spacing before and after semicolons
        'semi-spacing': [2, { 'before': false, 'after': true }],
        // sort import declarations within module
        'sort-imports': 0,
        // require or disallow use of semicolons instead of ASI
        'semi': [2, 'always'],
        // sort variables within the same declaration block
        'sort-vars': 0,
        // require or disallow space before blocks
        'space-before-blocks': 2,
        // require or disallow space before function opening parenthesis
        'space-before-function-paren': [2, { 'anonymous': 'always', 'named': 'never' }],
        // require or disallow spaces inside parentheses
        'space-in-parens': [2, 'never'],
        // require spaces around operators
        'space-infix-ops': 2,
        // require or disallow spaces before/after unary operators
        'space-unary-ops': 2,
        // require or disallow a space immediately following the // or /* in a comment
        'spaced-comment': [2, 'always', {
            'exceptions': ['-', '+'],
            'markers': ['=', '!'],                     // space here to support sprockets directives
        }],
        // require regex literals to be wrapped in parentheses
        'wrap-regex': 0,
    },
};
<commit_msg>Turn off rule.<commit_after>
// eslint - Stylistic issues
'use strict';

module.exports = {
    'rules': {
        // enforce spacing inside array brackets
        'array-bracket-spacing': [2, 'never'],
        // disallow or enforce spaces inside of single line blocks
        'block-spacing': 2,
        // enforce one true brace style
        'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
        // require camel case names
        'camelcase': 2,
        // enforce spacing before and after comma
        'comma-spacing': [2, { 'before': false, 'after': true }],
        // enforce one true comma style
        'comma-style': [2, 'last'],
        // disallow padding inside computed properties
        'computed-property-spacing': [2, 'never'],
        // enforces consistent naming when capturing the current execution context
        'consistent-this': [2, 'that'],
        // enforce newline at the end of file, with no multiple empty lines
        'eol-last': 2,
        // require function expressions to have a name
        'func-names': 0,
        // enforces use of function declarations or expressions
        'func-style': 0,
        // this option enforces minimum and maximum identifier lengths (variable names, property names etc.)
        'id-length': 0,
        // require identifiers to match the provided regular expression
        'id-match': 0,
        // this option sets a specific tab width for your code
        'indent': [2, 4, { 'SwitchCase': 0, 'VariableDeclarator': 1 }],
        // specify whether double or single quotes should be used in JSX attributes
        'jsx-quotes': [2, 'prefer-double'],
        // enforces spacing between keys and values in object literal properties
        'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
        // enforce spacing before and after keywords
        'keyword-spacing': 2,
        // disallow mixed 'LF' and 'CRLF' as linebreaks
        'linebreak-style': [2, 'unix'],
        // enforces empty lines around comments
        // comment: I would like to use this but the rule is very restrictive.. it lacks some flexibility
        'lines-around-comment': 0,
        // specify the maximum depth that blocks can be nested
        'max-depth': [1, 6],
        // specify the maximum length of a line in your program
        'max-len': [1, 140, 4],
        // specify the maximum depth callbacks can be nested
        'max-nested-callbacks': [2, 6],
        // limits the number of parameters that can be used in the function declaration.
        'max-params': [1, 6],
        // specify the maximum number of statement allowed in a function
        'max-statements': [1, 140],
        // enforce a maximum number of statements allowed per line
        'max-statements-per-line': 0,
        // require a capital letter for constructors
        'new-cap': [2, { 'newIsCap': true }],
        // disallow the omission of parentheses when invoking a constructor with no arguments
        'new-parens': 2,
        // allow/disallow an empty newline after var statement
        'newline-after-var': [2, 'always'],
        // require newline before return statement
        'newline-before-return': 0,
        // enforce newline after each call when chaining the calls
        'newline-per-chained-call': 0,
        // disallow use of the Array constructor
        'no-array-constructor': 2,
        // no-bitwise - disallow use of bitwise operators
        'no-bitwise': 2,
        // disallow use of the continue statement
        'no-continue': 0,
        // disallow comments inline after code
        'no-inline-comments': 0,
        // disallow if as the only statement in an else block
        'no-lonely-if': 2,
        // disallow mixed spaces and tabs for indentation
        'no-mixed-spaces-and-tabs': 2,
        // disallow multiple empty lines and only one newline at the end
        'no-multiple-empty-lines': [2, { 'max': 2, 'maxEOF': 1 }],
        // disallow negated conditions
        'no-negated-condition': 0,
        // disallow nested ternary expressions
        'no-nested-ternary': 2,
        // disallow use of the Object constructor
        'no-new-object': 2,
        // disallow use of unary operators, ++ and --
        'no-plusplus': 2,
        // disallow use of certain syntax in code
        'no-restricted-syntax': 0,
        // disallow space between function identifier and application
        'no-spaced-func': 2,
        // disallow the use of ternary operators
        'no-ternary': 0,
        // disallow trailing whitespace at the end of lines
        'no-trailing-spaces': 2,
        // disallow dangling underscores in identifiers
        'no-underscore-dangle': 0,
        // disallow the use of Boolean literals in conditional expressions
        'no-unneeded-ternary': 2,
        // disallow whitespace before properties
        'no-whitespace-before-property': 2,
        // require padding inside curly braces
        'object-curly-spacing': [2, 'always'],
        // allow just one var statement per function
        'one-var': [2, 'never'],
        // require or disallow an newline around variable declarations
        'one-var-declaration-per-line': [2, 'always'],
        // require assignment operator shorthand where possible or prohibit it entirely
        'operator-assignment': 0,
        // enforce operators to be placed before or after line breaks
        'operator-linebreak': 0,
        // enforce padding within blocks
        'padded-blocks': [2, 'never'],
        // require quotes around object literal property names
        'quote-props': [2, 'as-needed'],
        // specify whether double or single quotes should be used
        'quotes': [2, 'single', 'avoid-escape'],
        // require JSDoc comment
        'require-jsdoc': 0,
        // enforce spacing before and after semicolons
        'semi-spacing': [2, { 'before': false, 'after': true }],
        // sort import declarations within module
        'sort-imports': 0,
        // require or disallow use of semicolons instead of ASI
        'semi': [2, 'always'],
        // sort variables within the same declaration block
        'sort-vars': 0,
        // require or disallow space before blocks
        'space-before-blocks': 2,
        // require or disallow space before function opening parenthesis
        'space-before-function-paren': [2, { 'anonymous': 'always', 'named': 'never' }],
        // require or disallow spaces inside parentheses
        'space-in-parens': [2, 'never'],
        // require spaces around operators
        'space-infix-ops': 2,
        // require or disallow spaces before/after unary operators
        'space-unary-ops': 2,
        // require or disallow a space immediately following the // or /* in a comment
        'spaced-comment': [2, 'always', {
            'exceptions': ['-', '+'],
            'markers': ['=', '!'],                     // space here to support sprockets directives
        }],
        // require regex literals to be wrapped in parentheses
        'wrap-regex': 0,
    },
};
<|endoftext|>


<commit_before>module.exports = {
  'rules': {
    // enforce spacing inside array brackets
    'array-bracket-spacing': [2, 'never'],
    // enforce one true brace style
    'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
    // require camel case names
    'camelcase': [2, { 'properties': 'never' }],
    // enforce spacing before and after comma
    'comma-spacing': [2, { 'before': false, 'after': true }],
    // enforce one true comma style
    'comma-style': [2, 'last'],
    // disallow padding inside computed properties
    'computed-property-spacing': [2, 'never'],
    // enforces consistent naming when capturing the current execution context
    'consistent-this': 0,
    // enforce newline at the end of file, with no multiple empty lines
    'eol-last': 2,
    // require function expressions to have a name
    'func-names': 1,
    // enforces use of function declarations or expressions
    'func-style': 0,
    // this option enforces minimum and maximum identifier lengths
    // (variable names, property names etc.)
    'id-length': 0,
    // this option sets a specific tab width for your code
    // https://github.com/eslint/eslint/blob/master/docs/rules/indent.md
    'indent': [2, 2, { 'SwitchCase': 1, 'VariableDeclarator': 1 }],
    // specify whether double or single quotes should be used in JSX attributes
    // http://eslint.org/docs/rules/jsx-quotes
    'jsx-quotes': [2, 'prefer-double'],
    // enforces spacing between keys and values in object literal properties
    'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
    // enforces empty lines around comments
    'lines-around-comment': 0,
    // disallow mixed 'LF' and 'CRLF' as linebreaks
    'linebreak-style': 0,
    // specify the maximum length of a line in your program
    // https://github.com/eslint/eslint/blob/master/docs/rules/max-len.md
    'max-len': [2, 100, 2, {
      'ignoreUrls': true,
      'ignoreComments': false
    }],
    // specify the maximum depth callbacks can be nested
    'max-nested-callbacks': 0,
    // require a capital letter for constructors
    'new-cap': [2, { 'newIsCap': true }],
    // disallow the omission of parentheses when invoking a constructor with no arguments
    'new-parens': 0,
    // allow/disallow an empty newline after var statement
    'newline-after-var': 0,
    // disallow use of the Array constructor
    'no-array-constructor': 0,
    // disallow use of the continue statement
    'no-continue': 0,
    // disallow comments inline after code
    'no-inline-comments': 0,
    // disallow if as the only statement in an else block
    'no-lonely-if': 0,
    // disallow mixed spaces and tabs for indentation
    'no-mixed-spaces-and-tabs': 2,
    // disallow multiple empty lines and only one newline at the end
    'no-multiple-empty-lines': [2, { 'max': 2, 'maxEOF': 1 }],
    // disallow nested ternary expressions
    'no-nested-ternary': 2,
    // disallow use of the Object constructor
    'no-new-object': 2,
    // disallow space between function identifier and application
    'no-spaced-func': 2,
    // disallow the use of ternary operators
    'no-ternary': 0,
    // disallow trailing whitespace at the end of lines
    'no-trailing-spaces': 2,
    // disallow dangling underscores in identifiers
    'no-underscore-dangle': 0,
    // disallow the use of Boolean literals in conditional expressions
    // also, prefer `a || b` over `a ? a : b`
    // http://eslint.org/docs/rules/no-unneeded-ternary
    'no-unneeded-ternary': [2, { 'defaultAssignment': false }],
    // require padding inside curly braces
    'object-curly-spacing': [2, 'always'],
    // allow just one var statement per function
    'one-var': [2, 'never'],
    // require assignment operator shorthand where possible or prohibit it entirely
    'operator-assignment': 0,
    // enforce operators to be placed before or after line breaks
    'operator-linebreak': 0,
    // enforce padding within blocks
    'padded-blocks': [2, 'never'],
    // require quotes around object literal property names
    // http://eslint.org/docs/rules/quote-props.html
    'quote-props': [2, 'as-needed', { 'keywords': false, 'unnecessary': true, 'numbers': false }],
    // specify whether double or single quotes should be used
    'quotes': [2, 'single', 'avoid-escape'],
    // require identifiers to match the provided regular expression
    'id-match': 0,
    // enforce spacing before and after semicolons
    'semi-spacing': [2, { 'before': false, 'after': true }],
    // require or disallow use of semicolons instead of ASI
    'semi': [2, 'always'],
    // sort variables within the same declaration block
    'sort-vars': 0,
    // require a space before certain keywords
    'keyword-spacing': [2],
    // require or disallow space before blocks
    'space-before-blocks': 2,
    // require or disallow space before function opening parenthesis
    // https://github.com/eslint/eslint/blob/master/docs/rules/space-before-function-paren.md
    'space-before-function-paren': [2, { 'anonymous': 'always', 'named': 'never' }],
    // require or disallow spaces inside parentheses
    'space-in-parens': [2, 'never'],
    // require spaces around operators
    'space-infix-ops': 2,
    // Require or disallow spaces before/after unary operators
    'space-unary-ops': 0,
    // require or disallow a space immediately following the // or /* in a comment
    'spaced-comment': [2, 'always', {
      'exceptions': ['-', '+'],
      'markers': ['=', '!']           // space here to support sprockets directives
    }],
    // require regex literals to be wrapped in parentheses
    'wrap-regex': 0
  }
};
<commit_msg>Allow 120 chars line length.<commit_after>module.exports = {
  'rules': {
    // enforce spacing inside array brackets
    'array-bracket-spacing': [2, 'never'],
    // enforce one true brace style
    'brace-style': [2, '1tbs', { 'allowSingleLine': true }],
    // require camel case names
    'camelcase': [2, { 'properties': 'never' }],
    // enforce spacing before and after comma
    'comma-spacing': [2, { 'before': false, 'after': true }],
    // enforce one true comma style
    'comma-style': [2, 'last'],
    // disallow padding inside computed properties
    'computed-property-spacing': [2, 'never'],
    // enforces consistent naming when capturing the current execution context
    'consistent-this': 0,
    // enforce newline at the end of file, with no multiple empty lines
    'eol-last': 2,
    // require function expressions to have a name
    'func-names': 1,
    // enforces use of function declarations or expressions
    'func-style': 0,
    // this option enforces minimum and maximum identifier lengths
    // (variable names, property names etc.)
    'id-length': 0,
    // this option sets a specific tab width for your code
    // https://github.com/eslint/eslint/blob/master/docs/rules/indent.md
    'indent': [2, 2, { 'SwitchCase': 1, 'VariableDeclarator': 1 }],
    // specify whether double or single quotes should be used in JSX attributes
    // http://eslint.org/docs/rules/jsx-quotes
    'jsx-quotes': [2, 'prefer-double'],
    // enforces spacing between keys and values in object literal properties
    'key-spacing': [2, { 'beforeColon': false, 'afterColon': true }],
    // enforces empty lines around comments
    'lines-around-comment': 0,
    // disallow mixed 'LF' and 'CRLF' as linebreaks
    'linebreak-style': 0,
    // specify the maximum length of a line in your program
    // https://github.com/eslint/eslint/blob/master/docs/rules/max-len.md
    'max-len': [2, 120, 2, {
      'ignoreUrls': true,
      'ignoreComments': false
    }],
    // specify the maximum depth callbacks can be nested
    'max-nested-callbacks': 0,
    // require a capital letter for constructors
    'new-cap': [2, { 'newIsCap': true }],
    // disallow the omission of parentheses when invoking a constructor with no arguments
    'new-parens': 0,
    // allow/disallow an empty newline after var statement
    'newline-after-var': 0,
    // disallow use of the Array constructor
    'no-array-constructor': 0,
    // disallow use of the continue statement
    'no-continue': 0,
    // disallow comments inline after code
    'no-inline-comments': 0,
    // disallow if as the only statement in an else block
    'no-lonely-if': 0,
    // disallow mixed spaces and tabs for indentation
    'no-mixed-spaces-and-tabs': 2,
    // disallow multiple empty lines and only one newline at the end
    'no-multiple-empty-lines': [2, { 'max': 2, 'maxEOF': 1 }],
    // disallow nested ternary expressions
    'no-nested-ternary': 2,
    // disallow use of the Object constructor
    'no-new-object': 2,
    // disallow space between function identifier and application
    'no-spaced-func': 2,
    // disallow the use of ternary operators
    'no-ternary': 0,
    // disallow trailing whitespace at the end of lines
    'no-trailing-spaces': 2,
    // disallow dangling underscores in identifiers
    'no-underscore-dangle': 0,
    // disallow the use of Boolean literals in conditional expressions
    // also, prefer `a || b` over `a ? a : b`
    // http://eslint.org/docs/rules/no-unneeded-ternary
    'no-unneeded-ternary': [2, { 'defaultAssignment': false }],
    // require padding inside curly braces
    'object-curly-spacing': [2, 'always'],
    // allow just one var statement per function
    'one-var': [2, 'never'],
    // require assignment operator shorthand where possible or prohibit it entirely
    'operator-assignment': 0,
    // enforce operators to be placed before or after line breaks
    'operator-linebreak': 0,
    // enforce padding within blocks
    'padded-blocks': [2, 'never'],
    // require quotes around object literal property names
    // http://eslint.org/docs/rules/quote-props.html
    'quote-props': [2, 'as-needed', { 'keywords': false, 'unnecessary': true, 'numbers': false }],
    // specify whether double or single quotes should be used
    'quotes': [2, 'single', 'avoid-escape'],
    // require identifiers to match the provided regular expression
    'id-match': 0,
    // enforce spacing before and after semicolons
    'semi-spacing': [2, { 'before': false, 'after': true }],
    // require or disallow use of semicolons instead of ASI
    'semi': [2, 'always'],
    // sort variables within the same declaration block
    'sort-vars': 0,
    // require a space before certain keywords
    'keyword-spacing': [2],
    // require or disallow space before blocks
    'space-before-blocks': 2,
    // require or disallow space before function opening parenthesis
    // https://github.com/eslint/eslint/blob/master/docs/rules/space-before-function-paren.md
    'space-before-function-paren': [2, { 'anonymous': 'always', 'named': 'never' }],
    // require or disallow spaces inside parentheses
    'space-in-parens': [2, 'never'],
    // require spaces around operators
    'space-infix-ops': 2,
    // Require or disallow spaces before/after unary operators
    'space-unary-ops': 0,
    // require or disallow a space immediately following the // or /* in a comment
    'spaced-comment': [2, 'always', {
      'exceptions': ['-', '+'],
      'markers': ['=', '!']           // space here to support sprockets directives
    }],
    // require regex literals to be wrapped in parentheses
    'wrap-regex': 0
  }
};
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright (c) 2017 Google Inc. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt
 * Code distributed by Google as part of this project is also
 * subject to an additional IP rights grant found at
 * http://polymer.github.io/PATENTS.txt
 */
'use strict';

import runtime from './runtime.js';
import assert from '../platform/assert-web.js';
import tracing from '../tracelib/trace.js';
import Type from './type.js';
import Relation from './relation.js';
import handle from './handle.js';
import OuterPec from './outer-PEC.js';
import Recipe from './recipe/recipe.js';
import Manifest from './manifest.js';
import Description from './description.js';
import util from './recipe/util.js';
import FakePecFactory from './fake-pec-factory.js';
import StorageProviderFactory from './storage/storage-provider-factory.js';
import scheduler from './scheduler.js';

class Arc {
  constructor({id, context, pecFactory, slotComposer, loader, storageKey}) {
    // TODO: context should not be optional.
    this._context = context || new Manifest({id});
    // TODO: pecFactory should not be optional. update all callers and fix here.
    this._pecFactory = pecFactory || FakePecFactory.bind(null);
    this.id = id;
    this._nextLocalID = 0;
    this._activeRecipe = new Recipe();
    // TODO: rename: this are just tuples of {particles, views, slots} of instantiated recipes merged into active recipe..
    this._recipes = [];
    this._loader = loader;
    this._scheduler = scheduler;

    // All the views, mapped by view ID
    this._viewsById = new Map();
    // .. and mapped by Type
    this._viewsByType = new Map();

    // information about last-seen-versions of views
    this._lastSeenVersion = new Map();

    // storage keys for referenced views
    this._storageKeys = {};
    this._storageKey = storageKey;

    this.particleViewMaps = new Map();
    let pecId = this.generateID();
    let innerPecPort = this._pecFactory(pecId);
    this.pec = new OuterPec(innerPecPort, slotComposer, this, `${pecId}:outer`);
    if (slotComposer) {
      slotComposer.arc = this;
    }
    this._storageProviderFactory = new StorageProviderFactory(this);

    // Dictionary from each tag string to a list of views
    this._tags = {};
    // Map from each view to a list of tags.
    this._viewTags = new Map();
    // Map from each view to its description (originating in the manifest).
    this._viewDescriptions = new Map();

    this._search = null;
    this._description = new Description(this);
  }
  get loader() {
    return this._loader;
  }

  get scheduler() {
    return this._scheduler;
  }

  set search(search) {
    this._search = search ? search.toLowerCase().trim() : null;
  }

  get search() {
    return this._search;
  }

  get description() { return this._description; }

  get makeSuggestions() { return this._makeSuggestions; }
  set makeSuggestions(callback) {
    this._makeSuggestions = callback;
    this._scheduler.idleCallback = callback;
  }

  serialize() {
    return `
meta
  name: '${this.id}'

@active
${this.activeRecipe.toString()}`;
  }

  static async deserialize({serialization, pecFactory, slotComposer, loader}) {
    let manifest = await Manifest.parse(serialization, {loader});
    var arc = new Arc({id: manifest.meta.name, slotComposer, pecFactory, loader});
    var recipe = manifest.activeRecipe.clone();
    recipe.normalize();
    arc.instantiate(recipe);
    return arc;
  }

  get context() {
    return this._context;
  }

  get activeRecipe() { return this._activeRecipe; }
  get recipes() { return this._recipes; }

  loadedParticles() {
    return [...this.particleViewMaps.values()].map(({spec}) => spec);
  }

  _instantiateParticle(recipeParticle) {
    let id = this.generateID();
    let viewMap = {spec: recipeParticle.spec, views: new Map()};
    this.particleViewMaps.set(id, viewMap);

    for (let [name, connection] of Object.entries(recipeParticle.connections)) {
      if (!connection.view) {
        assert(connection.isOptional);
        continue;
      }
      let view = this.findViewById(connection.view.id);
      assert(view);
      this._connectParticleToView(id, recipeParticle, name, view);
    }

    // At least all non-optional connections must be resolved
    assert(viewMap.views.size >= viewMap.spec.connections.filter(c => !c.isOptional).length,
           `Not all mandatory connections are resolved for {$particle}`);
    this.pec.instantiate(recipeParticle, id, viewMap.spec, viewMap.views, this._lastSeenVersion);
    return id;
  }

  generateID() {
    return `${this.id}:${this._nextLocalID++}`;
  }

  generateIDComponents() {
    return {base: this.id, component: () => this._nextLocalID++};
  }

  get _views() {
    return [...this._viewsById.values()];
  }

  // Makes a copy of the arc used for speculative execution.
  async cloneForSpeculativeExecution() {
    var arc = new Arc({id: this.generateID(), pecFactory: this._pecFactory, context: this.context, loader: this._loader});
    arc._scheduler = this._scheduler.clone();
    var viewMap = new Map();
    for (let v of this._views) {
      let clone = await arc._storageProviderFactory.construct(v.id, v.type, 'in-memory');
      await clone.cloneFrom(v);
      viewMap.set(v, clone);
      if (this._viewDescriptions.has(v)) {
        arc._viewDescriptions.set(clone, this._viewDescriptions.get(v));
      }
    };
    this.particleViewMaps.forEach((value, key) => {
      arc.particleViewMaps.set(key, {
        spec: value.spec,
        views: new Map()
      });
      value.views.forEach(v => arc.particleViewMaps.get(key).views.set(v.name, viewMap.get(v)));
    });

   let {particles, views, slots} = this._activeRecipe.mergeInto(arc._activeRecipe);
   let particleIndex = 0, viewIndex = 0, slotIndex = 0;
   this._recipes.forEach(recipe => {
     let arcRecipe = {particles: [], views: [], slots: [], innerArcs: new Map()};
     recipe.particles.forEach(p => {
       arcRecipe.particles.push(particles[particleIndex++]);
       if (recipe.innerArcs.has(p)) {
         let thisInnerArc = recipe.innerArcs.get(p);
         let transformationParticle = arcRecipe.particles[arcRecipe.particles.length - 1];
         let innerArc = {activeRecipe: new Recipe(), recipes: []};
         let innerTuples = thisInnerArc.activeRecipe.mergeInto(innerArc.activeRecipe);
         thisInnerArc.recipes.forEach(thisInnerArcRecipe => {
           let innerArcRecipe = {particles: [], views: [], slots: [], innerArcs: new Map()};
           let innerIndex = 0;
           thisInnerArcRecipe.particles.forEach(thisInnerArcRecipeParticle => {
             innerArcRecipe.particles.push(innerTuples.particles[innerIndex++]);
           });
           innerIndex = 0;
           thisInnerArcRecipe.views.forEach(thisInnerArcRecipeParticle => {
             innerArcRecipe.views.push(innerTuples.views[innerIndex++]);
           });
           innerIndex = 0;
           thisInnerArcRecipe.slots.forEach(thisInnerArcRecipeParticle => {
             innerArcRecipe.slots.push(innerTuples.slots[innerIndex++]);
           });
           innerArc.recipes.push(innerArcRecipe);
         });
         arcRecipe.innerArcs.set(transformationParticle, innerArc);
       }
     });
     recipe.views.forEach(p => {
       arcRecipe.views.push(views[viewIndex++]);
     });
     recipe.slots.forEach(p => {
       arcRecipe.slots.push(slots[slotIndex++]);
     });

     arc._recipes.push(arcRecipe);
   });

    for (let v of viewMap.values()) {
      // FIXME: Tags
      arc._registerView(v, []);
    }
    return arc;
  }

  async instantiate(recipe, innerArc) {
    assert(recipe.isResolved(), 'Cannot instantiate an unresolved recipe');

    let currentArc = {activeRecipe: this._activeRecipe, recipes: this._recipes};
    if (innerArc) {
      let innerArcs = this._recipes.find(r => !!r.particles.find(p => p == innerArc.particle)).innerArcs;
      if (!innerArcs.has(innerArc.particle)) {
         innerArcs.set(innerArc.particle, {activeRecipe: new Recipe(), recipes: []});
      }
      currentArc = innerArcs.get(innerArc.particle);
    }
    let {views, particles, slots} = recipe.mergeInto(currentArc.activeRecipe);
    currentArc.recipes.push({particles, views, slots, innerArcs: new Map()});

    for (let recipeView of views) {
      if (['copy', 'create'].includes(recipeView.fate)) {
        let view = await this.createView(recipeView.type, /* name= */ null, this.generateID(), recipeView.tags);
        if (recipeView.fate === 'copy') {
          var copiedView = this.findViewById(recipeView.id);
          await view.cloneFrom(copiedView);
          let copiedViewDesc = this.getViewDescription(copiedView);
          if (copiedViewDesc) {
            this._viewDescriptions.set(view, copiedViewDesc);
          }
        }
        recipeView.id = view.id;
        recipeView.fate = 'use';
        recipeView.storageKey = view.storageKey;
        // TODO: move the call to OuterPEC's DefineView to here
      }
      let storageKey = recipeView.storageKey;
      if (!storageKey)
        storageKey = this.keyForId(recipeView.id);
      let view = await this._storageProviderFactory.connect(recipeView.id, recipeView.type, storageKey);
      assert(view, `view '${recipeView.id}' was not found`);
    }

    particles.forEach(recipeParticle => this._instantiateParticle(recipeParticle));

    if (this.pec.slotComposer) {
      // TODO: pass slot-connections instead
      this.pec.slotComposer.initializeRecipe(particles);
    }
  }

  _connectParticleToView(particleId, particle, name, targetView) {
    assert(targetView, 'no target view provided');
    var viewMap = this.particleViewMaps.get(particleId);
    assert(viewMap.spec.connectionMap.get(name) !== undefined, 'can\'t connect view to a view slot that doesn\'t exist');
    viewMap.views.set(name, targetView);
  }

  async createView(type, name, id, tags, storageKey) {
    assert(type instanceof Type, `can't createView with type ${type} that isn't a Type`);

    if (type.isRelation) {
      type = Type.newSetView(type);
    }

    if (id == undefined)
      id = this.generateID();

    if (storageKey == undefined && this._storageKey)
      storageKey = this._storageProviderFactory.parseStringAsKey(this._storageKey).childKeyForHandle(id).toString();

    if (storageKey == undefined)
      storageKey = 'in-memory';

    let view = await this._storageProviderFactory.construct(id, type, storageKey);
    assert(view, 'handle with id ${id} already exists');
    view.name = name;

    this._registerView(view, tags);
    return view;
  }

  _registerView(view, tags) {
    tags = tags || [];
    tags = Array.isArray(tags) ? tags : [tags];
    tags.forEach(tag => assert(tag.startsWith('#'),
      `tag ${tag} must start with '#'`));

    this._viewsById.set(view.id, view);
    let byType = this._viewsByType.get(Arc._viewKey(view.type)) || [];
    byType.push(view);
    this._viewsByType.set(Arc._viewKey(view.type), byType);

    if (tags.length) {
      for (let tag of tags) {
        if (this._tags[tag] == undefined)
          this._tags[tag] = [];
        this._tags[tag].push(view);
      }
    }
    this._viewTags.set(view, new Set(tags));

    this._storageKeys[view.id] = view.storageKey;
  }

  // TODO: Don't use this, we should be testing the schemas for compatiblity
  //       instead of using just the name.
  static _viewKey(type) {
    if (type.isSetView) {
      return `list:${type.primitiveType().entitySchema.name}`;
    } else if (type.isEntity) {
      return type.entitySchema.name;
    } else if (type.isShape) {
      // TODO we need to fix this too, otherwise all views of shape type will
      // be of the 'same type' when searching by type.
      return type.shapeShape;
    }
  }

  findViewsByType(type, options) {
    // TODO: use options (location, labels, etc.) somehow.
    var views = this._viewsByType.get(Arc._viewKey(type)) || [];
    if (options && options.tags) {
      views = views.filter(view => options.tags.filter(tag => !this._viewTags.get(view).has(tag)).length == 0);
    }
    return views;
  }

  findViewById(id) {
    let view = this._viewsById.get(id);
    if (view == null) {
      view = this._context.findViewById(id);
    }
    return view;
  }

  getViewDescription(view) {
    assert(view, 'Cannot fetch description for nonexistend view');
    return this._viewDescriptions.get(view) || view.description;
  }

  keyForId(id) {
    return this._storageKeys[id];
  }

  newCommit(entityMap) {
    for (let [entity, view] of entityMap.entries()) {
      entity.identify(this.generateID());
    }
    for (let [entity, view] of entityMap.entries()) {
      new handle.handleFor(view).store(entity);
    }
  }

  stop() {
    this.pec.stop();
  }

  toContextString(options) {
    let results = [];
    let views = [...this._viewsById.values()].sort(util.compareComparables);
    views.forEach(v => {
      results.push(v.toString(this._viewTags.get(v)));
    });

    // TODO: include views entities
    // TODO: include (remote) slots?

    if (!this._activeRecipe.isEmpty()) {
      results.push(this._activeRecipe.toString());
    }

    return results.join('\n');
  }
}

export default Arc;
<commit_msg>Report helpful info via assert if no storage key is found.<commit_after>/**
 * @license
 * Copyright (c) 2017 Google Inc. All rights reserved.
 * This code may only be used under the BSD style license found at
 * http://polymer.github.io/LICENSE.txt
 * Code distributed by Google as part of this project is also
 * subject to an additional IP rights grant found at
 * http://polymer.github.io/PATENTS.txt
 */
'use strict';

import runtime from './runtime.js';
import assert from '../platform/assert-web.js';
import tracing from '../tracelib/trace.js';
import Type from './type.js';
import Relation from './relation.js';
import handle from './handle.js';
import OuterPec from './outer-PEC.js';
import Recipe from './recipe/recipe.js';
import Manifest from './manifest.js';
import Description from './description.js';
import util from './recipe/util.js';
import FakePecFactory from './fake-pec-factory.js';
import StorageProviderFactory from './storage/storage-provider-factory.js';
import scheduler from './scheduler.js';

class Arc {
  constructor({id, context, pecFactory, slotComposer, loader, storageKey}) {
    // TODO: context should not be optional.
    this._context = context || new Manifest({id});
    // TODO: pecFactory should not be optional. update all callers and fix here.
    this._pecFactory = pecFactory || FakePecFactory.bind(null);
    this.id = id;
    this._nextLocalID = 0;
    this._activeRecipe = new Recipe();
    // TODO: rename: this are just tuples of {particles, views, slots} of instantiated recipes merged into active recipe..
    this._recipes = [];
    this._loader = loader;
    this._scheduler = scheduler;

    // All the views, mapped by view ID
    this._viewsById = new Map();
    // .. and mapped by Type
    this._viewsByType = new Map();

    // information about last-seen-versions of views
    this._lastSeenVersion = new Map();

    // storage keys for referenced views
    this._storageKeys = {};
    this._storageKey = storageKey;

    this.particleViewMaps = new Map();
    let pecId = this.generateID();
    let innerPecPort = this._pecFactory(pecId);
    this.pec = new OuterPec(innerPecPort, slotComposer, this, `${pecId}:outer`);
    if (slotComposer) {
      slotComposer.arc = this;
    }
    this._storageProviderFactory = new StorageProviderFactory(this);

    // Dictionary from each tag string to a list of views
    this._tags = {};
    // Map from each view to a list of tags.
    this._viewTags = new Map();
    // Map from each view to its description (originating in the manifest).
    this._viewDescriptions = new Map();

    this._search = null;
    this._description = new Description(this);
  }
  get loader() {
    return this._loader;
  }

  get scheduler() {
    return this._scheduler;
  }

  set search(search) {
    this._search = search ? search.toLowerCase().trim() : null;
  }

  get search() {
    return this._search;
  }

  get description() { return this._description; }

  get makeSuggestions() { return this._makeSuggestions; }
  set makeSuggestions(callback) {
    this._makeSuggestions = callback;
    this._scheduler.idleCallback = callback;
  }

  serialize() {
    return `
meta
  name: '${this.id}'

@active
${this.activeRecipe.toString()}`;
  }

  static async deserialize({serialization, pecFactory, slotComposer, loader}) {
    let manifest = await Manifest.parse(serialization, {loader});
    var arc = new Arc({id: manifest.meta.name, slotComposer, pecFactory, loader});
    var recipe = manifest.activeRecipe.clone();
    recipe.normalize();
    arc.instantiate(recipe);
    return arc;
  }

  get context() {
    return this._context;
  }

  get activeRecipe() { return this._activeRecipe; }
  get recipes() { return this._recipes; }

  loadedParticles() {
    return [...this.particleViewMaps.values()].map(({spec}) => spec);
  }

  _instantiateParticle(recipeParticle) {
    let id = this.generateID();
    let viewMap = {spec: recipeParticle.spec, views: new Map()};
    this.particleViewMaps.set(id, viewMap);

    for (let [name, connection] of Object.entries(recipeParticle.connections)) {
      if (!connection.view) {
        assert(connection.isOptional);
        continue;
      }
      let view = this.findViewById(connection.view.id);
      assert(view);
      this._connectParticleToView(id, recipeParticle, name, view);
    }

    // At least all non-optional connections must be resolved
    assert(viewMap.views.size >= viewMap.spec.connections.filter(c => !c.isOptional).length,
           `Not all mandatory connections are resolved for {$particle}`);
    this.pec.instantiate(recipeParticle, id, viewMap.spec, viewMap.views, this._lastSeenVersion);
    return id;
  }

  generateID() {
    return `${this.id}:${this._nextLocalID++}`;
  }

  generateIDComponents() {
    return {base: this.id, component: () => this._nextLocalID++};
  }

  get _views() {
    return [...this._viewsById.values()];
  }

  // Makes a copy of the arc used for speculative execution.
  async cloneForSpeculativeExecution() {
    var arc = new Arc({id: this.generateID(), pecFactory: this._pecFactory, context: this.context, loader: this._loader});
    arc._scheduler = this._scheduler.clone();
    var viewMap = new Map();
    for (let v of this._views) {
      let clone = await arc._storageProviderFactory.construct(v.id, v.type, 'in-memory');
      await clone.cloneFrom(v);
      viewMap.set(v, clone);
      if (this._viewDescriptions.has(v)) {
        arc._viewDescriptions.set(clone, this._viewDescriptions.get(v));
      }
    };
    this.particleViewMaps.forEach((value, key) => {
      arc.particleViewMaps.set(key, {
        spec: value.spec,
        views: new Map()
      });
      value.views.forEach(v => arc.particleViewMaps.get(key).views.set(v.name, viewMap.get(v)));
    });

   let {particles, views, slots} = this._activeRecipe.mergeInto(arc._activeRecipe);
   let particleIndex = 0, viewIndex = 0, slotIndex = 0;
   this._recipes.forEach(recipe => {
     let arcRecipe = {particles: [], views: [], slots: [], innerArcs: new Map()};
     recipe.particles.forEach(p => {
       arcRecipe.particles.push(particles[particleIndex++]);
       if (recipe.innerArcs.has(p)) {
         let thisInnerArc = recipe.innerArcs.get(p);
         let transformationParticle = arcRecipe.particles[arcRecipe.particles.length - 1];
         let innerArc = {activeRecipe: new Recipe(), recipes: []};
         let innerTuples = thisInnerArc.activeRecipe.mergeInto(innerArc.activeRecipe);
         thisInnerArc.recipes.forEach(thisInnerArcRecipe => {
           let innerArcRecipe = {particles: [], views: [], slots: [], innerArcs: new Map()};
           let innerIndex = 0;
           thisInnerArcRecipe.particles.forEach(thisInnerArcRecipeParticle => {
             innerArcRecipe.particles.push(innerTuples.particles[innerIndex++]);
           });
           innerIndex = 0;
           thisInnerArcRecipe.views.forEach(thisInnerArcRecipeParticle => {
             innerArcRecipe.views.push(innerTuples.views[innerIndex++]);
           });
           innerIndex = 0;
           thisInnerArcRecipe.slots.forEach(thisInnerArcRecipeParticle => {
             innerArcRecipe.slots.push(innerTuples.slots[innerIndex++]);
           });
           innerArc.recipes.push(innerArcRecipe);
         });
         arcRecipe.innerArcs.set(transformationParticle, innerArc);
       }
     });
     recipe.views.forEach(p => {
       arcRecipe.views.push(views[viewIndex++]);
     });
     recipe.slots.forEach(p => {
       arcRecipe.slots.push(slots[slotIndex++]);
     });

     arc._recipes.push(arcRecipe);
   });

    for (let v of viewMap.values()) {
      // FIXME: Tags
      arc._registerView(v, []);
    }
    return arc;
  }

  async instantiate(recipe, innerArc) {
    assert(recipe.isResolved(), 'Cannot instantiate an unresolved recipe');

    let currentArc = {activeRecipe: this._activeRecipe, recipes: this._recipes};
    if (innerArc) {
      let innerArcs = this._recipes.find(r => !!r.particles.find(p => p == innerArc.particle)).innerArcs;
      if (!innerArcs.has(innerArc.particle)) {
         innerArcs.set(innerArc.particle, {activeRecipe: new Recipe(), recipes: []});
      }
      currentArc = innerArcs.get(innerArc.particle);
    }
    let {views, particles, slots} = recipe.mergeInto(currentArc.activeRecipe);
    currentArc.recipes.push({particles, views, slots, innerArcs: new Map()});

    for (let recipeView of views) {
      if (['copy', 'create'].includes(recipeView.fate)) {
        let view = await this.createView(recipeView.type, /* name= */ null, this.generateID(), recipeView.tags);
        if (recipeView.fate === 'copy') {
          var copiedView = this.findViewById(recipeView.id);
          await view.cloneFrom(copiedView);
          let copiedViewDesc = this.getViewDescription(copiedView);
          if (copiedViewDesc) {
            this._viewDescriptions.set(view, copiedViewDesc);
          }
        }
        recipeView.id = view.id;
        recipeView.fate = 'use';
        recipeView.storageKey = view.storageKey;
        // TODO: move the call to OuterPEC's DefineView to here
      }
      let storageKey = recipeView.storageKey;
      if (!storageKey)
        storageKey = this.keyForId(recipeView.id);
      assert(storageKey, `couldn't find storage key for view '${recipeView}'`);
      let view = await this._storageProviderFactory.connect(recipeView.id, recipeView.type, storageKey);
      assert(view, `view '${recipeView.id}' was not found`);
    }

    particles.forEach(recipeParticle => this._instantiateParticle(recipeParticle));

    if (this.pec.slotComposer) {
      // TODO: pass slot-connections instead
      this.pec.slotComposer.initializeRecipe(particles);
    }
  }

  _connectParticleToView(particleId, particle, name, targetView) {
    assert(targetView, 'no target view provided');
    var viewMap = this.particleViewMaps.get(particleId);
    assert(viewMap.spec.connectionMap.get(name) !== undefined, 'can\'t connect view to a view slot that doesn\'t exist');
    viewMap.views.set(name, targetView);
  }

  async createView(type, name, id, tags, storageKey) {
    assert(type instanceof Type, `can't createView with type ${type} that isn't a Type`);

    if (type.isRelation) {
      type = Type.newSetView(type);
    }

    if (id == undefined)
      id = this.generateID();

    if (storageKey == undefined && this._storageKey)
      storageKey = this._storageProviderFactory.parseStringAsKey(this._storageKey).childKeyForHandle(id).toString();

    if (storageKey == undefined)
      storageKey = 'in-memory';

    let view = await this._storageProviderFactory.construct(id, type, storageKey);
    assert(view, 'handle with id ${id} already exists');
    view.name = name;

    this._registerView(view, tags);
    return view;
  }

  _registerView(view, tags) {
    tags = tags || [];
    tags = Array.isArray(tags) ? tags : [tags];
    tags.forEach(tag => assert(tag.startsWith('#'),
      `tag ${tag} must start with '#'`));

    this._viewsById.set(view.id, view);
    let byType = this._viewsByType.get(Arc._viewKey(view.type)) || [];
    byType.push(view);
    this._viewsByType.set(Arc._viewKey(view.type), byType);

    if (tags.length) {
      for (let tag of tags) {
        if (this._tags[tag] == undefined)
          this._tags[tag] = [];
        this._tags[tag].push(view);
      }
    }
    this._viewTags.set(view, new Set(tags));

    this._storageKeys[view.id] = view.storageKey;
  }

  // TODO: Don't use this, we should be testing the schemas for compatiblity
  //       instead of using just the name.
  static _viewKey(type) {
    if (type.isSetView) {
      return `list:${type.primitiveType().entitySchema.name}`;
    } else if (type.isEntity) {
      return type.entitySchema.name;
    } else if (type.isShape) {
      // TODO we need to fix this too, otherwise all views of shape type will
      // be of the 'same type' when searching by type.
      return type.shapeShape;
    }
  }

  findViewsByType(type, options) {
    // TODO: use options (location, labels, etc.) somehow.
    var views = this._viewsByType.get(Arc._viewKey(type)) || [];
    if (options && options.tags) {
      views = views.filter(view => options.tags.filter(tag => !this._viewTags.get(view).has(tag)).length == 0);
    }
    return views;
  }

  findViewById(id) {
    let view = this._viewsById.get(id);
    if (view == null) {
      view = this._context.findViewById(id);
    }
    return view;
  }

  getViewDescription(view) {
    assert(view, 'Cannot fetch description for nonexistend view');
    return this._viewDescriptions.get(view) || view.description;
  }

  keyForId(id) {
    return this._storageKeys[id];
  }

  newCommit(entityMap) {
    for (let [entity, view] of entityMap.entries()) {
      entity.identify(this.generateID());
    }
    for (let [entity, view] of entityMap.entries()) {
      new handle.handleFor(view).store(entity);
    }
  }

  stop() {
    this.pec.stop();
  }

  toContextString(options) {
    let results = [];
    let views = [...this._viewsById.values()].sort(util.compareComparables);
    views.forEach(v => {
      results.push(v.toString(this._viewTags.get(v)));
    });

    // TODO: include views entities
    // TODO: include (remote) slots?

    if (!this._activeRecipe.isEmpty()) {
      results.push(this._activeRecipe.toString());
    }

    return results.join('\n');
  }
}

export default Arc;
<|endoftext|>