<commit_before>'use strict';

const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const commonTags = require('common-tags');
const fsExtra = require('fs-extra');
const NpmRegistryClient = require('npm-registry-client');
const conventionalCommitsParser = require('conventional-commits-parser');
const semver = require('semver');

function promisify(fn) {
  return function () {
    const args = Array.prototype.slice.call(arguments);
    return new Promise((resolve, reject) => {
      fn.apply(null, [].concat(args, function () {
        const args = Array.prototype.slice.call(arguments);
        if (args[0]) {
          return reject(args[0]);
        }
        resolve.apply(null, args.slice(1));
      }));
    });
  };
}

const fsReaddir = promisify(fs.readdir);
const fsReadfile = promisify(fs.readFile);
const fsOutputFile = promisify(fsExtra.outputFile);
const fsCopy = promisify(fsExtra.copy);
const fsMove = promisify(fsExtra.move);
const fsRemove = promisify(fsExtra.remove);
const fsReadJson = promisify(fsExtra.readJson);
const fsWriteJson = promisify(fsExtra.writeJson);

const packagesDirectory = path.join(process.cwd(), 'packages');

function forEach(list, task) {
  return list.reduce((promise, entry) => promise.then(() => task(entry)), Promise.resolve());
}

function getPackages() {
  return fsReaddir(packagesDirectory);
}

function readAllPackageJsonFiles(list) {
  return Promise.all(list.map(file => getPackageJson(file)));
}

function sortDependencys(list, pkgs) {
  list.sort((left, right) => {
    let pkg = pkgs.find(pkg => right === pkg.name);
    if (left in pkg.devDependencies || left in pkg.dependencies) {
      return -1;
    }
    pkg = pkgs.find(pkg => left === pkg.name);
    if (right in pkg.devDependencies || right in pkg.dependencies) {
      return 1;
    }
    return 0;
  });
  return list;
}

function getOrderedPackages() {
  return getPackages()
    .then(packages =>
      readAllPackageJsonFiles(packages)
        .then(pkgs => ({packages, pkgs})))
    .then(context => sortDependencys(context.packages, context.pkgs));
}

function getDependents(packageDir) {
  return getPackages()
    .then(packages => readAllPackageJsonFiles(packages).then(pkgs => ({packages, pkgs})))
    .then(context => context.packages.filter(name => {
      const pkg = context.pkgs.find(pkg => name === pkg.name);
      return packageDir in pkg.devDependencies || packageDir in pkg.dependencies;
    }));
}

function getPackageJson(packageDir) {
  return fsReadJson(path.join(packagesDirectory, packageDir, 'package.json'));
}

function patchPackageJson(pkg) {
  return getPackages()
    .then(packages => {
      packages.forEach(file => delete pkg.devDependencies[file]);
      packages.forEach(file => delete pkg.dependencies[file]);
    })
    .then(() => pkg);
}

function npm(packageDir, command) {
  return Promise.resolve()
    .then(() => {
      const opts = {
        cwd: path.join(packagesDirectory, packageDir),
        env: process.env,
        stdio: 'inherit'
      };
      childProcess.execSync(`npm ${command}`, opts);
    });
}

function getPackageDependencies(pkg) {
  return getPackages()
    .then(packages => {
      return [].concat(
        packages.filter(file => file in pkg.devDependencies),
        packages.filter(file => file in pkg.dependencies)
      );
    });
}

function linkDependencies(packageDir) {
  return getPackages()
    .then(() => {
      return getPackageJson(packageDir)
        .then(pkg => getPackageDependencies(pkg))
        .then(dependencies => {
          return forEach(dependencies,
              dependency => {
                const dependecyModulPath = path.join(packagesDirectory, packageDir, 'node_modules', dependency);
                return fsOutputFile(path.join(dependecyModulPath, 'index.js'),
                    `module.exports = require('../../../${dependency}/')`)
                  .then(() => fsOutputFile(path.join(dependecyModulPath, 'index.d.ts'),
                    `export * from '../../../${dependency}/index.d.ts';`));
              });
        });
    });
}

function withPatchedPackageJson(packageDir, fn) {
  const packageJsonPath = path.join(packagesDirectory, packageDir, 'package.json');
  const packageJsonBackupPath = path.join(packagesDirectory, packageDir, 'package.json.orig');
  return fsCopy(packageJsonPath, packageJsonBackupPath)
    .then(() => {
      return fsReadJson(packageJsonPath)
        .then(pkg => patchPackageJson(pkg))
        .then(pkg => fsWriteJson(packageJsonPath, pkg))
        .then(() => fn())
        .catch(err => {
          return fsMove(packageJsonBackupPath, packageJsonPath, {clobber: true})
            .then(() => {
              throw err;
            });
        });
    })
    .then(() => fsMove(packageJsonBackupPath, packageJsonPath, {clobber: true}));
}

function remoteNpmGet(packageDir) {
  return new Promise((resolve, reject) => {
    const noop = () => undefined;
    const client = new NpmRegistryClient({
      log: {
        error: noop,
        warn: noop,
        info: noop,
        verbose: noop,
        silly: noop,
        http: noop,
        pause: noop,
        resume: noop
      }
    });
    const params = {
      timeout: 1000
    };
    return client
      .get(`https://registry.npmjs.org/${packageDir}`, params, (err, data) => {
        if (err) {
          if (err.statusCode === 404) {
            return resolve(undefined);
          }
          return reject(err);
        }
        resolve(data);
      });
  });
}

function getReleaseData(packageDir, npm) {
  return Promise.resolve()
    .then(() => getPackageJson(packageDir)
        .then(pkg => ({
          npm,
          pkg
        })))
    .then(data => {
      data.tag = (data.pkg.publishConfig || {}).tag || 'latest';
      if (data.npm) {
        data.lastVersion = data.npm['dist-tags'][data.tag];
        if (data.lastVersion) {
          data.lastGitHash = data.npm.versions[data.lastVersion].gitHead;
        }
      }
      return data;
    })
    .then(data => {
      if (!data.lastGitHash) {
        return git(packageDir, 'rev-list --abbrev-commit --max-parents=0 HEAD')
          .then(firstGitHash => {
            data.lastGitHash = firstGitHash;
            return data;
          });
      }
      return data;
    });
}

function getReleaseCommits(packageDir, data) {
  return Promise.resolve()
    .then(() => {
      return git(packageDir, `log -E --format=%B==END== ${data.lastGitHash}..HEAD`)
        .then(stdout => stdout.split('==END==\n'))
        .then(commits => commits.filter(commit => Boolean(commit.trim())))
        .then(commits => {
          const parsedCommits = [];
          const parser = conventionalCommitsParser();
          parser.on('data', parsed => parsedCommits.push(parsed));
          commits.forEach(commit => parser.write(commit));
          parser.end();
          return parsedCommits;
        })
        .then(commits => commits.filter(commit => commit.scope === packageDir))
        .then(commits => {
          data.commits = commits;
          data.requireRelease = data.commits.length > 0;
          return data;
        });
    });
}

function getNextVersion(packageDir, data) {
  const releases = ['patch', 'minor', 'major'];
  const typeToReleaseIndex = {
    fix: 0,
    feat: 1
  };

  return Promise.resolve()
    .then(() => {
      const relaseIndex = data.commits.reduce((relase, commit) => {
        let result = relase > (typeToReleaseIndex[commit.type] || 0) ? relase : typeToReleaseIndex[commit.type];
        if (commit.footer && commit.footer.indexOf('BREAKING CHANGE:\n') > -1) {
          result = 2;
        }
        return result;
      }, 0);
      data.release = releases[relaseIndex];
      if (data.lastVersion) {
        data.nextVersion = semver.inc(data.lastVersion, data.release);
      } else {
        data.nextVersion = data.pkg.version;
      }
      return data;
    });
}

function git(packageDir, command) {
  return Promise.resolve()
    .then(() => {
      const opts = {
        cwd: path.join(packagesDirectory, packageDir),
        env: process.env
      };
      return childProcess.execSync(`git ${command}`, opts);
    })
    .then(buffer => buffer.toString().trim());
}

function runOnPackages(commands, command, args) {
  return getOrderedPackages()
    .then(packages => {
      return forEach(packages, file => {
        return commands[command].apply(null, [].concat([file], args));
      });
    });
}

function runCommandBootstrap(packageDir) {
  return Promise.resolve()
    .then(() => {
      return withPatchedPackageJson(packageDir, () => {
        return npm(packageDir, 'install')
          .then(() => npm(packageDir, 'prune'));
      })
      .then(() => linkDependencies(packageDir));
    });
}

function runCommandReset(packageDir) {
  return fsRemove(path.join(packagesDirectory, packageDir, 'node_modules'));
}

function updatePackageJson(packageDir, fn) {
  const packageJson = path.join(packagesDirectory, packageDir, 'package.json');
  return fsReadfile(packageJson)
    .then(buffer => buffer.toString())
    .then(data => fn(data))
    .then(data => fsOutputFile(packageJson, data));
}

function incrementPackageVersion(packageDir, data) {
  return updatePackageJson(packageDir, content =>
    content.replace(/^(\s*"version"\s*:\s*")\d+(?:\.\d+(?:\.\d+)?)?("\s*(?:,\s*)?)$/gm, `$1${data.nextVersion}$2`));
}

function updateDependents(packageDir, data) {
  return getDependents(packageDir)
    .then(dependents => forEach(dependents, dependent =>
      updatePackageJson(dependent, content =>
        content.replace(new RegExp(`^(\\s*"${packageDir}"\\s*:\\s*")\\d+(?:\\.\\d+(?:\\.\\d+)?)?("\\s*(?:,\\s*)?)$`, 'gm'),
          `$1${data.nextVersion}$2`))));
}

function runCommandRelease(packageDir) {
  return git('..', `status --porcelain`)
    .then(stdout => {
      if (stdout !== '') {
        throw new Error('Git workspace not clean!');
      }
    })
    .then(() => remoteNpmGet(packageDir))
    .then(npm => getReleaseData(packageDir, npm))
    .then(data => getReleaseCommits(packageDir, data))
    .then(data => getNextVersion(packageDir, data))
    .then(data => {
      if (data.requireRelease) {
        return incrementPackageVersion(packageDir, data)
          .then(() => runCommandNpmRun(packageDir, 'release'))
          .then(() => updateDependents(packageDir, data))
          .then(() => git('..', `status --porcelain`))
          .then(stdout => {
            if (stdout !== '') {
              return git('..', `add .`)
                .then(() => git('..', `commit -m "chore(${packageDir}): releases ${data.nextVersion}"`));
            }
          })
          .then(() => git('..', `tag ${packageDir}-${data.nextVersion}`));
      }
      console.log(`No release for ${packageDir} required`);
      return data;
    });
}

function runCommandNpmRun(packageDir, task) {
  return Promise.resolve()
    .then(() => getPackageJson(packageDir))
    .then(pkg => task in pkg.scripts)
    .then(hasTask => hasTask ? npm(packageDir, `run ${task}`) : console.log(`No ${task} script for ${packageDir}`));
}

function runCommandNpm(packageDir, args) {
  return Promise.resolve()
    .then(() => withPatchedPackageJson(packageDir, () => {
      return npm(packageDir, args.join(' '));
    }));
}

const commands = {
  bootstrap(packageDir) {
    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Bootstrapping ${packageDir}

      -------------------------------------------------------------------------------
    `}\n`);
    return runCommandBootstrap(packageDir);
  },
  reset(packageDir) {
    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Reset ${packageDir}

      -------------------------------------------------------------------------------
    `}\n`);
    return runCommandReset(packageDir);
  },
  release(packageDir) {
    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Release ${packageDir}

      -------------------------------------------------------------------------------
    `}\n`);
    return runCommandRelease(packageDir);
  },
  run(packageDir, task) {
    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Running npm script '${task}' in ${packageDir}

      -------------------------------------------------------------------------------
    `}\n`);
    return runCommandNpmRun(packageDir, task);
  },
  npm(packageDir) {
    const args = Array.prototype.slice.call(arguments).slice(1);

    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Running 'npm ${args.join(' ')}' in ${packageDir}

      -------------------------------------------------------------------------------
    `}\n`);
    return runCommandNpm(packageDir, args);
  }
};

if (process.argv.length < 3) {
  console.error('Missing task');
  process.exit(1); // eslint-disable-line xo/no-process-exit
}

const command = process.argv[2];
const commandArguments = process.argv.slice(3);
const start = new Date().getTime();
runOnPackages(commands, command, commandArguments)
  .then(() => {
    const end = new Date().getTime();
    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Successful command: ${command} (${((end - start) / 1000)}s)

      -------------------------------------------------------------------------------
    `}\n`);
  })
  .catch(err => {
    const end = new Date().getTime();
    console.error(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Failed command: ${command}  (${((end - start) / 1000)}s)
          ${err.toString()}

      -------------------------------------------------------------------------------
    `}\n`);
    if (err.stack) {
      console.error(err.stack);
    }
    process.exit(1); // eslint-disable-line xo/no-process-exit
  });
<commit_msg>chore(paeckchen): use tag if no git hash is in npm data (#115)<commit_after>'use strict';

const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const commonTags = require('common-tags');
const fsExtra = require('fs-extra');
const NpmRegistryClient = require('npm-registry-client');
const conventionalCommitsParser = require('conventional-commits-parser');
const semver = require('semver');

function promisify(fn) {
  return function () {
    const args = Array.prototype.slice.call(arguments);
    return new Promise((resolve, reject) => {
      fn.apply(null, [].concat(args, function () {
        const args = Array.prototype.slice.call(arguments);
        if (args[0]) {
          return reject(args[0]);
        }
        resolve.apply(null, args.slice(1));
      }));
    });
  };
}

const fsReaddir = promisify(fs.readdir);
const fsReadfile = promisify(fs.readFile);
const fsOutputFile = promisify(fsExtra.outputFile);
const fsCopy = promisify(fsExtra.copy);
const fsMove = promisify(fsExtra.move);
const fsRemove = promisify(fsExtra.remove);
const fsReadJson = promisify(fsExtra.readJson);
const fsWriteJson = promisify(fsExtra.writeJson);

const packagesDirectory = path.join(process.cwd(), 'packages');

function forEach(list, task) {
  return list.reduce((promise, entry) => promise.then(() => task(entry)), Promise.resolve());
}

function getPackages() {
  return fsReaddir(packagesDirectory);
}

function readAllPackageJsonFiles(list) {
  return Promise.all(list.map(file => getPackageJson(file)));
}

function sortDependencys(list, pkgs) {
  list.sort((left, right) => {
    let pkg = pkgs.find(pkg => right === pkg.name);
    if (left in pkg.devDependencies || left in pkg.dependencies) {
      return -1;
    }
    pkg = pkgs.find(pkg => left === pkg.name);
    if (right in pkg.devDependencies || right in pkg.dependencies) {
      return 1;
    }
    return 0;
  });
  return list;
}

function getOrderedPackages() {
  return getPackages()
    .then(packages =>
      readAllPackageJsonFiles(packages)
        .then(pkgs => ({packages, pkgs})))
    .then(context => sortDependencys(context.packages, context.pkgs));
}

function getDependents(packageDir) {
  return getPackages()
    .then(packages => readAllPackageJsonFiles(packages).then(pkgs => ({packages, pkgs})))
    .then(context => context.packages.filter(name => {
      const pkg = context.pkgs.find(pkg => name === pkg.name);
      return packageDir in pkg.devDependencies || packageDir in pkg.dependencies;
    }));
}

function getPackageJson(packageDir) {
  return fsReadJson(path.join(packagesDirectory, packageDir, 'package.json'));
}

function patchPackageJson(pkg) {
  return getPackages()
    .then(packages => {
      packages.forEach(file => delete pkg.devDependencies[file]);
      packages.forEach(file => delete pkg.dependencies[file]);
    })
    .then(() => pkg);
}

function npm(packageDir, command) {
  return Promise.resolve()
    .then(() => {
      const opts = {
        cwd: path.join(packagesDirectory, packageDir),
        env: process.env,
        stdio: 'inherit'
      };
      childProcess.execSync(`npm ${command}`, opts);
    });
}

function getPackageDependencies(pkg) {
  return getPackages()
    .then(packages => {
      return [].concat(
        packages.filter(file => file in pkg.devDependencies),
        packages.filter(file => file in pkg.dependencies)
      );
    });
}

function linkDependencies(packageDir) {
  return getPackages()
    .then(() => {
      return getPackageJson(packageDir)
        .then(pkg => getPackageDependencies(pkg))
        .then(dependencies => {
          return forEach(dependencies,
              dependency => {
                const dependecyModulPath = path.join(packagesDirectory, packageDir, 'node_modules', dependency);
                return fsOutputFile(path.join(dependecyModulPath, 'index.js'),
                    `module.exports = require('../../../${dependency}/')`)
                  .then(() => fsOutputFile(path.join(dependecyModulPath, 'index.d.ts'),
                    `export * from '../../../${dependency}/index.d.ts';`));
              });
        });
    });
}

function withPatchedPackageJson(packageDir, fn) {
  const packageJsonPath = path.join(packagesDirectory, packageDir, 'package.json');
  const packageJsonBackupPath = path.join(packagesDirectory, packageDir, 'package.json.orig');
  return fsCopy(packageJsonPath, packageJsonBackupPath)
    .then(() => {
      return fsReadJson(packageJsonPath)
        .then(pkg => patchPackageJson(pkg))
        .then(pkg => fsWriteJson(packageJsonPath, pkg))
        .then(() => fn())
        .catch(err => {
          return fsMove(packageJsonBackupPath, packageJsonPath, {clobber: true})
            .then(() => {
              throw err;
            });
        });
    })
    .then(() => fsMove(packageJsonBackupPath, packageJsonPath, {clobber: true}));
}

function remoteNpmGet(packageDir) {
  return new Promise((resolve, reject) => {
    const noop = () => undefined;
    const client = new NpmRegistryClient({
      log: {
        error: noop,
        warn: noop,
        info: noop,
        verbose: noop,
        silly: noop,
        http: noop,
        pause: noop,
        resume: noop
      }
    });
    const params = {
      timeout: 1000
    };
    return client
      .get(`https://registry.npmjs.org/${packageDir}`, params, (err, data) => {
        if (err) {
          if (err.statusCode === 404) {
            return resolve(undefined);
          }
          return reject(err);
        }
        resolve(data);
      });
  });
}

function getReleaseData(packageDir, npm) {
  return Promise.resolve()
    .then(() => getPackageJson(packageDir)
        .then(pkg => ({
          npm,
          pkg
        })))
    .then(data => {
      data.tag = (data.pkg.publishConfig || {}).tag || 'latest';
      if (data.npm) {
        data.lastVersion = data.npm['dist-tags'][data.tag];
        if (data.lastVersion) {
          const npmVersionData = data.npm.versions[data.lastVersion];
          data.lastGitHash = npmVersionData.gitHead || `${npmVersionData.name}-${npmVersionData.version}`;
        }
      }
      return data;
    })
    .then(data => {
      if (!data.lastGitHash) {
        return git(packageDir, 'rev-list --abbrev-commit --max-parents=0 HEAD')
          .then(firstGitHash => {
            data.lastGitHash = firstGitHash;
            return data;
          });
      }
      return data;
    });
}

function getReleaseCommits(packageDir, data) {
  return Promise.resolve()
    .then(() => {
      return git(packageDir, `log -E --format=%B==END== ${data.lastGitHash}..HEAD`)
        .then(stdout => stdout.split('==END==\n'))
        .then(commits => commits.filter(commit => Boolean(commit.trim())))
        .then(commits => {
          const parsedCommits = [];
          const parser = conventionalCommitsParser();
          parser.on('data', parsed => parsedCommits.push(parsed));
          commits.forEach(commit => parser.write(commit));
          parser.end();
          return parsedCommits;
        })
        .then(commits => commits.filter(commit => commit.scope === packageDir))
        .then(commits => {
          data.commits = commits;
          data.requireRelease = data.commits.length > 0;
          return data;
        });
    });
}

function getNextVersion(packageDir, data) {
  const releases = ['patch', 'minor', 'major'];
  const typeToReleaseIndex = {
    fix: 0,
    feat: 1
  };

  return Promise.resolve()
    .then(() => {
      const relaseIndex = data.commits.reduce((relase, commit) => {
        let result = relase > (typeToReleaseIndex[commit.type] || 0) ? relase : typeToReleaseIndex[commit.type];
        if (commit.footer && commit.footer.indexOf('BREAKING CHANGE:\n') > -1) {
          result = 2;
        }
        return result;
      }, 0);
      data.release = releases[relaseIndex];
      if (data.lastVersion) {
        data.nextVersion = semver.inc(data.lastVersion, data.release);
      } else {
        data.nextVersion = data.pkg.version;
      }
      return data;
    });
}

function git(packageDir, command) {
  return Promise.resolve()
    .then(() => {
      const opts = {
        cwd: path.join(packagesDirectory, packageDir),
        env: process.env
      };
      return childProcess.execSync(`git ${command}`, opts);
    })
    .then(buffer => buffer.toString().trim());
}

function runOnPackages(commands, command, args) {
  return getOrderedPackages()
    .then(packages => {
      return forEach(packages, file => {
        return commands[command].apply(null, [].concat([file], args));
      });
    });
}

function runCommandBootstrap(packageDir) {
  return Promise.resolve()
    .then(() => {
      return withPatchedPackageJson(packageDir, () => {
        return npm(packageDir, 'install')
          .then(() => npm(packageDir, 'prune'));
      })
      .then(() => linkDependencies(packageDir));
    });
}

function runCommandReset(packageDir) {
  return fsRemove(path.join(packagesDirectory, packageDir, 'node_modules'));
}

function updatePackageJson(packageDir, fn) {
  const packageJson = path.join(packagesDirectory, packageDir, 'package.json');
  return fsReadfile(packageJson)
    .then(buffer => buffer.toString())
    .then(data => fn(data))
    .then(data => fsOutputFile(packageJson, data));
}

function incrementPackageVersion(packageDir, data) {
  return updatePackageJson(packageDir, content =>
    content.replace(/^(\s*"version"\s*:\s*")\d+(?:\.\d+(?:\.\d+)?)?("\s*(?:,\s*)?)$/gm, `$1${data.nextVersion}$2`));
}

function updateDependents(packageDir, data) {
  return getDependents(packageDir)
    .then(dependents => forEach(dependents, dependent =>
      updatePackageJson(dependent, content =>
        content.replace(new RegExp(`^(\\s*"${packageDir}"\\s*:\\s*")\\d+(?:\\.\\d+(?:\\.\\d+)?)?("\\s*(?:,\\s*)?)$`, 'gm'),
          `$1${data.nextVersion}$2`))));
}

function runCommandRelease(packageDir) {
  return git('..', `status --porcelain`)
    .then(stdout => {
      if (stdout !== '') {
        throw new Error('Git workspace not clean!');
      }
    })
    .then(() => remoteNpmGet(packageDir))
    .then(npm => getReleaseData(packageDir, npm))
    .then(data => getReleaseCommits(packageDir, data))
    .then(data => getNextVersion(packageDir, data))
    .then(data => {
      if (data.requireRelease) {
        return incrementPackageVersion(packageDir, data)
          .then(() => runCommandNpmRun(packageDir, 'release'))
          .then(() => updateDependents(packageDir, data))
          .then(() => git('..', `status --porcelain`))
          .then(stdout => {
            if (stdout !== '') {
              return git('..', `add .`)
                .then(() => git('..', `commit -m "chore(${packageDir}): releases ${data.nextVersion}"`));
            }
          })
          .then(() => git('..', `tag ${packageDir}-${data.nextVersion}`));
      }
      console.log(`No release for ${packageDir} required`);
      return data;
    });
}

function runCommandNpmRun(packageDir, task) {
  return Promise.resolve()
    .then(() => getPackageJson(packageDir))
    .then(pkg => task in pkg.scripts)
    .then(hasTask => hasTask ? npm(packageDir, `run ${task}`) : console.log(`No ${task} script for ${packageDir}`));
}

function runCommandNpm(packageDir, args) {
  return Promise.resolve()
    .then(() => withPatchedPackageJson(packageDir, () => {
      return npm(packageDir, args.join(' '));
    }));
}

const commands = {
  bootstrap(packageDir) {
    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Bootstrapping ${packageDir}

      -------------------------------------------------------------------------------
    `}\n`);
    return runCommandBootstrap(packageDir);
  },
  reset(packageDir) {
    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Reset ${packageDir}

      -------------------------------------------------------------------------------
    `}\n`);
    return runCommandReset(packageDir);
  },
  release(packageDir) {
    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Release ${packageDir}

      -------------------------------------------------------------------------------
    `}\n`);
    return runCommandRelease(packageDir);
  },
  run(packageDir, task) {
    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Running npm script '${task}' in ${packageDir}

      -------------------------------------------------------------------------------
    `}\n`);
    return runCommandNpmRun(packageDir, task);
  },
  npm(packageDir) {
    const args = Array.prototype.slice.call(arguments).slice(1);

    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Running 'npm ${args.join(' ')}' in ${packageDir}

      -------------------------------------------------------------------------------
    `}\n`);
    return runCommandNpm(packageDir, args);
  }
};

if (process.argv.length < 3) {
  console.error('Missing task');
  process.exit(1); // eslint-disable-line xo/no-process-exit
}

const command = process.argv[2];
const commandArguments = process.argv.slice(3);
const start = new Date().getTime();
runOnPackages(commands, command, commandArguments)
  .then(() => {
    const end = new Date().getTime();
    console.log(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Successful command: ${command} (${((end - start) / 1000)}s)

      -------------------------------------------------------------------------------
    `}\n`);
  })
  .catch(err => {
    const end = new Date().getTime();
    console.error(`\n${commonTags.stripIndent`
      -------------------------------------------------------------------------------

        Failed command: ${command}  (${((end - start) / 1000)}s)
          ${err.toString()}

      -------------------------------------------------------------------------------
    `}\n`);
    if (err.stack) {
      console.error(err.stack);
    }
    process.exit(1); // eslint-disable-line xo/no-process-exit
  });
<|endoftext|>


<commit_before>/*****************************************************************************/
/* ProcurementRequisitionApprovalList: Event Handlers */
/*****************************************************************************/
import _ from 'underscore';

Template.ProcurementRequisitionApprovalList.events({
    'click .requisitionRow': function(e, tmpl) {
        e.preventDefault()
        let requisitionId = e.currentTarget.getAttribute('data-RequisitionId')

        let invokeReason = {}
        invokeReason.requisitionId = requisitionId
        invokeReason.reason = 'approve'
        invokeReason.approverId = Meteor.userId()

        Modal.show('ProcurementRequisitionDetail', invokeReason)
    },
    'click .goToPage': function(e, tmpl) {
        let pageNum = e.currentTarget.getAttribute('data-pageNum')
        console.log(`pageNum: ${pageNum}`)
        let pageNumAsInt = parseInt(pageNum)
        let limit = Template.instance().NUMBER_PER_PAGE.get()
        let skip = limit * pageNumAsInt

        let newPageOfProcurements = Template.instance().getProcurementsToApprove(skip)
        Template.instance().procurementsToApprove.set(newPageOfProcurements)

        Template.instance().currentPage.set(pageNumAsInt)
    },
    'click .paginateLeft': function(e, tmpl) {

    },
    'click .paginateRight': function(e, tmpl) {

    }
});

Template.registerHelper('formatDate', function(date) {
    return moment(date).format('DD-MM-YYYY');
});

Template.registerHelper('repeat', function(max) {
    return _.range(max - 1); // undescore.js but every range impl would work
});

/*****************************************************************************/
/* ProcurementRequisitionApprovalList: Helpers */
/*****************************************************************************/
Template.ProcurementRequisitionApprovalList.helpers({
    'procurementsICreated': function() {
        return Template.instance().procurementsToApprove.get()
    },
    'numberOfPages': function() {
        let currentUser = Meteor.user()
        if(currentUser.employeeProfile && currentUser.employeeProfile.employment) {
            let currentUserPosition = currentUser.employeeProfile.employment.position

            let limit = Template.instance().NUMBER_PER_PAGE.get()
            let totalNum = ProcurementRequisitions.find({supervisorPositionId: currentUserPosition}).count();

            let result = Math.floor(totalNum/limit)
            var remainder = totalNum % limit;
            if (remainder > 0)
                result += 2;
            return result;
        }
        return 0;
    },
    'currentPage': function() {
        return Template.instance().currentPage.get()
    },
    'getUnitName': function(unitId) {
        if(unitId)
            return EntityObjects.findOne({_id: unitId}).name
    }
});

/*****************************************************************************/
/* ProcurementRequisitionApprovalList: Lifecycle Hooks */
/*****************************************************************************/
Template.ProcurementRequisitionApprovalList.onCreated(function () {
    let self = this;
    let businessUnitId = Session.get('context')

    self.NUMBER_PER_PAGE = new ReactiveVar(10);
    self.currentPage = new ReactiveVar(0);
    //--
    self.procurementsToApprove = new ReactiveVar()

    self.getProcurementsToApprove = function(skip) {
        let sortBy = "createdAt";
        let sortDirection = -1;

        let options = {};
        options.sort = {};
        options.sort[sortBy] = sortDirection;
        options.limit = self.NUMBER_PER_PAGE.get();
        options.skip = skip

        let currentUser = Meteor.user()
        if(currentUser.employeeProfile && currentUser.employeeProfile.employment) {
            let currentUserPosition = currentUser.employeeProfile.employment.position

            return ProcurementRequisitions.find({
                $or: [{supervisorPositionId : currentUserPosition},
                        {alternativeSupervisorPositionId: currentUserPosition}],
                status: 'Pending'
            }, options);
        }
        return null
    }

    self.subscribe('getCostElement', businessUnitId)

    self.autorun(function() {
        let limit = self.NUMBER_PER_PAGE.get();
        let sortBy = "createdAt";
        let sortDirection =  -1;
        let sort = {};
        sort[sortBy] = sortDirection;

        let procurementsToApproveSub = self.subscribe('ProcurementRequisitionsToApprove', businessUnitId, limit, sort)
        //--
        if(procurementsToApproveSub.ready()) {
            let currentUser = Meteor.user()
            if(currentUser.employeeProfile && currentUser.employeeProfile.employment) {
                let currentUserPosition = currentUser.employeeProfile.employment.position
                self.procurementsToApprove.set(self.getProcurementsToApprove(0))
            }
        }
    })

});

Template.ProcurementRequisitionApprovalList.onRendered(function () {
    $('select.dropdown').dropdown();
    $("html, body").animate({ scrollTop: 0 }, "slow");
});

Template.ProcurementRequisitionApprovalList.onDestroyed(function () {
});
<commit_msg>Logic for displaying requisitions to approve when is first or second approver<commit_after>/*****************************************************************************/
/* ProcurementRequisitionApprovalList: Event Handlers */
/*****************************************************************************/
import _ from 'underscore';

Template.ProcurementRequisitionApprovalList.events({
    'click .requisitionRow': function(e, tmpl) {
        e.preventDefault()
        let requisitionId = e.currentTarget.getAttribute('data-RequisitionId')

        let invokeReason = {}
        invokeReason.requisitionId = requisitionId
        invokeReason.reason = 'approve'
        invokeReason.approverId = Meteor.userId()

        Modal.show('ProcurementRequisitionDetail', invokeReason)
    },
    'click .goToPage': function(e, tmpl) {
        let pageNum = e.currentTarget.getAttribute('data-pageNum')
        console.log(`pageNum: ${pageNum}`)
        let pageNumAsInt = parseInt(pageNum)
        let limit = Template.instance().NUMBER_PER_PAGE.get()
        let skip = limit * pageNumAsInt

        let newPageOfProcurements = Template.instance().getProcurementsToApprove(skip)
        Template.instance().procurementsToApprove.set(newPageOfProcurements)

        Template.instance().currentPage.set(pageNumAsInt)
    },
    'click .paginateLeft': function(e, tmpl) {

    },
    'click .paginateRight': function(e, tmpl) {

    }
});

Template.registerHelper('formatDate', function(date) {
    return moment(date).format('DD-MM-YYYY');
});

Template.registerHelper('repeat', function(max) {
    return _.range(max - 1); // undescore.js but every range impl would work
});

/*****************************************************************************/
/* ProcurementRequisitionApprovalList: Helpers */
/*****************************************************************************/
Template.ProcurementRequisitionApprovalList.helpers({
    'procurementsICreated': function() {
        return Template.instance().procurementsToApprove.get()
    },
    'numberOfPages': function() {
        let currentUser = Meteor.user()
        if(currentUser.employeeProfile && currentUser.employeeProfile.employment) {
            let currentUserPosition = currentUser.employeeProfile.employment.position

            let limit = Template.instance().NUMBER_PER_PAGE.get()
            let totalNum = ProcurementRequisitions.find({supervisorPositionId: currentUserPosition}).count();

            let result = Math.floor(totalNum/limit)
            var remainder = totalNum % limit;
            if (remainder > 0)
                result += 2;
            return result;
        }
        return 0;
    },
    'currentPage': function() {
        return Template.instance().currentPage.get()
    },
    'getUnitName': function(unitId) {
        if(unitId)
            return EntityObjects.findOne({_id: unitId}).name
    }
});

/*****************************************************************************/
/* ProcurementRequisitionApprovalList: Lifecycle Hooks */
/*****************************************************************************/
Template.ProcurementRequisitionApprovalList.onCreated(function () {
    let self = this;
    let businessUnitId = Session.get('context')

    self.businessUnitCustomConfig = new ReactiveVar()

    self.NUMBER_PER_PAGE = new ReactiveVar(10);
    self.currentPage = new ReactiveVar(0);
    //--
    self.procurementsToApprove = new ReactiveVar()

    Meteor.call('BusinessUnitCustomConfig/getConfig', businessUnitId, function(err, res) {
        if(!err) {
            self.businessUnitCustomConfig.set(res)
        }
    })

    self.getProcurementsToApprove = function(skip) {
        let sortBy = "createdAt";
        let sortDirection = -1;

        let options = {};
        options.sort = {};
        options.sort[sortBy] = sortDirection;
        options.limit = self.NUMBER_PER_PAGE.get();
        options.skip = skip

        let currentUser = Meteor.user()
        let businessUnitCustomConfig = self.businessUnitCustomConfig.get()
        if(businessUnitCustomConfig && businessUnitCustomConfig.isTwoStepApprovalEnabled) {
            if(currentUser.employeeProfile && currentUser.employeeProfile.employment) {
                let currentUserPosition = currentUser.employeeProfile.employment.position

                return ProcurementRequisitions.find({
                    $or: [
                        {
                            alternativeSupervisorPositionId: currentUserPosition,
                            $or: [{status : 'PartiallyApproved'}, {status: 'PartiallyRejected'}],
                        },
                        {
                            supervisorPositionId : currentUserPosition,
                            status: 'Pending'
                        }
                    ],
                }, options);
            }
        } else {
            if(currentUser.employeeProfile && currentUser.employeeProfile.employment) {
                let currentUserPosition = currentUser.employeeProfile.employment.position

                return ProcurementRequisitions.find({
                    $or: [{supervisorPositionId : currentUserPosition},
                            {alternativeSupervisorPositionId: currentUserPosition}],
                    status: 'Pending'
                }, options);
            }
        }
        return null
    }

    self.subscribe('getCostElement', businessUnitId)

    self.autorun(function() {
        let limit = self.NUMBER_PER_PAGE.get();
        let sortBy = "createdAt";
        let sortDirection =  -1;
        let sort = {};
        sort[sortBy] = sortDirection;

        let procurementsToApproveSub = self.subscribe('ProcurementRequisitionsToApprove', businessUnitId, limit, sort)
        //--
        if(procurementsToApproveSub.ready()) {
            let currentUser = Meteor.user()
            if(currentUser.employeeProfile && currentUser.employeeProfile.employment) {
                let currentUserPosition = currentUser.employeeProfile.employment.position
                self.procurementsToApprove.set(self.getProcurementsToApprove(0))
            }
        }
    })
});

Template.ProcurementRequisitionApprovalList.onRendered(function () {
    $('select.dropdown').dropdown();
    $("html, body").animate({ scrollTop: 0 }, "slow");
});

Template.ProcurementRequisitionApprovalList.onDestroyed(function () {
});
<|endoftext|>


<commit_before>/**
 * A FreeDOM transport provider using WebRTC
 */
var Transport_unprivileged = function(channel) {
  this.nextId = 10;
  this.channel = channel;
  this.rtcConnections = {};
  this.rtcChannels = {};
  this.rtcStates = {};
  handleEvents(this);
};

Transport_unprivileged.prototype.onStateChange = function(id) {
  var sendChannel = this.rtcChannels[id];
  //console.log(id + " send channel's state:"+sendChannel.readyState);
  this.channel.postMessage({
    'action':'event',
    'type': 'onStateChange',
    'value': {'id': id, 'state': sendChannel.readyState}
  });
};

Transport_unprivileged.prototype.onMessage = function(id, evt) {
  console.log(id + " message: "+evt.data.length);
  var blobber = new Blob([evt.data]);
  console.log("as blob it is length " + blobber.size);

  this.channel.postMessage({
    'action':'event',
    'type': 'onMessage',
    'value': {'header': id, 'data': new Blob([evt.data])}
  });
};

Transport_unprivileged.prototype.onIceCandidate = function(id, evt) {
  //console.log(id + " icecandidate: "+JSON.stringify(evt));
  if (evt && evt.candidate) {
    this.channel.postMessage({
      'action': 'event',
      'type': 'onSignal',
      'value': {'id': id, 'message': JSON.stringify(evt)}
    });
  }
}

Transport_unprivileged.prototype['create'] = function (continuation) {
  var sockId = this.nextId++;
  var servers = null;
  var sendChannel;
  var pc = new webkitRTCPeerConnection(servers,
                {'optional': [{'RtpDataChannels': true}]});
  this.rtcConnections[sockId] = pc;
  try {
    sendChannel = pc['createDataChannel']("sendDataChannel", {'reliable': false});
    this.rtcChannels[sockId] = sendChannel;
    sendChannel.onopen = this.onStateChange.bind(this,sockId);
    sendChannel.onclose = this.onStateChange.bind(this,sockId);
    sendChannel.onmessage = this.onMessage.bind(this,sockId);
    pc['onicecandidate'] = this.onIceCandidate.bind(this,sockId);

    pc['createOffer'](function(desc) {
      pc['setLocalDescription'](desc);
      continuation({'id': sockId, 'offer': JSON.stringify(desc)});
    });
  } catch (e) {
    console.warn(e.message);
    console.warn('Failed to create data channel. You need Chrome M25' +
                  'or later with --enable-data-channels flag');
    delete this.rtcConnections[sockId];
  }
};

Transport_unprivileged.prototype['accept'] = function (id, strdesc, continuation) {
  var desc = JSON.parse(strdesc);
  if (desc.type == 'icecandidate') {
    /**
    var candidate = new RTCIceCandidate({
      sdpMLineIndex: desc.candidate.sdpMLineIndex,
      sdpMid: desc.candidate.sdpMid,
      candidate: desc.candidate.candidate
    });
    **/
    var candidate = new RTCIceCandidate(desc.candidate);
    this.rtcConnections[id]['addIceCandidate'](candidate);
    console.log("Successfully accepted ICE candidate");
    continuation();
  //} else if (id == null || !(id in this.rtcConnections)) {
  } else if (desc.type == 'offer') {
    var desc = new RTCSessionDescription(desc);
    var sockId = this.nextId++;
    var servers = null;
    var pc = new webkitRTCPeerConnection(servers,
                  {'optional': [{'RtpDataChannels': true}]});
    this.rtcConnections[sockId] = pc;
    pc['onicecandidate'] = this.onIceCandidate.bind(this,sockId);
    pc['ondatachannel'] = function(evt) {
      var channel = evt['channel'];
      this.rtcChannels[sockId] = channel;
      channel.onopen = this.onStateChange.bind(this,sockId);
      channel.onclose = this.onStateChange.bind(this,sockId);
      channel.onmessage = this.onMessage.bind(this,sockId);
    }.bind(this);
    //desc, successCallback, failureCallback
    pc['setRemoteDescription'](desc,
      function(){console.log("Successfully set remote description");},
      function(){console.log("Failed set remote description");});
    pc['createAnswer'](function(answer) {
      pc['setLocalDescription'](answer);
      continuation({'id': sockId, 'offer': JSON.stringify(answer)});
    });
  } else if (desc.type == 'answer') {
    var desc = new RTCSessionDescription(desc);
    this.rtcConnections[id]['setRemoteDescription'](desc,
      function(){console.log("Successfully set remote description");},
      function(){console.log("Failed set remote description");});
    continuation();
  } else if (desc.type == 'bye') {
    this.close(id);
    continuation();
  }
};

Transport_unprivileged.prototype['send'] = function (msg, continuation) {
  var blob = msg.data;
  var reader = new FileReader();
  reader.onload = function(m, e) {
    this.rtcChannels[m.header].send(e.target.result);
    continuation();
  }.bind(this, msg);
  reader.onerror = function(e) {
    console.log("arraybuffer conversion on send error!");
    continuation();
  }
  reader.readAsText(blob);
};

Transport_unprivileged.prototype['close'] = function (id, continuation) {
  this.rtcChannels[id].close();
  this.rtcConnections[id].close();
  delete this.rtcChannels[id];
  delete this.rtcConnections[id];
  continuation();
};

Transport_unprivileged.prototype.get = function(key, continuation) {
  try {
    var val = localTransport[this.channel.app.id + key];
    continuation(val);
  } catch(e) {
    continuation(null);
  }
};

Transport_unprivileged.prototype.set = function(key, value, continuation) {
  localTransport[this.channel.app.id + key] = value;
  continuation();
};

fdom.apis.register("core.transport", Transport_unprivileged);
<commit_msg>Tranport cleanup<commit_after>/**
 * A FreeDOM transport provider using WebRTC
 */
var Transport_unprivileged = function(channel) {
  this.nextId = 10;
  this.channel = channel;
  this.rtcConnections = {};
  this.rtcChannels = {};
  this.rtcStates = {};
  handleEvents(this);
};

Transport_unprivileged.prototype.onStateChange = function(id) {
  var sendChannel = this.rtcChannels[id];
  //console.log(id + " send channel's state:"+sendChannel.readyState);
  this.channel.postMessage({
    'action':'event',
    'type': 'onStateChange',
    'value': {'id': id, 'state': sendChannel.readyState}
  });
};

Transport_unprivileged.prototype.onMessage = function(id, evt) {
  console.log(id + " message: "+evt.data.length);
  var blobber = new Blob([evt.data]);
  console.log("as blob it is length " + blobber.size);

  this.channel.postMessage({
    'action':'event',
    'type': 'onMessage',
    'value': {'header': id, 'data': new Blob([evt.data])}
  });
};

Transport_unprivileged.prototype.onIceCandidate = function(id, evt) {
  //console.log(id + " icecandidate: "+JSON.stringify(evt));
  if (evt && evt.candidate) {
    this.channel.postMessage({
      'action': 'event',
      'type': 'onSignal',
      'value': {'id': id, 'message': JSON.stringify(evt)}
    });
  }
}

Transport_unprivileged.prototype['create'] = function (continuation) {
  var sockId = this.nextId++;
  var servers = null;
  var sendChannel;
  var pc = new webkitRTCPeerConnection(servers,
                {'optional': [{'RtpDataChannels': true}]});
  this.rtcConnections[sockId] = pc;
  try {
    sendChannel = pc['createDataChannel']("sendDataChannel", {'reliable': false});
    this.rtcChannels[sockId] = sendChannel;
    sendChannel.onopen = this.onStateChange.bind(this,sockId);
    sendChannel.onclose = this.onStateChange.bind(this,sockId);
    sendChannel.onmessage = this.onMessage.bind(this,sockId);
    pc['onicecandidate'] = this.onIceCandidate.bind(this,sockId);

    pc['createOffer'](function(desc) {
      pc['setLocalDescription'](desc);
      continuation({'id': sockId, 'offer': JSON.stringify(desc)});
    });
  } catch (e) {
    console.warn(e.message);
    console.warn('Failed to create data channel. You need Chrome M25' +
                  'or later with --enable-data-channels flag');
    delete this.rtcConnections[sockId];
  }
};

Transport_unprivileged.prototype['accept'] = function (id, strdesc, continuation) {
  var desc = JSON.parse(strdesc);
  if (desc.type == 'icecandidate') {
    /**
    var candidate = new RTCIceCandidate({
      sdpMLineIndex: desc.candidate.sdpMLineIndex,
      sdpMid: desc.candidate.sdpMid,
      candidate: desc.candidate.candidate
    });
    **/
    var candidate = new RTCIceCandidate(desc.candidate);
    if (this.rtcConnections[id]) {
      this.rtcConnections[id]['addIceCandidate'](candidate);
      console.log("Successfully accepted ICE candidate");
    }
    continuation();
  //} else if (id == null || !(id in this.rtcConnections)) {
  } else if (desc.type == 'offer') {
    var desc = new RTCSessionDescription(desc);
    var sockId = this.nextId++;
    var servers = null;
    var pc = new webkitRTCPeerConnection(servers,
                  {'optional': [{'RtpDataChannels': true}]});
    this.rtcConnections[sockId] = pc;
    pc['onicecandidate'] = this.onIceCandidate.bind(this,sockId);
    pc['ondatachannel'] = function(evt) {
      var channel = evt['channel'];
      this.rtcChannels[sockId] = channel;
      channel.onopen = this.onStateChange.bind(this,sockId);
      channel.onclose = this.onStateChange.bind(this,sockId);
      channel.onmessage = this.onMessage.bind(this,sockId);
    }.bind(this);
    //desc, successCallback, failureCallback
    pc['setRemoteDescription'](desc,
      function(){console.log("Successfully set remote description");},
      function(){console.log("Failed set remote description");});
    pc['createAnswer'](function(answer) {
      pc['setLocalDescription'](answer);
      continuation({'id': sockId, 'offer': JSON.stringify(answer)});
    });
  } else if (desc.type == 'answer') {
    var desc = new RTCSessionDescription(desc);
    this.rtcConnections[id]['setRemoteDescription'](desc,
      function(){console.log("Successfully set remote description");},
      function(){console.log("Failed set remote description");});
    continuation();
  } else if (desc.type == 'bye') {
    this.close(id);
    continuation();
  }
};

Transport_unprivileged.prototype['send'] = function (msg, continuation) {
  var blob = msg.data;
  var reader = new FileReader();
  reader.onload = function(m, e) {
    this.rtcChannels[m.header].send(e.target.result);
    continuation();
  }.bind(this, msg);
  reader.onerror = function(e) {
    console.log("arraybuffer conversion on send error!");
    continuation();
  }
  reader.readAsText(blob);
};

Transport_unprivileged.prototype['close'] = function (id, continuation) {
  this.rtcChannels[id].close();
  this.rtcConnections[id].close();
  //delete this.rtcChannels[id];
  //delete this.rtcConnections[id];
  continuation();
};

/**
Transport_unprivileged.prototype.get = function(key, continuation) {
  try {
    var val = localTransport[this.channel.app.id + key];
    continuation(val);
  } catch(e) {
    continuation(null);
  }
};

Transport_unprivileged.prototype.set = function(key, value, continuation) {
  localTransport[this.channel.app.id + key] = value;
  continuation();
};
**/

fdom.apis.register("core.transport", Transport_unprivileged);
<|endoftext|>


<commit_before>var context    = this,
    extend     = jsface.extend,
    isMap      = jsface.isMap,
    isArray    = jsface.isArray,
    isFunction = jsface.isFunction,
    isString   = jsface.isString,
    isClass    = jsface.isClass;

test("$ready plugin: class notifies itself", function() {
  var notified = false;

  var Foo = Class({
    $ready: function(clazz, parent, api) {
      notified = true;
      equal(this, clazz, "clazz must be equal to this");
      ok(isFunction(api.$ready), "$ready works incorrectly");
      ok(isFunction(api.echo), "$ready works incorrectly");
      ok(isFunction(clazz.prototype.echo), "$ready works incorrectly");
      ok( !parent, "$ready works incorrectly");
    },
    echo: function(o) {
      return o;
    }
  });

  ok(notified, "$ready must be executed");
});

test("$ready plugin: class is notified when its subclasses are ready", function() {
  var notified = false;

  var Foo = Class({
    $ready: function(clazz, parent, api) {
      notified = true;

      if (this !== clazz) {
        ok(api.echo2, "$ready works incorrectly");
        ok( !api.$ready, "$ready works incorrectly");
        ok(isFunction(clazz.prototype.echo2), "$ready works incorrectly");
      }
    },
    echo: function(o) {
      return o;
    }
  });

  ok(notified, "$ready must be executed when class is created");

  var Bar = Class(Foo, {
    echo2: function(o) {
      return o;
    }
  });
});
<commit_msg>invoke $ready in multiple level inheritance<commit_after>var context    = this,
    extend     = jsface.extend,
    isMap      = jsface.isMap,
    isArray    = jsface.isArray,
    isFunction = jsface.isFunction,
    isString   = jsface.isString,
    isClass    = jsface.isClass;

test("$ready plugin: class notifies itself", function() {
  var notified = false;

  var Foo = Class({
    $ready: function(clazz, parent, api) {
      notified = true;
      equal(this, clazz, "clazz must be equal to this");
      ok(isFunction(api.$ready), "$ready works incorrectly");
      ok(isFunction(api.echo), "$ready works incorrectly");
      ok(isFunction(clazz.prototype.echo), "$ready works incorrectly");
      ok( !parent, "$ready works incorrectly");
    },
    echo: function(o) {
      return o;
    }
  });

  ok(notified, "$ready must be executed");
});

test("$ready plugin: class is notified when its subclasses are ready", function() {
  var notified = false;

  var Foo = Class({
    $ready: function(clazz, parent, api) {
      notified = true;

      if (this !== clazz) {
        ok(api.echo2, "$ready works incorrectly");
        ok( !api.$ready, "$ready works incorrectly");
        ok(isFunction(clazz.prototype.echo2), "$ready works incorrectly");
      }
    },
    echo: function(o) {
      return o;
    }
  });

  ok(notified, "$ready must be executed when class is created");

  var Bar = Class(Foo, {
    echo2: function(o) {
      return o;
    }
  });
});

test("$ready plugin: class is notified when its subclasses are ready (multiple levels)", function() {
  var count = 0;

  var Foo = Class({
    $ready: function(clazz, parent, api) {
      if (this !== clazz) {
        count++;
      }
    }
  });

  var Bar1 = Class(Foo, {});
  var Bar2 = Class(Bar1, {});
  var Bar3 = Class(Bar2, {});

  ok(count === 3, "$ready must be executed in multiple level inheritance");
});
<|endoftext|>


<commit_before>var pageURL = "http://tlks.io/terms";

exports.testTerms = function(test) {
    casper
        .start(pageURL)
        .then(function() {
            test.assert(this.getCurrentUrl() === pageURL, 'url is the one expected');
        })
        .then(function() {
            test.assertHttpStatus(200, pageURL + ' is up');
        })
        .then(function() {
            var title = "tlks.io : Terms of service";
            test.assertTitle(title, "title is the one expected");
        })
        .run(function() {
            test.done();
        });
};
<commit_msg>Lint source code<commit_after>var pageURL = 'http://tlks.io/terms';

exports.testTerms = function(test) {
    casper
        .start(pageURL)
        .then(function() {
            test.assert(this.getCurrentUrl() === pageURL, 'url is the one expected');
        })
        .then(function() {
            test.assertHttpStatus(200, pageURL + ' is up');
        })
        .then(function() {
            var title = 'tlks.io : Terms of service';
            test.assertTitle(title, 'title is the one expected');
        })
        .run(function() {
            test.done();
        });
};
<|endoftext|>


<commit_before>/* global describe, it */
const assert = require('assert');
const $ = require('jquery');

const {run, Rx} = require('@cycle/core');
const {makeDOMDriver} = require('@cycle/dom');

const TimeTravel = require('../src/time-travel');

function createRenderTarget () {
  const element = document.createElement('div');
  element.className = 'cycletest';
  document.body.appendChild(element);
  return element;
}

describe('TimeTravel', () => {
  it('can be created', () => {
    function main ({DOM}) {
      const timeTravel = TimeTravel(DOM, []);

      return {DOM: timeTravel.DOM};
    }

    assert.doesNotThrow(() => {
      run(main, {DOM: makeDOMDriver(createRenderTarget())});
    });
  });

  it('takes streams to control', () => {
    function main ({DOM}) {
      const count$ = DOM.select('.count').events('click');
      const timeTravel = TimeTravel(DOM, [
        {stream: count$, label: 'count$'}
      ]);

      return {DOM: timeTravel.DOM};
    }

    assert.doesNotThrow(() => {
      run(main, {DOM: makeDOMDriver(createRenderTarget())});
    });
  });

  it('can be paused', (done) => {
    function main ({DOM}) {
      const count$ = DOM.select('.count').events('click');
      const timeTravel = TimeTravel(DOM, [
        {stream: count$, label: 'count$'}
      ]);

      return {DOM: timeTravel.DOM};
    }

    assert.doesNotThrow(() => {
      const renderTarget = createRenderTarget();
      run(main, {DOM: makeDOMDriver(renderTarget)});

      // this is probably not a good way to do this
      setTimeout(() => {
        $(renderTarget).find('.pause').trigger('click');

        assert.equal($(renderTarget).find('.pause').text(), 'Play');

        $(renderTarget).find('.pause').trigger('click');

        assert.equal($(renderTarget).find('.pause').text(), 'Pause');

        done();
      }, 1);
    });
  });
});
<commit_msg>Add a test for wrapping apps without impacting them<commit_after>/* global describe, it */
const assert = require('assert');
const $ = require('jquery');

const {run, Rx} = require('@cycle/core');
const {h, makeDOMDriver} = require('@cycle/dom');

const TimeTravel = require('../src/time-travel');

function createRenderTarget () {
  const element = document.createElement('div');
  element.className = 'cycletest';
  document.body.appendChild(element);
  return element;
}

describe('TimeTravel', () => {
  it('can be created', () => {
    function main ({DOM}) {
      const timeTravel = TimeTravel(DOM, []);

      return {DOM: timeTravel.DOM};
    }

    assert.doesNotThrow(() => {
      run(main, {DOM: makeDOMDriver(createRenderTarget())});
    });
  });

  it('takes streams to control', () => {
    function main ({DOM}) {
      const count$ = DOM.select('.count').events('click');
      const timeTravel = TimeTravel(DOM, [
        {stream: count$, label: 'count$'}
      ]);

      return {DOM: timeTravel.DOM};
    }

    assert.doesNotThrow(() => {
      run(main, {DOM: makeDOMDriver(createRenderTarget())});
    });
  });

  it('can be paused', (done) => {
    function main ({DOM}) {
      const count$ = DOM.select('.count').events('click');
      const timeTravel = TimeTravel(DOM, [
        {stream: count$, label: 'count$'}
      ]);

      return {DOM: timeTravel.DOM};
    }

    const renderTarget = createRenderTarget();
    run(main, {DOM: makeDOMDriver(renderTarget)});

    setTimeout(() => {
      $(renderTarget).find('.pause').trigger('click');

      assert.equal($(renderTarget).find('.pause').text(), 'Play');

      $(renderTarget).find('.pause').trigger('click');

      assert.equal($(renderTarget).find('.pause').text(), 'Pause');

      done();
    }, 1);
  });

  it('plays the app as normal', (done) => {
    function main ({DOM}) {
      const count$ = DOM.select('.increment').events('click')
        .scan((count, _) => count + 1, 0)
        .startWith(0);

      const timeTravel = TimeTravel(DOM, [
        {stream: count$, label: 'count$'}
      ]);

      const vtree$ = count$.map(count => (
        h('.app', [
          h('button.increment', 'Increment'),
          h('.count', `Count: ${count}`)
        ])
      ));

      return {
        DOM: Rx.Observable.combineLatest(vtree$, timeTravel.DOM)
          .map(vtrees => h('.test', vtrees))
      };
    }

    const renderTarget = createRenderTarget();
    run(main, {DOM: makeDOMDriver(renderTarget)});

    setTimeout(() => {
      assert.equal($(renderTarget).find('.count').text(), 'Count: 0');

      $(renderTarget).find('.increment').trigger('click');

      assert.equal($(renderTarget).find('.count').text(), 'Count: 1');

      done();
    }, 1);
  });
});
<|endoftext|>


<commit_before>const test = require('ava')
const helpers = require('./_helpers')
const laabr = require('../src')
const tokens = require('../src/tokens')

const mockColors = {
  level: helpers.noop,
  status: helpers.noop,
  dim: helpers.noop
}

test('throw error if token arguments are invalid – key', (t) => {
  t.throws(() => laabr.token(null, () => 42))
  t.throws(() => laabr.token(false, () => 42))
  t.throws(() => laabr.token(NaN, () => 42))
  t.throws(() => laabr.token(42, () => 42))
  t.throws(() => laabr.token('', () => 42))
  t.throws(() => laabr.token({}, () => 42))
  t.throws(() => laabr.token([], () => 42))
  t.throws(() => laabr.token(new RegExp(), () => 42))
})

test('throw error if token arguments are invalid – token', (t) => {
  t.throws(() => laabr.token('foobar', null))
  t.throws(() => laabr.token('foobar', false))
  t.throws(() => laabr.token('foobar', NaN))
  t.throws(() => laabr.token('foobar', 42))
  t.throws(() => laabr.token('foobar', ''))
  t.throws(() => laabr.token('foobar', 'foobar'))
  t.throws(() => laabr.token('foobar', {}))
  t.throws(() => laabr.token('foobar', []))
  t.throws(() => laabr.token('foobar', new RegExp()))
  t.throws(() => laabr.token('foobar', (a, b, c, d) => 42))
})

test('throw no error if token arguments are valid', (t) => {
  t.notThrows(() => laabr.token('foobar', () => 42))
  t.is(tokens.foobar(), 42)
})

test('return requested general attributes', (t) => {
  const mockData = {
    pid: 52818,
    tags: ['foobar'],
    host: 'localhost',
    port: 3000,
    uri: 'http://localhost:3000',
    address: '127.0.0.1',
    protocol: 'http',
    level: 60,
    time: 971186136,
    responseTime: 42,
    foo: 42,
    bar: {
      foo: 42
    },
    msg: 'foobar',
    err: new Error('foobar')
  }

  process.env.FOO = 'bar'

  t.is(tokens.pid(mockData), mockData.pid)
  t.is(tokens.tags(mockData), mockData.tags)
  t.is(tokens.level(mockData, mockColors), 'fatal')
  t.is(tokens.level(mockData, mockColors, 'code'), 60)
  t.is(tokens.time(mockData, mockColors), mockData.time)
  t.is(tokens.time(mockData, mockColors, 'iso'), '1970-01-12T05:46:26.136Z')
  t.is(tokens.time(mockData, mockColors, 'utc'), 'Mon, 12 Jan 1970 05:46:26 GMT')
  t.is(tokens.responseTime(mockData), mockData.responseTime)
  t.is(tokens.get(mockData, mockColors, 'foo'), mockData.foo)
  t.is(tokens.get(mockData, mockColors, 'bar'), mockData.bar)
  t.is(tokens.message(mockData), mockData.msg)
  t.is(tokens.error(mockData), mockData.err.message)
  t.is(tokens.error({ data: mockData }), mockData.err.message)
  t.is(tokens.host(mockData), mockData.host)
  t.is(tokens.host(mockData, mockColors, 'uri'), mockData.uri)
  t.is(tokens.host(mockData, mockColors, 'address'), mockData.address)
  t.is(tokens.host(mockData, mockColors, 'port'), mockData.port)
  t.is(tokens.host(mockData, mockColors, 'protocol'), mockData.protocol)
  t.is(tokens.host(mockData, mockColors, 'foo'), undefined)
  t.is(tokens.environment(), 'test')
  t.is(tokens.environment(mockData, mockColors, 'FOO'), 'bar')
  t.is(tokens.res(mockData, mockColors, 'foo'), undefined)
})

test('return empty array in case of no tags', (t) => {
  const mockData = {}

  t.deepEqual(tokens.tags(mockData), [])
})

test('return requested alternative attributes', (t) => {
  const mockData = {
    data: 'foobar'
  }

  t.is(tokens.message(mockData), mockData.data)
  t.is(tokens.error(mockData), undefined)
})

test('return requested req/res attributes', (t) => {
  const mockData = {
    res: {
      statusCode: 200,
      headers: {
        'foobar': 'no-cache',
        'barfoo': '42'
      }
    },
    req: {
      id: '1499781055994:f3lix:67067:j4zmw7av:10000',
      method: 'get',
      url: '/',
      remoteAddress: '127.0.0.1',
      remotePort: 52086,
      headers: {
        'x-header': 'foobar'
      }
    }
  }

  t.is(tokens.requestId(mockData), mockData.req.id)
  t.is(tokens.method(mockData, mockColors), mockData.req.method.toUpperCase())
  t.is(tokens.status(mockData, mockColors), mockData.res.statusCode)
  t.is(tokens.payload(mockData), JSON.stringify({}))
  t.is(tokens.remoteAddress(mockData), mockData.req.remoteAddress)
  t.is(tokens.remotePort(mockData), mockData.req.remotePort)
  t.is(tokens.url(mockData), mockData.req.url)
  t.is(tokens.req(mockData, mockColors, 'x-header'), mockData.req.headers['x-header'])
  t.is(tokens.res(mockData, mockColors, 'foobar'), 'no-cache')
  t.is(tokens.res(mockData, mockColors, 'barfoo'), '42')
  t.is(tokens.res(mockData, mockColors, 'foo'), undefined)
})
<commit_msg>fix(style) Unnecessarily quoted property<commit_after>const test = require('ava')
const helpers = require('./_helpers')
const laabr = require('../src')
const tokens = require('../src/tokens')

const mockColors = {
  level: helpers.noop,
  status: helpers.noop,
  dim: helpers.noop
}

test('throw error if token arguments are invalid – key', (t) => {
  t.throws(() => laabr.token(null, () => 42))
  t.throws(() => laabr.token(false, () => 42))
  t.throws(() => laabr.token(NaN, () => 42))
  t.throws(() => laabr.token(42, () => 42))
  t.throws(() => laabr.token('', () => 42))
  t.throws(() => laabr.token({}, () => 42))
  t.throws(() => laabr.token([], () => 42))
  t.throws(() => laabr.token(new RegExp(), () => 42))
})

test('throw error if token arguments are invalid – token', (t) => {
  t.throws(() => laabr.token('foobar', null))
  t.throws(() => laabr.token('foobar', false))
  t.throws(() => laabr.token('foobar', NaN))
  t.throws(() => laabr.token('foobar', 42))
  t.throws(() => laabr.token('foobar', ''))
  t.throws(() => laabr.token('foobar', 'foobar'))
  t.throws(() => laabr.token('foobar', {}))
  t.throws(() => laabr.token('foobar', []))
  t.throws(() => laabr.token('foobar', new RegExp()))
  t.throws(() => laabr.token('foobar', (a, b, c, d) => 42))
})

test('throw no error if token arguments are valid', (t) => {
  t.notThrows(() => laabr.token('foobar', () => 42))
  t.is(tokens.foobar(), 42)
})

test('return requested general attributes', (t) => {
  const mockData = {
    pid: 52818,
    tags: ['foobar'],
    host: 'localhost',
    port: 3000,
    uri: 'http://localhost:3000',
    address: '127.0.0.1',
    protocol: 'http',
    level: 60,
    time: 971186136,
    responseTime: 42,
    foo: 42,
    bar: {
      foo: 42
    },
    msg: 'foobar',
    err: new Error('foobar')
  }

  process.env.FOO = 'bar'

  t.is(tokens.pid(mockData), mockData.pid)
  t.is(tokens.tags(mockData), mockData.tags)
  t.is(tokens.level(mockData, mockColors), 'fatal')
  t.is(tokens.level(mockData, mockColors, 'code'), 60)
  t.is(tokens.time(mockData, mockColors), mockData.time)
  t.is(tokens.time(mockData, mockColors, 'iso'), '1970-01-12T05:46:26.136Z')
  t.is(tokens.time(mockData, mockColors, 'utc'), 'Mon, 12 Jan 1970 05:46:26 GMT')
  t.is(tokens.responseTime(mockData), mockData.responseTime)
  t.is(tokens.get(mockData, mockColors, 'foo'), mockData.foo)
  t.is(tokens.get(mockData, mockColors, 'bar'), mockData.bar)
  t.is(tokens.message(mockData), mockData.msg)
  t.is(tokens.error(mockData), mockData.err.message)
  t.is(tokens.error({ data: mockData }), mockData.err.message)
  t.is(tokens.host(mockData), mockData.host)
  t.is(tokens.host(mockData, mockColors, 'uri'), mockData.uri)
  t.is(tokens.host(mockData, mockColors, 'address'), mockData.address)
  t.is(tokens.host(mockData, mockColors, 'port'), mockData.port)
  t.is(tokens.host(mockData, mockColors, 'protocol'), mockData.protocol)
  t.is(tokens.host(mockData, mockColors, 'foo'), undefined)
  t.is(tokens.environment(), 'test')
  t.is(tokens.environment(mockData, mockColors, 'FOO'), 'bar')
  t.is(tokens.res(mockData, mockColors, 'foo'), undefined)
})

test('return empty array in case of no tags', (t) => {
  const mockData = {}

  t.deepEqual(tokens.tags(mockData), [])
})

test('return requested alternative attributes', (t) => {
  const mockData = {
    data: 'foobar'
  }

  t.is(tokens.message(mockData), mockData.data)
  t.is(tokens.error(mockData), undefined)
})

test('return requested req/res attributes', (t) => {
  const mockData = {
    res: {
      statusCode: 200,
      headers: {
        foobar: 'no-cache',
        barfoo: '42'
      }
    },
    req: {
      id: '1499781055994:f3lix:67067:j4zmw7av:10000',
      method: 'get',
      url: '/',
      remoteAddress: '127.0.0.1',
      remotePort: 52086,
      headers: {
        'x-header': 'foobar'
      }
    }
  }

  t.is(tokens.requestId(mockData), mockData.req.id)
  t.is(tokens.method(mockData, mockColors), mockData.req.method.toUpperCase())
  t.is(tokens.status(mockData, mockColors), mockData.res.statusCode)
  t.is(tokens.payload(mockData), JSON.stringify({}))
  t.is(tokens.remoteAddress(mockData), mockData.req.remoteAddress)
  t.is(tokens.remotePort(mockData), mockData.req.remotePort)
  t.is(tokens.url(mockData), mockData.req.url)
  t.is(tokens.req(mockData, mockColors, 'x-header'), mockData.req.headers['x-header'])
  t.is(tokens.res(mockData, mockColors, 'foobar'), 'no-cache')
  t.is(tokens.res(mockData, mockColors, 'barfoo'), '42')
  t.is(tokens.res(mockData, mockColors, 'foo'), undefined)
})
<|endoftext|>


<commit_before>export default function({ $axios, redirect, store }) {
  $axios.onResponseError((error) => {
    if (error.status === 403) {
      if (process.server) {
        redirect('/sign-in')
      } else {
        store.commit('setToken', null)
      }
    }
  })
}
<commit_msg>Add incremental updates<commit_after>export default function({ $axios, redirect, store }) {
  $axios.onResponseError((error) => {
    if (error?.response?.status === 403) {
      if (process.server) {
        redirect('/sign-in')
      } else {
        store.commit('setToken', null)
      }
    }
  })
}
<|endoftext|>


<commit_before>import "should"
import {Observable as O} from "rx"
import TSERS from "../src/index"

const noop = () => null

const tsersDriver = () => ({
  signals: O.just("tsers").delay(0),
  executor: noop
})

describe("common transducers", () => {

  describe("decompose(in$, ...keys)", () => {
    it("decomposes streams to object of streams of given keys and rest output", done => {
      const input$ = O.of({key: "A", val: "a"}, {key: "B", val: "b"}, {key: "C", val: "c"})
      const [{decompose}] = TSERS({A: tsersDriver, B: tsersDriver})
      const [out, rest$] = decompose(input$, "A", "B")
      out.A.should.be.instanceof(O)
      out.B.should.be.instanceof(O)
      rest$.should.be.instanceof(O)
      Object.keys(out).should.deepEqual(["A", "B"])
      out.A
        .map(a => ({a}))
        .merge(out.B.delay(1).map(b => ({b})))
        .bufferWithCount(2)
        .subscribe(x => {
          x.should.deepEqual([{a: "a"}, {b: "b"}])
          done()
        })
    })
    it("re-directs rest output to the second argument stream", done => {
      const [{decompose}] = TSERS({A: tsersDriver, B: tsersDriver})
      const in$ = O.of({key: "A", val: "a"}, {key: "state$", val: ".."})
      const [_, rest$] = decompose(in$, "state$", "val$")
      rest$.bufferWithTime(10).subscribe(x => {
        x.should.deepEqual([{key: "A", val: "a"}])
        done()
      })
    })
  })

  describe("run(in$, main)", () => {
    it("is lazy", done => {
      const [{run}, S, _] = TSERS({A: tsersDriver})
      const out$ = run(S, in$ => [in$.do(done.fail), in$.do(done.fail)])
      out$.should.be.instanceof(O)
      setTimeout(done, 10)
    })
    it("allows returning only output stream", done => {
      const [{run}, S, _] = TSERS({A: tsersDriver})
      const out$ = run(S, in$ => in$)
      out$.should.be.instanceof(O)
      out$.subscribe(() => done())
    })
    it("allows looping signals back to input", done => {
      const [{run, compose, extract}, S, _] = TSERS({A: tsersDriver})

      function main(in$) {
        const withBang = s$ => s$.map(x => x + "!")
        return [
          compose({A3: withBang(extract(in$, "A2").A2)}),    // out
          compose({A2: withBang(extract(in$, "A").A)})       // loop
        ]
      }

      extract(run(S, main), "A3").A3.subscribe(x => {
        x.should.equal("tsers!!")
        done()
      })
    })
    it("disposes the signal loop the output stream is disposed", done => {
      const [{run, compose, extract}, S, _] = TSERS({A: tsersDriver})

      function main(in$) {
        return [in$.merge(compose({A: O.just("lolbal").delay(10)}))]
      }

      const d = extract(run(S, main), "A").A.subscribe(x => {
        x.should.equal("tsers")
        d.dispose()
        setTimeout(done, 100)
      })
    })
    it("passes 'complete' messages", done => {
      const [{run}, S, _] = TSERS({A: tsersDriver})
      const main = in$ => [in$, in$.filter(() => false)]
      run(S, main).subscribe(noop, done.fail, done)
    })
    it("passes 'error' messages", done => {
      const [{run}, _, __] = TSERS({A: tsersDriver})
      const main = in$ => [in$, in$.filter(() => false)]
      run(O.throw(new Error("tsers")), main).subscribe(noop, () => done())
    })
  })

})
<commit_msg>Add tests for lift* transducers <commit_after>import "should"
import {Observable as O} from "rx"
import TSERS from "../src/index"

const noop = () => null

const tsersDriver = () => ({
  signals: O.just("tsers").delay(0),
  executor: noop
})

describe("common transducers", () => {

  describe("decompose(in$, ...keys)", () => {
    it("decomposes streams to object of streams of given keys and rest output", done => {
      const input$ = O.of({key: "A", val: "a"}, {key: "B", val: "b"}, {key: "C", val: "c"})
      const [{decompose}] = TSERS({A: tsersDriver, B: tsersDriver})
      const [out, rest$] = decompose(input$, "A", "B")
      out.A.should.be.instanceof(O)
      out.B.should.be.instanceof(O)
      rest$.should.be.instanceof(O)
      Object.keys(out).should.deepEqual(["A", "B"])
      out.A
        .map(a => ({a}))
        .merge(out.B.delay(1).map(b => ({b})))
        .bufferWithCount(2)
        .subscribe(x => {
          x.should.deepEqual([{a: "a"}, {b: "b"}])
          done()
        })
    })
    it("re-directs rest output to the second argument stream", done => {
      const [{decompose}] = TSERS({A: tsersDriver, B: tsersDriver})
      const in$ = O.of({key: "A", val: "a"}, {key: "state$", val: ".."})
      const [_, rest$] = decompose(in$, "state$", "val$")
      rest$.bufferWithTime(10).subscribe(x => {
        x.should.deepEqual([{key: "A", val: "a"}])
        done()
      })
    })
  })

  describe("run(in$, main)", () => {
    it("is lazy", done => {
      const [{run}, S, _] = TSERS({A: tsersDriver})
      const out$ = run(S, in$ => [in$.do(done.fail), in$.do(done.fail)])
      out$.should.be.instanceof(O)
      setTimeout(done, 10)
    })
    it("allows returning only output stream", done => {
      const [{run}, S, _] = TSERS({A: tsersDriver})
      const out$ = run(S, in$ => in$)
      out$.should.be.instanceof(O)
      out$.subscribe(() => done())
    })
    it("allows looping signals back to input", done => {
      const [{run, compose, extract}, S, _] = TSERS({A: tsersDriver})

      function main(in$) {
        const withBang = s$ => s$.map(x => x + "!")
        return [
          compose({A3: withBang(extract(in$, "A2").A2)}),    // out
          compose({A2: withBang(extract(in$, "A").A)})       // loop
        ]
      }

      extract(run(S, main), "A3").A3.subscribe(x => {
        x.should.equal("tsers!!")
        done()
      })
    })
    it("disposes the signal loop the output stream is disposed", done => {
      const [{run, compose, extract}, S, _] = TSERS({A: tsersDriver})

      function main(in$) {
        return [in$.merge(compose({A: O.just("lolbal").delay(10)}))]
      }

      const d = extract(run(S, main), "A").A.subscribe(x => {
        x.should.equal("tsers")
        d.dispose()
        setTimeout(done, 100)
      })
    })
    it("passes 'complete' messages", done => {
      const [{run}, S, _] = TSERS({A: tsersDriver})
      const main = in$ => [in$, in$.filter(() => false)]
      run(S, main).subscribe(noop, done.fail, done)
    })
    it("passes 'error' messages", done => {
      const [{run}, _, __] = TSERS({A: tsersDriver})
      const main = in$ => [in$, in$.filter(() => false)]
      run(O.throw(new Error("tsers")), main).subscribe(noop, () => done())
    })
  })

  describe("lift(val$$, ...keys)", () => {
    it("switches to the latest input and decomposes it by using the given keys", done => {
      const [{lift}, _, __] = TSERS({A: tsersDriver})
      const streams = {
        foo: O.of({key: "A", val: "a1"}, {key: "B", val: "b1"}).merge(O.of({key: "A", val: "a11"}).delay(10)),
        bar: O.of({key: "A", val: "a2"})
      }
      const in$ = O.of("foo").merge(O.of("bar").delay(1))
      const [res] = lift(in$.map(s => streams[s]), "A", "B")
      res.A.should.be.instanceof(O)
      res.B.should.be.instanceof(O)
      res.A.bufferWithTime(100).first().subscribe(x => {
        x.should.deepEqual(["a1", "a2"])
        done()
      })
    })
    it("returns the rest of output as a second parameter stream", done => {
      const [{lift}] = TSERS({A: tsersDriver})
      const streams = {
        foo: O.of({key: "A", val: "a1"}, {key: "B", val: "b1"}).merge(O.of({key: "A", val: "a11"}).delay(1)),
        bar: O.of({key: "A", val: "a2"})
      }
      const in$ = O.of("foo").merge(O.of("bar").delay(1))
      const [_, rest$] = lift(in$.map(s => streams[s]), "A")
      rest$.should.be.instanceof(O)
      rest$.bufferWithTime(100).first().subscribe(x => {
        x.should.deepEqual([{key: "B", val: "b1"}])
        done()
      })
    })
  })

  describe("liftArray(arr$, val => res$, ...keys)", () => {
    it("switches decomposes and combines array values by using given keys", done => {
      const [{liftArray}] = TSERS({A: tsersDriver})
      let n = 0
      const sBy = val =>
        O.of({key: "A", val: "a" + n + val}, {key: "B", val: "b" + n + val})

      const in$ = O.create(o => setTimeout(() => {
        n++
        o.onNext([4, 1, 5])
        setTimeout(() => {
          n++
          o.onNext([2, 5, 3])
        }, 0)
      }, 0))

      const [res, _] = liftArray(in$, sBy, "A")
      res.A.should.be.instanceof(O)
      res.A.bufferWithTime(100).first().subscribe(x => {
        x.should.deepEqual([["a14", "a11", "a15"], ["a22", "a25", "a23"]])
        done()
      })
    })
    it("returns the rest of output as a second parameter stream", done => {
      const [{liftArray}] = TSERS({A: tsersDriver})
      let n = 0
      const sBy = val =>
        O.of({key: "A", val: "a" + n + val}, {key: "B", val: "b" + n + val})

      const in$ = O.create(o => {
        n++
        o.onNext([4, 1, 5])
        setTimeout(() => {
          n++
          o.onNext([2, 5, 3])
        }, 0)
      })

      const [_, rest$] = liftArray(in$, sBy, "A")
      rest$.should.be.instanceof(O)
      rest$.bufferWithTime(100).first().subscribe(x => {
        x.should.deepEqual([
          {key: "B", val: "b14"},
          {key: "B", val: "b11"},
          {key: "B", val: "b15"},
          {key: "B", val: "b22"},
          {key: "B", val: "b25"},
          {key: "B", val: "b23"}
        ])
        done()
      })
    })
  })
})
<|endoftext|>


<commit_before>'use strict';

var async = require('asyncawait/async');
var await = require('asyncawait/await');
var VAController = require('../modules/annotations/video-annotations-controller');
var Plugin = require('../modules/apps-gallery/backgroundPlugins.js');

module.exports = function (io) {

  // notifies all users about changes
  var emitAnnotationUpdatedAsync = async(function (videoId) {
    if (!videoId) {
      throw 'Invalid video ID: ' + videoId;
    }
    var annotations = await(VAController.findByVideoIdAsync(videoId));
    io.sockets.emit('annotations:updated', annotations);
  });

  // Notify users that the annotation
  // comments have been updated
  var emitCommentsUpdatedAsync = async(function (annotation) {
    if (!annotation) {
      return;
    }
    var eventName = annotation._id + ':comments:updated';
    io.sockets.emit(eventName, {comments: annotation.comments});
  });

  io.sockets.on('connection', function (socket) {

    var getUser = function () {
      var hasSession = socket && socket.request && socket.request.session && socket.request.session.passport;
      if (!hasSession) {
        throw 'No user session found.'
      }
      return socket.request.session.passport.user;
    };

    socket.on('annotations:get', async(function (params) {
      var annotations = await(VAController.findByVideoIdAsync(params.video_id));
      // return annotations only to requester
      socket.emit('annotations:updated', annotations);
    }));

    socket.on('annotations:save', async(function (params) {
      try {
        var user = getUser();
        var model = params.annotation;
        var annotation = await(VAController.updateAsync(model, user));
        if (annotation) {
          Plugin.doAction('onAfterVideoAnnotationEdited', annotation, user);
        } else {
          annotation = await(VAController.addAsync(model, user));
          Plugin.doAction('onAfterVideoAnnotationCreated', annotation);
        }
        var videoId = annotation.video_id;
        await(emitAnnotationUpdatedAsync(videoId));
      }
      catch (e) {
        console.log('Error saving video annotation: ' + e);
      }
    }));

    socket.on('annotations:delete', async(function (params) {
      try {
        var annotation = await(VAController.removeAsync(params.id, getUser()));
        if (!annotation) {
          return;
        }
        var videoId = annotation.video_id;
        Plugin.doAction('onAfterVideoAnnotationDeleted', annotation, getUser());
        await(emitAnnotationUpdatedAsync(videoId));
      } catch (e) {
        console.log('Error removing video annotation: ' + e);
      }
    }));

    socket.on('comments:post', async(function (params) {
      try {
        var annotation = await(VAController.addCommentAsync(params, getUser()));
        await(emitCommentsUpdatedAsync(annotation));
      } catch (e) {
        console.log('Error posting comment: ' + e);
      }
    }));

    socket.on('comments:remove', async(function (params) {
      try {
        var annotation = await(VAController.removeCommentAsync(params, getUser()));
        await(emitCommentsUpdatedAsync(annotation));
      } catch (e) {
        console.log('Error removing comment: ' + e);
      }
    }));
  });
};<commit_msg>Hook - Add hook for video annotation comment (reply) added and delete<commit_after>'use strict';

var async = require('asyncawait/async');
var await = require('asyncawait/await');
var VAController = require('../modules/annotations/video-annotations-controller');
var Plugin = require('../modules/apps-gallery/backgroundPlugins.js');

module.exports = function (io) {

  // notifies all users about changes
  var emitAnnotationUpdatedAsync = async(function (videoId) {
    if (!videoId) {
      throw 'Invalid video ID: ' + videoId;
    }
    var annotations = await(VAController.findByVideoIdAsync(videoId));
    io.sockets.emit('annotations:updated', annotations);
  });

  // Notify users that the annotation
  // comments have been updated
  var emitCommentsUpdatedAsync = async(function (annotation) {
    if (!annotation) {
      return;
    }
    var eventName = annotation._id + ':comments:updated';
    io.sockets.emit(eventName, {comments: annotation.comments});
  });

  io.sockets.on('connection', function (socket) {

    var getUser = function () {
      var hasSession = socket && socket.request && socket.request.session && socket.request.session.passport;
      if (!hasSession) {
        throw 'No user session found.'
      }
      return socket.request.session.passport.user;
    };

    socket.on('annotations:get', async(function (params) {
      var annotations = await(VAController.findByVideoIdAsync(params.video_id));
      // return annotations only to requester
      socket.emit('annotations:updated', annotations);
    }));

    socket.on('annotations:save', async(function (params) {
      try {
        var user = getUser();
        var model = params.annotation;
        var annotation = await(VAController.updateAsync(model, user));
        if (annotation) {
          Plugin.doAction('onAfterVideoAnnotationEdited', annotation, user);
        } else {
          annotation = await(VAController.addAsync(model, user));
          Plugin.doAction('onAfterVideoAnnotationCreated', annotation);
        }
        var videoId = annotation.video_id;
        await(emitAnnotationUpdatedAsync(videoId));
      }
      catch (e) {
        console.log('Error saving video annotation: ' + e);
      }
    }));

    socket.on('annotations:delete', async(function (params) {
      try {
        var annotation = await(VAController.removeAsync(params.id, getUser()));
        if (!annotation) {
          return;
        }
        var videoId = annotation.video_id;
        Plugin.doAction('onAfterVideoAnnotationDeleted', annotation, getUser());
        await(emitAnnotationUpdatedAsync(videoId));
      } catch (e) {
        console.log('Error removing video annotation: ' + e);
      }
    }));

    socket.on('comments:post', async(function (params) {
      try {
        var annotation = await(VAController.addCommentAsync(params, getUser()));
        Plugin.doAction('onAfterVideoCommentCreated', annotation, getUser());
        await(emitCommentsUpdatedAsync(annotation));
      } catch (e) {
        console.log('Error posting comment: ' + e);
      }
    }));

    socket.on('comments:remove', async(function (params) {
      try {
        var annotation = await(VAController.removeCommentAsync(params, getUser()));
        Plugin.doAction('onAfterVideoCommentDeleted', annotation, getUser());
        await(emitCommentsUpdatedAsync(annotation));
      } catch (e) {
        console.log('Error removing comment: ' + e);
      }
    }));
  });
};<|endoftext|>