import * as watchLibrary from "pathwatcher";
import { Profile } from "../tool-env/profile.js";
import {
  statOrNull,
  pathDirname,
  pathResolve,
  convertToOSPath,
  convertToStandardPath,
  watchFile,
  unwatchFile,
} from "./files.js";

// Set METEOR_WATCH_FORCE_POLLING environment variable to a truthy value to
// force the use of files.watchFile instead of watchLibrary.watch.
var WATCHER_ENABLED = ! JSON.parse(
  process.env.METEOR_WATCH_FORCE_POLLING || "false"
);

// Default to prioritizing changed files, but disable that behavior (and
// thus prioritize all files equally) if METEOR_WATCH_PRIORITIZE_CHANGED
// is explicitly set to a string that parses to a falsy value.
var PRIORITIZE_CHANGED = true;
if (process.env.METEOR_WATCH_PRIORITIZE_CHANGED &&
    ! JSON.parse(process.env.METEOR_WATCH_PRIORITIZE_CHANGED)) {
  PRIORITIZE_CHANGED = false;
}

var DEFAULT_POLLING_INTERVAL =
  ~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 5000;

var NO_WATCHER_POLLING_INTERVAL =
  ~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 500;

// This may seems like a long time to wait before actually closing the
// file watchers, but it's to our advantage if they survive restarts.
const WATCHER_CLEANUP_DELAY_MS = 30000;

const entries = Object.create(null);

// Pathwatcher complains (using console.error, ugh) if you try to watch
// two files with the same stat.ino number but different paths, so we have
// to deduplicate files by ino.
const entriesByIno = new Map;

// Set of paths for which a change event has been fired, watched with
// watchLibrary.watch if available. This could be an LRU cache, but in
// practice it should never grow large enough for that to matter.
const changedPaths = new Set;

function hasPriority(absPath) {
  // If we're not prioritizing changed files, then all files have
  // priority, which means they should be watched with native file
  // watchers if the platform supports them. If we are prioritizing
  // changed files, then only changed files get priority.
  return PRIORITIZE_CHANGED
    ? changedPaths.has(absPath)
    : true;
}

function acquireWatcher(absPath, callback) {
  const entry = entries[absPath] || (
    entries[absPath] = startNewWatcher(absPath));

  // Watches successfully established in the past may have become invalid
  // because the watched file was deleted or renamed, so we need to make
  // sure we're still watching every time we call safeWatcher.watch.
  entry.rewatch();

  // The size of the entry.callbacks Set also serves as a reference count
  // for this watcher.
  entry.callbacks.add(callback);

  return entry;
}

function startNewWatcher(absPath) {
  const stat = statOrNull(absPath);
  const ino = stat && stat.ino;
  if (ino > 0 && entriesByIno.has(ino)) {
    const entry = entriesByIno.get(ino);
    if (entries[absPath] === entry) {
      return entry;
    }
  }

  function safeUnwatch() {
    if (watcher) {
      watcher.close();
      watcher = null;
      if (ino > 0) {
        entriesByIno.delete(ino);
      }
    }
  }

  let lastWatcherEventTime = +new Date;
  const callbacks = new Set;
  let watcherCleanupTimer = null;
  let watcher;

  // Determines the polling interval to be used for the fs.watchFile-based
  // safety net that works on all platforms and file systems.
  function getPollingInterval() {
    if (hasPriority(absPath)) {
      // Regardless of whether we have a native file watcher and it works
      // correctly on this file system, poll prioritized files (that is,
      // files that have been changed at least once) at a higher frequency
      // (every 500ms by default).
      return NO_WATCHER_POLLING_INTERVAL;
    }

    if (WATCHER_ENABLED || PRIORITIZE_CHANGED) {
      // As long as native file watching is enabled (even if it doesn't
      // work correctly) and the developer hasn't explicitly opted out of
      // the file watching priority system, poll unchanged files at a
      // lower frequency (every 5000ms by default).
      return DEFAULT_POLLING_INTERVAL;
    }

    // If native file watching is disabled and the developer has
    // explicitly opted out of the priority system, poll everything at the
    // higher frequency (every 500ms by default). Note that this leads to
    // higher idle CPU usage, so the developer may want to adjust the
    // METEOR_WATCH_POLLING_INTERVAL_MS environment variable.
    return NO_WATCHER_POLLING_INTERVAL;
  }

  function fire(event) {
    if (event !== "change") {
      // When we receive a "delete" or "rename" event, the watcher is
      // probably not going to generate any more notifications for this
      // file, so we close and nullify the watcher to ensure that
      // entry.rewatch() will attempt to reestablish the watcher the next
      // time we call safeWatcher.watch.
      safeUnwatch();

      // Make sure we don't throttle the watchFile callback after a
      // "delete" or "rename" event, since it is now our only reliable
      // source of file change notifications.
      lastWatcherEventTime = 0;

    } else {
      changedPaths.add(absPath);
      rewatch();
    }

    callbacks.forEach(cb => cb.call(this, event));
  }

  function watchWrapper(event) {
    lastWatcherEventTime = +new Date;
    fire.call(this, event);

    // It's tempting to call unwatchFile(absPath, watchFileWrapper) here,
    // but previous watcher success is no guarantee of future watcher
    // reliability. For example, watchLibrary.watch works just fine when file
    // changes originate from within a Vagrant VM, but changes to shared
    // files made outside the VM are invisible to watcher, so our only
    // hope of catching them is to continue polling.
  }

  function rewatch() {
    if (hasPriority(absPath)) {
      if (watcher) {
        // Already watching; nothing to do.
        return;
      }
      watcher = watchLibraryWatch(absPath, watchWrapper);
    } else if (watcher) {
      safeUnwatch();
    }

    // Since we're about to restart the stat-based file watcher, we don't
    // want to miss any of its events because of the lastWatcherEventTime
    // throttling that it attempts to do.
    lastWatcherEventTime = 0;

    // We use files.watchFile in addition to watcher.watch as a fail-safe
    // to detect file changes even on network file systems.  However
    // (unless the user disabled watcher or this watcher call failed), we
    // use a relatively long default polling interval of 5000ms to save
    // CPU cycles.
    statWatch(absPath, getPollingInterval(), watchFileWrapper);
  }

  function watchFileWrapper(...args) {
    const [newStat, oldStat] = args;

    if (newStat.ino === 0 &&
        oldStat.ino === 0 &&
        +newStat.mtime === +oldStat.mtime) {
      // Node calls the watchFile listener once with bogus identical stat
      // objects, which should not trigger a file change event.
      return;
    }

    // If a watcher event fired in the last polling interval, ignore
    // this event.
    if (new Date - lastWatcherEventTime > getPollingInterval()) {
      fire.call(this, "change");
    }
  }

  const entry = {
    callbacks,
    rewatch,

    release(callback) {
      if (! entries[absPath]) {
        return;
      }

      callbacks.delete(callback);
      if (callbacks.size > 0) {
        return;
      }

      // Once there are no more callbacks in the Set, close both watchers
      // and nullify the shared data.
      clearTimeout(watcherCleanupTimer);
      watcherCleanupTimer = setTimeout(() => {
        if (callbacks.size > 0) {
          // If another callback was added while the timer was pending, we
          // can avoid tearing anything down.
          return;
        }
        entry.close();
      }, WATCHER_CLEANUP_DELAY_MS);
    },

    close() {
      if (entries[absPath] !== entry) return;
      entries[absPath] = null;

      if (watcherCleanupTimer) {
        clearTimeout(watcherCleanupTimer);
        watcherCleanupTimer = null;
      }

      safeUnwatch();

      unwatchFile(absPath, watchFileWrapper);
    }
  };

  if (ino > 0) {
    entriesByIno.set(ino, entry);
  }

  return entry;
}

export function closeAllWatchers() {
  Object.keys(entries).forEach(absPath => {
    const entry = entries[absPath];
    if (entry) {
      entry.close();
    }
  });
}

const statWatchers = Object.create(null);

function statWatch(absPath, interval, callback) {
  const oldWatcher = statWatchers[absPath];

  while (oldWatcher) {
    // Make sure this callback no longer appears among the listeners for
    // this StatWatcher.
    const countBefore = oldWatcher.stat.listenerCount("change");

    // This removes at most one occurrence of the callback from the
    // listeners list...
    oldWatcher.stat.removeListener("change", callback);

    // ... so we have to keep calling it until the first time
    // it removes nothing.
    if (oldWatcher.stat.listenerCount("change") === countBefore) {
      break;
    }
  }

  // This doesn't actually call newStat.start again if there's already a
  // watcher for this file, so it won't change any interval previously
  // specified. In the rare event that the interval needs to change, we
  // manually stop and restart the StatWatcher below.
  const newStat = watchFile(absPath, {
    persistent: false, // never persistent
    interval,
  }, callback);

  if (! oldWatcher) {
    const newWatcher = {
      stat: newStat,
      interval,
    };

    newStat.on("stop", () => {
      if (statWatchers[absPath] === newWatcher) {
        delete statWatchers[absPath];
      }
    });

    return statWatchers[absPath] = newWatcher;
  }

  // These should be identical at this point, but just in case.
  oldWatcher.stat = newStat;

  // If the interval needs to be changed, manually stop and restart the
  // StatWatcher using lower-level methods than unwatchFile and watchFile.
  if (oldWatcher.interval !== interval) {
    oldWatcher.stat.stop();
    oldWatcher.stat.start(
      convertToOSPath(pathResolve(absPath)),
      false, // never persistent
      oldWatcher.interval = interval,
    );
  }

  return oldWatcher;
}

function watchLibraryWatch(absPath, callback) {
  if (WATCHER_ENABLED) {
    try {
      return watchLibrary.watch(convertToOSPath(absPath), callback);
    } catch (e) {
      maybeSuggestRaisingWatchLimit(e);
      // ... ignore the error.  We'll still have watchFile, which is good
      // enough.
    }
  }

  return null;
}

let suggestedRaisingWatchLimit = false;

// This function is async so that archinfo.host() (which may call
// utils.execFileSync) will run in a Fiber.
async function maybeSuggestRaisingWatchLimit(error) {
  var constants = require('constants');
  var archinfo = require('../utils/archinfo.js');
  if (! suggestedRaisingWatchLimit &&
      // Note: the not-super-documented require('constants') maps from
      // strings to SYSTEM errno values. System errno values aren't the same
      // as the numbers used internally by libuv! Once we're upgraded
      // to Node 0.12, we'll have the system errno as a string (on 'code'),
      // but the support for that wasn't in Node 0.10's uv.
      // See our PR https://github.com/atom/node-pathwatcher/pull/53
      // (and make sure to read the final commit message, not the original
      // proposed PR, which had a slightly different interface).
      error.errno === constants.ENOSPC &&
      // The only suggestion we currently have is for Linux.
      archinfo.matches(archinfo.host(), 'os.linux')) {

    // Check suggestedRaisingWatchLimit again because archinfo.host() may
    // have yielded.
    if (suggestedRaisingWatchLimit) return;
    suggestedRaisingWatchLimit = true;

    var Console = require('../console/console.js').Console;
    if (! Console.isHeadless()) {
      Console.arrowWarn(
        "It looks like a simple tweak to your system's configuration will " +
          "make many tools (including this Meteor command) more efficient. " +
          "To learn more, see " +
          Console.url("https://github.com/meteor/docs/blob/master/long-form/file-change-watcher-efficiency.md"));
    }
  }
}

export const watch = Profile(
  "safeWatcher.watch",
  (absPath, callback) => {
    const entry = acquireWatcher(absPath, callback);
    return {
      close() {
        entry.release(callback);
      }
    };
  }
);


documentation: "README.md"
});
Npm.depends({
reify: "0.20.1"
});
Package.onUse(function(api) {

keep keep keep keep replace keep keep keep


// Keep the versions of these packages consistent with the versions

// found in dev-bundle-server-package.js.


///
/// Remote methods and access control.
///

const hasOwn = Object.prototype.hasOwnProperty;

// Restrict default mutators on collection. allow() and deny() take the
// same options:
//
// options.insert {Function(userId, doc)}
//   return true to allow/deny adding this document
//
// options.update {Function(userId, docs, fields, modifier)}
//   return true to allow/deny updating these documents.
//   `fields` is passed as an array of fields that are to be modified
//
// options.remove {Function(userId, docs)}
//   return true to allow/deny removing these documents
//
// options.fetch {Array}
//   Fields to fetch for these validators. If any call to allow or deny
//   does not have this option then all fields are loaded.
//
// allow and deny can be called multiple times. The validators are
// evaluated as follows:
// - If neither deny() nor allow() has been called on the collection,
//   then the request is allowed if and only if the "insecure" smart
//   package is in use.
// - Otherwise, if any deny() function returns true, the request is denied.
// - Otherwise, if any allow() function returns true, the request is allowed.
// - Otherwise, the request is denied.
//
// Meteor may call your deny() and allow() functions in any order, and may not
// call all of them if it is able to make a decision without calling them all
// (so don't include side effects).

AllowDeny = {
  CollectionPrototype: {}
};

// In the `mongo` package, we will extend Mongo.Collection.prototype with these
// methods
const CollectionPrototype = AllowDeny.CollectionPrototype;

/**
 * @summary Allow users to write directly to this collection from client code, subject to limitations you define.
 * @locus Server
 * @method allow
 * @memberOf Mongo.Collection
 * @instance
 * @param {Object} options
 * @param {Function} options.insert,update,remove Functions that look at a proposed modification to the database and return true if it should be allowed.
 * @param {String[]} options.fetch Optional performance enhancement. Limits the fields that will be fetched from the database for inspection by your `update` and `remove` functions.
 * @param {Function} options.transform Overrides `transform` on the  [`Collection`](#collections).  Pass `null` to disable transformation.
 */
CollectionPrototype.allow = function(options) {
  addValidator(this, 'allow', options);
};

/**
 * @summary Override `allow` rules.
 * @locus Server
 * @method deny
 * @memberOf Mongo.Collection
 * @instance
 * @param {Object} options
 * @param {Function} options.insert,update,remove Functions that look at a proposed modification to the database and return true if it should be denied, even if an [allow](#allow) rule says otherwise.
 * @param {String[]} options.fetch Optional performance enhancement. Limits the fields that will be fetched from the database for inspection by your `update` and `remove` functions.
 * @param {Function} options.transform Overrides `transform` on the  [`Collection`](#collections).  Pass `null` to disable transformation.
 */
CollectionPrototype.deny = function(options) {
  addValidator(this, 'deny', options);
};

CollectionPrototype._defineMutationMethods = function(options) {
  const self = this;
  options = options || {};

  // set to true once we call any allow or deny methods. If true, use
  // allow/deny semantics. If false, use insecure mode semantics.
  self._restricted = false;

  // Insecure mode (default to allowing writes). Defaults to 'undefined' which
  // means insecure iff the insecure package is loaded. This property can be
  // overriden by tests or packages wishing to change insecure mode behavior of
  // their collections.
  self._insecure = undefined;

  self._validators = {
    insert: {allow: [], deny: []},
    update: {allow: [], deny: []},
    remove: {allow: [], deny: []},
    upsert: {allow: [], deny: []}, // dummy arrays; can't set these!
    fetch: [],
    fetchAllFields: false
  };

  if (!self._name)
    return; // anonymous collection

  // XXX Think about method namespacing. Maybe methods should be
  // "Meteor:Mongo:insert/NAME"?
  self._prefix = '/' + self._name + '/';

  // Mutation Methods
  // Minimongo on the server gets no stubs; instead, by default
  // it wait()s until its result is ready, yielding.
  // This matches the behavior of macromongo on the server better.
  // XXX see #MeteorServerNull
  if (self._connection && (self._connection === Meteor.server || Meteor.isClient)) {
    const m = {};

    ['insert', 'update', 'remove'].forEach((method) => {
      const methodName = self._prefix + method;

      if (options.useExisting) {
        const handlerPropName = Meteor.isClient ? '_methodHandlers' : 'method_handlers';
        // Do not try to create additional methods if this has already been called.
        // (Otherwise the .methods() call below will throw an error.)
        if (self._connection[handlerPropName] &&
          typeof self._connection[handlerPropName][methodName] === 'function') return;
      }

      m[methodName] = function (/* ... */) {
        // All the methods do their own validation, instead of using check().
        check(arguments, [Match.Any]);
        const args = Array.from(arguments);
        try {
          // For an insert, if the client didn't specify an _id, generate one
          // now; because this uses DDP.randomStream, it will be consistent with
          // what the client generated. We generate it now rather than later so
          // that if (eg) an allow/deny rule does an insert to the same
          // collection (not that it really should), the generated _id will
          // still be the first use of the stream and will be consistent.
          //
          // However, we don't actually stick the _id onto the document yet,
          // because we want allow/deny rules to be able to differentiate
          // between arbitrary client-specified _id fields and merely
          // client-controlled-via-randomSeed fields.
          let generatedId = null;
          if (method === "insert" && !hasOwn.call(args[0], '_id')) {
            generatedId = self._makeNewID();
          }

          if (this.isSimulation) {
            // In a client simulation, you can do any mutation (even with a
            // complex selector).
            if (generatedId !== null)
              args[0]._id = generatedId;
            return self._collection[method].apply(
              self._collection, args);
          }

          // This is the server receiving a method call from the client.

          // We don't allow arbitrary selectors in mutations from the client: only
          // single-ID selectors.
          if (method !== 'insert')
            throwIfSelectorIsNotId(args[0], method);

          if (self._restricted) {
            // short circuit if there is no way it will pass.
            if (self._validators[method].allow.length === 0) {
              throw new Meteor.Error(
                403, "Access denied. No allow validators set on restricted " +
                  "collection for method '" + method + "'.");
            }

            const validatedMethodName =
                  '_validated' + method.charAt(0).toUpperCase() + method.slice(1);
            args.unshift(this.userId);
            method === 'insert' && args.push(generatedId);
            return self[validatedMethodName].apply(self, args);
          } else if (self._isInsecure()) {
            if (generatedId !== null)
              args[0]._id = generatedId;
            // In insecure mode, allow any mutation (with a simple selector).
            // XXX This is kind of bogus.  Instead of blindly passing whatever
            //     we get from the network to this function, we should actually
            //     know the correct arguments for the function and pass just
            //     them.  For example, if you have an extraneous extra null
            //     argument and this is Mongo on the server, the .wrapAsync'd
            //     functions like update will get confused and pass the
            //     "fut.resolver()" in the wrong slot, where _update will never
            //     invoke it. Bam, broken DDP connection.  Probably should just
            //     take this whole method and write it three times, invoking
            //     helpers for the common code.
            return self._collection[method].apply(self._collection, args);
          } else {
            // In secure mode, if we haven't called allow or deny, then nothing
            // is permitted.
            throw new Meteor.Error(403, "Access denied");
          }
        } catch (e) {
          if (
            e.name === 'MongoError' ||
            e.name === 'BulkWriteError' ||
            e.name === 'MinimongoError'
          ) {
            throw new Meteor.Error(409, e.toString());
          } else {
            throw e;
          }
        }
      };
    });

    self._connection.methods(m);
  }
};

CollectionPrototype._updateFetch = function (fields) {
  const self = this;

  if (!self._validators.fetchAllFields) {
    if (fields) {
      const union = Object.create(null);
      const add = names => names && names.forEach(name => union[name] = 1);
      add(self._validators.fetch);
      add(fields);
      self._validators.fetch = Object.keys(union);
    } else {
      self._validators.fetchAllFields = true;
      // clear fetch just to make sure we don't accidentally read it
      self._validators.fetch = null;
    }
  }
};

CollectionPrototype._isInsecure = function () {
  const self = this;
  if (self._insecure === undefined)
    return !!Package.insecure;
  return self._insecure;
};

CollectionPrototype._validatedInsert = function (userId, doc,
                                                         generatedId) {
  const self = this;

  // call user validators.
  // Any deny returns true means denied.
  if (self._validators.insert.deny.some((validator) => {
    return validator(userId, docToValidate(validator, doc, generatedId));
  })) {
    throw new Meteor.Error(403, "Access denied");
  }
  // Any allow returns true means proceed. Throw error if they all fail.
  if (self._validators.insert.allow.every((validator) => {
    return !validator(userId, docToValidate(validator, doc, generatedId));
  })) {
    throw new Meteor.Error(403, "Access denied");
  }

  // If we generated an ID above, insert it now: after the validation, but
  // before actually inserting.
  if (generatedId !== null)
    doc._id = generatedId;

  self._collection.insert.call(self._collection, doc);
};

// Simulate a mongo `update` operation while validating that the access
// control rules set by calls to `allow/deny` are satisfied. If all
// pass, rewrite the mongo operation to use $in to set the list of
// document ids to change ##ValidatedChange
CollectionPrototype._validatedUpdate = function(
    userId, selector, mutator, options) {
  const self = this;

  check(mutator, Object);

  options = Object.assign(Object.create(null), options);

  if (!LocalCollection._selectorIsIdPerhapsAsObject(selector))
    throw new Error("validated update should be of a single ID");

  // We don't support upserts because they don't fit nicely into allow/deny
  // rules.
  if (options.upsert)
    throw new Meteor.Error(403, "Access denied. Upserts not " +
                           "allowed in a restricted collection.");

  const noReplaceError = "Access denied. In a restricted collection you can only" +
        " update documents, not replace them. Use a Mongo update operator, such " +
        "as '$set'.";

  const mutatorKeys = Object.keys(mutator);

  // compute modified fields
  const modifiedFields = {};

  if (mutatorKeys.length === 0) {
    throw new Meteor.Error(403, noReplaceError);
  }
  mutatorKeys.forEach((op) => {
    const params = mutator[op];
    if (op.charAt(0) !== '$') {
      throw new Meteor.Error(403, noReplaceError);
    } else if (!hasOwn.call(ALLOWED_UPDATE_OPERATIONS, op)) {
      throw new Meteor.Error(
        403, "Access denied. Operator " + op + " not allowed in a restricted collection.");
    } else {
      Object.keys(params).forEach((field) => {
        // treat dotted fields as if they are replacing their
        // top-level part
        if (field.indexOf('.') !== -1)
          field = field.substring(0, field.indexOf('.'));

        // record the field we are trying to change
        modifiedFields[field] = true;
      });
    }
  });

  const fields = Object.keys(modifiedFields);

  const findOptions = {transform: null};
  if (!self._validators.fetchAllFields) {
    findOptions.fields = {};
    self._validators.fetch.forEach((fieldName) => {
      findOptions.fields[fieldName] = 1;
    });
  }

  const doc = self._collection.findOne(selector, findOptions);
  if (!doc)  // none satisfied!
    return 0;

  // call user validators.
  // Any deny returns true means denied.
  if (self._validators.update.deny.some((validator) => {
    const factoriedDoc = transformDoc(validator, doc);
    return validator(userId,
                     factoriedDoc,
                     fields,
                     mutator);
  })) {
    throw new Meteor.Error(403, "Access denied");
  }
  // Any allow returns true means proceed. Throw error if they all fail.
  if (self._validators.update.allow.every((validator) => {
    const factoriedDoc = transformDoc(validator, doc);
    return !validator(userId,
                      factoriedDoc,
                      fields,
                      mutator);
  })) {
    throw new Meteor.Error(403, "Access denied");
  }

  options._forbidReplace = true;

  // Back when we supported arbitrary client-provided selectors, we actually
  // rewrote the selector to include an _id clause before passing to Mongo to
  // avoid races, but since selector is guaranteed to already just be an ID, we
  // don't have to any more.

  return self._collection.update.call(
    self._collection, selector, mutator, options);
};

// Only allow these operations in validated updates. Specifically
// whitelist operations, rather than blacklist, so new complex
// operations that are added aren't automatically allowed. A complex
// operation is one that does more than just modify its target
// field. For now this contains all update operations except '$rename'.
// http://docs.mongodb.org/manual/reference/operators/#update
const ALLOWED_UPDATE_OPERATIONS = {
  $inc:1, $set:1, $unset:1, $addToSet:1, $pop:1, $pullAll:1, $pull:1,
  $pushAll:1, $push:1, $bit:1
};

// Simulate a mongo `remove` operation while validating access control
// rules. See #ValidatedChange
CollectionPrototype._validatedRemove = function(userId, selector) {
  const self = this;

  const findOptions = {transform: null};
  if (!self._validators.fetchAllFields) {
    findOptions.fields = {};
    self._validators.fetch.forEach((fieldName) => {
      findOptions.fields[fieldName] = 1;
    });
  }

  const doc = self._collection.findOne(selector, findOptions);
  if (!doc)
    return 0;

  // call user validators.
  // Any deny returns true means denied.
  if (self._validators.remove.deny.some((validator) => {
    return validator(userId, transformDoc(validator, doc));
  })) {
    throw new Meteor.Error(403, "Access denied");
  }
  // Any allow returns true means proceed. Throw error if they all fail.
  if (self._validators.remove.allow.every((validator) => {
    return !validator(userId, transformDoc(validator, doc));
  })) {
    throw new Meteor.Error(403, "Access denied");
  }

  // Back when we supported arbitrary client-provided selectors, we actually
  // rewrote the selector to {_id: {$in: [ids that we found]}} before passing to
  // Mongo to avoid races, but since selector is guaranteed to already just be
  // an ID, we don't have to any more.

  return self._collection.remove.call(self._collection, selector);
};

CollectionPrototype._callMutatorMethod = function _callMutatorMethod(name, args, callback) {
  if (Meteor.isClient && !callback && !alreadyInSimulation()) {
    // Client can't block, so it can't report errors by exception,
    // only by callback. If they forget the callback, give them a
    // default one that logs the error, so they aren't totally
    // baffled if their writes don't work because their database is
    // down.
    // Don't give a default callback in simulation, because inside stubs we
    // want to return the results from the local collection immediately and
    // not force a callback.
    callback = function (err) {
      if (err)
        Meteor._debug(name + " failed", err);
    };
  }

  // For two out of three mutator methods, the first argument is a selector
  const firstArgIsSelector = name === "update" || name === "remove";
  if (firstArgIsSelector && !alreadyInSimulation()) {
    // If we're about to actually send an RPC, we should throw an error if
    // this is a non-ID selector, because the mutation methods only allow
    // single-ID selectors. (If we don't throw here, we'll see flicker.)
    throwIfSelectorIsNotId(args[0], name);
  }

  const mutatorMethodName = this._prefix + name;
  return this._connection.apply(
    mutatorMethodName, args, { returnStubValue: true }, callback);
}

function transformDoc(validator, doc) {
  if (validator.transform)
    return validator.transform(doc);
  return doc;
}

function docToValidate(validator, doc, generatedId) {
  let ret = doc;
  if (validator.transform) {
    ret = EJSON.clone(doc);
    // If you set a server-side transform on your collection, then you don't get
    // to tell the difference between "client specified the ID" and "server
    // generated the ID", because transforms expect to get _id.  If you want to
    // do that check, you can do it with a specific
    // `C.allow({insert: f, transform: null})` validator.
    if (generatedId !== null) {
      ret._id = generatedId;
    }
    ret = validator.transform(ret);
  }
  return ret;
}

function addValidator(collection, allowOrDeny, options) {
  // validate keys
  const validKeysRegEx = /^(?:insert|update|remove|fetch|transform)$/;
  Object.keys(options).forEach((key) => {
    if (!validKeysRegEx.test(key))
      throw new Error(allowOrDeny + ": Invalid key: " + key);
  });

  collection._restricted = true;

  ['insert', 'update', 'remove'].forEach((name) => {
    if (hasOwn.call(options, name)) {
      if (!(options[name] instanceof Function)) {
        throw new Error(allowOrDeny + ": Value for `" + name + "` must be a function");
      }

      // If the transform is specified at all (including as 'null') in this
      // call, then take that; otherwise, take the transform from the
      // collection.
      if (options.transform === undefined) {
        options[name].transform = collection._transform;  // already wrapped
      } else {
        options[name].transform = LocalCollection.wrapTransform(
          options.transform);
      }

      collection._validators[name][allowOrDeny].push(options[name]);
    }
  });

  // Only update the fetch fields if we're passed things that affect
  // fetching. This way allow({}) and allow({insert: f}) don't result in
  // setting fetchAllFields
  if (options.update || options.remove || options.fetch) {
    if (options.fetch && !(options.fetch instanceof Array)) {
      throw new Error(allowOrDeny + ": Value for `fetch` must be an array");
    }
    collection._updateFetch(options.fetch);
  }
}

function throwIfSelectorIsNotId(selector, methodName) {
  if (!LocalCollection._selectorIsIdPerhapsAsObject(selector)) {
    throw new Meteor.Error(
      403, "Not permitted. Untrusted code may only " + methodName +
        " documents by ID.");
  }
};

// Determine if we are in a DDP method simulation
function alreadyInSimulation() {
  var CurrentInvocation =
    DDP._CurrentMethodInvocation ||
    // For backwards compatibility, as explained in this issue:
    // https://github.com/meteor/meteor/issues/8947
    DDP._CurrentInvocation;

  const enclosing = CurrentInvocation.get();
  return enclosing && enclosing.isSimulation;
}


var selftest = require('../tool-testing/selftest.js');
var Sandbox = selftest.Sandbox;

selftest.skip.define("minifiers can't register non-js/non-css extensions", [], function () {
  var s = new Sandbox();
  var run;

  s.createApp("myapp", "minifier-plugin-bad-extension", { dontPrepareApp: true });
  s.cd("myapp");

  run = s.run();
  run.match("foo: Minifiers are only allowed to register \"css\" or \"js\" extensions.");
  run.stop();
});

selftest.skip.define("minifiers: apps can't use more than one package providing a minifier for the same extension", [], function () {
  var s = new Sandbox();
  var run;

  s.createApp("myapp", "minifier-plugin-multiple-minifiers-for-js", { dontPrepareApp: true });
  s.cd("myapp");

  run = s.run("--production");
  run.match("local-plugin, local-plugin-2: multiple packages registered minifiers for extension \"js\".");
  run.stop();
});


// This file contains a package.json for the dependencies of the command-line
// tool.

// We put this in a JS file so that it can contain comments. It is processed
// into a package.json file by generate-dev-bundle.sh.

var packageJson = {
  name: "meteor-dev-bundle-tool",
  private: true,
  dependencies: {
    // Explicit dependency because we are replacing it with a bundled version
    // and we want to make sure there are no dependencies on a higher version
    npm: "6.9.0",
    pacote: "https://github.com/meteor/pacote/tarball/c5043daa1b768594e01d76275e3854fc19f038f9",
    "node-gyp": "3.7.0",
    "node-pre-gyp": "0.10.3",
    "meteor-babel": "7.4.8",
    // Keep the versions of these packages consistent with the versions
    // found in dev-bundle-server-package.js.
    "meteor-promise": "0.8.7",
    reify: "0.20.1",
    fibers: "3.1.1",
    // So that Babel can emit require("@babel/runtime/helpers/...") calls.
    "@babel/runtime": "7.4.4",
    // For backwards compatibility with isopackets that still depend on
    // babel-runtime rather than @babel/runtime.
    "babel-runtime": "7.0.0-beta.3",
    // Not yet upgrading Underscore from 1.5.2 to 1.7.0 (which should be done
    // in the package too) because we should consider using lodash instead
    // (and there are backwards-incompatible changes either way).
    underscore: "1.5.2",
    "source-map-support": "https://github.com/meteor/node-source-map-support/tarball/1912478769d76e5df4c365e147f25896aee6375e",
    semver: "5.4.1",
    request: "2.88.0",
    uuid: "3.3.2",
    fstream: "https://github.com/meteor/fstream/tarball/cf4ea6c175355cec7bee38311e170d08c4078a5d",
    tar: "2.2.1",
    kexec: "3.0.0",
    "source-map": "0.5.7",
    chalk: "0.5.1",
    sqlite3: "3.1.8",
    netroute: "1.0.2",
    "http-proxy": "1.16.2",
    "is-reachable": "3.1.0",
    "wordwrap": "0.0.2",
    "moment": "2.20.1",
    "rimraf": "2.6.2",
    "glob": "7.1.2",
    ignore: "3.3.7",
    // XXX: When we update this, see if it fixes this Github issue:
    // https://github.com/jgm/CommonMark/issues/276 . If it does, remove the
    // workaround from the tool.
    "commonmark": "0.15.0",
    escope: "3.6.0",
    split2: "2.2.0",
    multipipe: "2.0.1",
    pathwatcher: "8.0.2",
    // The @wry/context package version must be compatible with the
    // version constraint imposed by optimism/package.json.
    optimism: "0.9.5",
    "@wry/context": "0.4.0",
    'lru-cache': '4.1.3'
  }
};

if (process.platform === 'win32') {
  // Remove dependencies that do not work on Windows
  delete packageJson.dependencies.netroute;
  delete packageJson.dependencies.kexec;
}

process.stdout.write(JSON.stringify(packageJson, null, 2) + '\n');


if (Meteor.isClient) {

  var isParent = (window.location.pathname === '/');
  var isChild = ! isParent;

  Template.route.isParent = function () {
    return isParent;
  };

  Template.parent.testStatus = function () {
    return Session.get('testStatus');
  };

  Template.parent.events({
    'click #openTab': function () {
      window.open('/child');
    },

    'click #runTest': function () {
      if (localStorage.getItem('ping') === '!' ||
          localStorage.getItem('pong') === '!') {
        Session.set('testStatus', 'Test already run.  Close the second tab (if open), refresh this page, and run again.');
      }
      else {
        localStorage.setItem('ping', '!');
      }
    }
  });

  if (isParent) {
    Session.set('testStatus', '');

    Meteor.startup(function () {
      localStorage.setItem('ping', null);
      localStorage.setItem('pong', null);
    });
    window.addEventListener('storage', function (event) {
      if (event.key === 'pong' && event.newValue === '!') {
        Session.set('testStatus', 'test successful');
      }
    });
  }

  if (isChild) {
    window.addEventListener('storage', function (event) {
      if (event.key === 'ping' && event.newValue === '!') {
        // If we used setTimeout here in iOS Safari it wouldn't
        // work (unless we switched tabs) because setTimeout and
        // setInterval events don't fire in inactive tabs.
        Meteor.defer(function () {
          localStorage.setItem('pong', '!');
        });
      }
    });
  }

}


} else if (range === 'berry' || range === 'v2' || range === '2') {

const releases = await fetchReleases(config, {

const release = releases.find(release => {

keep keep keep keep keep replace replace replace keep keep keep keep

/* @flow */

import fs from 'fs';
import http from 'http';
import url from 'url';
import dnscache from 'dnscache';
import invariant from 'invariant';
import RequestCaptureHar from 'request-capture-har';

import type {Reporter} from '../reporters/index.js';
import {MessageError, ResponseError, OneTimePasswordError} from '../errors.js';
import BlockingQueue from './blocking-queue.js';
import * as constants from '../constants.js';
import * as network from './network.js';
import map from '../util/map.js';

import typeof * as RequestModuleT from 'request';

// Initialize DNS cache so we don't look up the same
// domains like registry.yarnpkg.com over and over again
// for each request.
dnscache({
  enable: true,
  ttl: 300,
  cachesize: 10,
});
const successHosts = map();
const controlOffline = network.isOffline();

interface RequestError extends Error {
  hostname?: ?string,
  code?: ?string,
}

export type RequestMethods = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE';

type RequestParams<T> = {
  url: string,
  auth?: {
    email?: string,
    username?: string,
    password?: string,
    token?: string,
  },
  buffer?: boolean,
  method?: RequestMethods,
  queue?: BlockingQueue,
  json?: boolean,
  body?: mixed,
  proxy?: string,
  encoding?: ?string,
  ca?: Array<string>,
  cert?: string,
  networkConcurrency?: number,
  timeout?: number,
  key?: string,
  forever?: boolean,
  strictSSL?: boolean,
  headers?: {
    [name: string]: string,
  },
  process?: (req: Object, resolve: (body: T) => void, reject: (err: Error) => void) => void,
  callback?: (err: ?Error, res: any, body: any) => void,
  retryAttempts?: number,
  maxRetryAttempts?: number,
  followRedirect?: boolean,
  rejectStatusCode?: number | Array<number>,
};

type RequestOptions = {
  retryReason: ?string,
  params: RequestParams<Object>,
  resolve: (body: any) => void,
  reject: (err: any) => void,
};

export default class RequestManager {
  constructor(reporter: Reporter) {
    this.offlineNoRequests = false;
    this._requestCaptureHar = null;
    this._requestModule = null;
    this.offlineQueue = [];
    this.captureHar = false;
    this.httpsProxy = '';
    this.ca = null;
    this.httpProxy = '';
    this.strictSSL = true;
    this.userAgent = '';
    this.reporter = reporter;
    this.running = 0;
    this.queue = [];
    this.cache = {};
    this.max = constants.NETWORK_CONCURRENCY;
    this.maxRetryAttempts = 5;
  }

  offlineNoRequests: boolean;
  captureHar: boolean;
  userAgent: string;
  reporter: Reporter;
  running: number;
  httpsProxy: string | boolean;
  httpProxy: string | boolean;
  strictSSL: boolean;
  ca: ?Array<string>;
  cert: ?string;
  key: ?string;
  offlineQueue: Array<RequestOptions>;
  queue: Array<Object>;
  max: number;
  timeout: number;
  maxRetryAttempts: number;
  cache: {
    [key: string]: Promise<any>,
  };

  _requestCaptureHar: ?RequestCaptureHar;
  _requestModule: ?RequestModuleT;

  setOptions(opts: {
    userAgent?: string,
    offline?: boolean,
    captureHar?: boolean,
    httpProxy?: string | boolean,
    httpsProxy?: string | boolean,
    strictSSL?: boolean,
    ca?: Array<string>,
    cafile?: string,
    cert?: string,
    networkConcurrency?: number,
    networkTimeout?: number,
    maxRetryAttempts?: number,
    key?: string,
  }) {
    if (opts.userAgent != null) {
      this.userAgent = opts.userAgent;
    }

    if (opts.offline != null) {
      this.offlineNoRequests = opts.offline;
    }

    if (opts.captureHar != null) {
      this.captureHar = opts.captureHar;
    }

    if (opts.httpProxy != null) {
      this.httpProxy = opts.httpProxy || '';
    }

    if (opts.httpsProxy === '') {
      this.httpsProxy = opts.httpProxy || '';
    } else if (opts.httpsProxy === false) {
      this.httpsProxy = false;
    } else {
      this.httpsProxy = opts.httpsProxy || '';
    }

    if (opts.strictSSL !== null && typeof opts.strictSSL !== 'undefined') {
      this.strictSSL = opts.strictSSL;
    }

    if (opts.ca != null && opts.ca.length > 0) {
      this.ca = opts.ca;
    }

    if (opts.networkConcurrency != null) {
      this.max = opts.networkConcurrency;
    }

    if (opts.networkTimeout != null) {
      this.timeout = opts.networkTimeout;
    }

    if (opts.maxRetryAttempts != null) {
      this.maxRetryAttempts = opts.maxRetryAttempts;
    }

    if (opts.cafile != null && opts.cafile != '') {
      // The CA bundle file can contain one or more certificates with comments/text between each PEM block.
      // tls.connect wants an array of certificates without any comments/text, so we need to split the string
      // and strip out any text in between the certificates
      try {
        const bundle = fs.readFileSync(opts.cafile).toString();
        const hasPemPrefix = block => block.startsWith('-----BEGIN ');
        // opts.cafile overrides opts.ca, this matches with npm behavior
        this.ca = bundle.split(/(-----BEGIN .*\r?\n[^-]+\r?\n--.*)/).filter(hasPemPrefix);
      } catch (err) {
        this.reporter.error(`Could not open cafile: ${err.message}`);
      }
    }

    if (opts.cert != null) {
      this.cert = opts.cert;
    }

    if (opts.key != null) {
      this.key = opts.key;
    }
  }

  /**
   * Lazy load `request` since it is exceptionally expensive to load and is
   * often not needed at all.
   */

  _getRequestModule(): RequestModuleT {
    if (!this._requestModule) {
      const request = require('request');
      if (this.captureHar) {
        this._requestCaptureHar = new RequestCaptureHar(request);
        this._requestModule = this._requestCaptureHar.request.bind(this._requestCaptureHar);
      } else {
        this._requestModule = request;
      }
    }
    return this._requestModule;
  }

  /**
   * Queue up a request.
   */

  request<T>(params: RequestParams<T>): Promise<T> {
    if (this.offlineNoRequests) {
      return Promise.reject(new MessageError(this.reporter.lang('cantRequestOffline', params.url)));
    }

    const cached = this.cache[params.url];
    if (cached) {
      return cached;
    }

    params.method = params.method || 'GET';
    params.forever = true;
    params.retryAttempts = 0;
    params.strictSSL = this.strictSSL;
    params.headers = Object.assign(
      {
        'User-Agent': this.userAgent,
      },
      params.headers,
    );

    const promise = new Promise((resolve, reject) => {
      this.queue.push({params, reject, resolve});
      this.shiftQueue();
    });

    // we can't cache a request with a processor
    if (!params.process) {
      this.cache[params.url] = promise;
    }

    return promise;
  }

  /**
   * Clear the request cache. This is important as we cache all HTTP requests so you'll
   * want to do this as soon as you can.
   */

  clearCache() {
    this.cache = {};
    if (this._requestCaptureHar != null) {
      this._requestCaptureHar.clear();
    }
  }

  /**
   * Check if an error is possibly due to lost or poor network connectivity.
   */

  isPossibleOfflineError(err: RequestError): boolean {
    const {code, hostname} = err;
    if (!code) {
      return false;
    }

    // network was previously online but now we're offline
    const possibleOfflineChange = !controlOffline && !network.isOffline();
    if (code === 'ENOTFOUND' && possibleOfflineChange) {
      // can't resolve a domain
      return true;
    }

    // used to be able to resolve this domain! something is wrong
    if (code === 'ENOTFOUND' && hostname && successHosts[hostname]) {
      // can't resolve this domain but we've successfully resolved it before
      return true;
    }

    // network was previously offline and we can't resolve the domain
    if (code === 'ENOTFOUND' && controlOffline) {
      return true;
    }

    // connection was reset or dropped
    if (code === 'ECONNRESET') {
      return true;
    }

    // TCP timeout
    if (code === 'ESOCKETTIMEDOUT' || code === 'ETIMEDOUT') {
      return true;
    }

    return false;
  }

  /**
   * Queue up request arguments to be retried. Start a network connectivity timer if there
   * isn't already one.
   */

  queueForRetry(opts: RequestOptions) {
    if (opts.retryReason) {
      let containsReason = false;

      for (const queuedOpts of this.offlineQueue) {
        if (queuedOpts.retryReason === opts.retryReason) {
          containsReason = true;
          break;
        }
      }

      if (!containsReason) {
        this.reporter.info(opts.retryReason);
      }
    }

    if (!this.offlineQueue.length) {
      this.initOfflineRetry();
    }

    this.offlineQueue.push(opts);
  }

  /**
   * Begin timers to retry failed requests when we possibly establish network connectivity
   * again.
   */

  initOfflineRetry() {
    setTimeout(() => {
      const queue = this.offlineQueue;
      this.offlineQueue = [];
      for (const opts of queue) {
        this.execute(opts);
      }
    }, 3000);
  }

  /**
   * Execute a request.
   */

  execute(opts: RequestOptions) {
    const {params} = opts;
    const {reporter} = this;

    const buildNext = fn => data => {
      fn(data);
      this.running--;
      this.shiftQueue();
    };

    const resolve = buildNext(opts.resolve);

    const rejectNext = buildNext(opts.reject);
    const reject = function(err) {
      err.message = `${params.url}: ${err.message}`;
      rejectNext(err);
    };

    const queueForRetry = reason => {
      const attempts = params.retryAttempts || 0;
      if (attempts >= this.maxRetryAttempts - 1) {
        return false;
      }
      if (opts.params.method && opts.params.method.toUpperCase() !== 'GET') {
        return false;
      }
      params.retryAttempts = attempts + 1;
      if (typeof params.cleanup === 'function') {
        params.cleanup();
      }
      opts.retryReason = reason;
      this.queueForRetry(opts);
      return true;
    };

    let calledOnError = false;
    const onError = err => {
      if (calledOnError) {
        return;
      }
      calledOnError = true;

      if (this.isPossibleOfflineError(err)) {
        if (!queueForRetry(this.reporter.lang('offlineRetrying'))) {
          reject(err);
        }
      } else {
        reject(err);
      }
    };

    if (!params.process) {
      const parts = url.parse(params.url);

      params.callback = (err, res, body) => {
        if (err) {
          onError(err);
          return;
        }

        successHosts[parts.hostname] = true;

        this.reporter.verbose(this.reporter.lang('verboseRequestFinish', params.url, res.statusCode));

        if (res.statusCode === 408 || res.statusCode >= 500) {
          const description = `${res.statusCode} ${http.STATUS_CODES[res.statusCode]}`;
          if (!queueForRetry(this.reporter.lang('internalServerErrorRetrying', description))) {
            throw new ResponseError(this.reporter.lang('requestFailed', description), res.statusCode);
          } else {
            return;
          }
        }

        if (res.statusCode === 401 && res.headers['www-authenticate']) {
          const authMethods = res.headers['www-authenticate'].split(/,\s*/).map(s => s.toLowerCase());

          if (authMethods.indexOf('otp') !== -1) {
            reject(new OneTimePasswordError());
            return;
          }
        }

        if (body && typeof body.error === 'string') {
          reject(new Error(body.error));
          return;
        }

        if ([400, 401, 404].concat(params.rejectStatusCode || []).indexOf(res.statusCode) !== -1) {
          // So this is actually a rejection ... the hosted git resolver uses this to know whether http is supported
          resolve(false);
        } else if (res.statusCode >= 400) {
          const errMsg = (body && body.message) || reporter.lang('requestError', params.url, res.statusCode);
          reject(new Error(errMsg));
        } else {
          resolve(body);
        }
      };
    }

    if (params.buffer) {
      params.encoding = null;
    }

    let proxy = this.httpProxy;
    if (params.url.startsWith('https:')) {
      proxy = this.httpsProxy;
    }

    if (proxy) {
      // if no proxy is set, do not pass a proxy down to request.
      // the request library will internally check the HTTP_PROXY and HTTPS_PROXY env vars.
      params.proxy = String(proxy);
    } else if (proxy === false) {
      // passing empty string prevents the underlying library from falling back to the env vars.
      // an explicit false in the yarn config should override the env var. See #4546.
      params.proxy = '';
    }

    if (this.ca != null) {
      params.ca = this.ca;
    }

    if (this.cert != null) {
      params.cert = this.cert;
    }

    if (this.key != null) {
      params.key = this.key;
    }

    if (this.timeout != null) {
      params.timeout = this.timeout;
    }

    const request = this._getRequestModule();
    const req = request(params);
    this.reporter.verbose(this.reporter.lang('verboseRequestStart', params.method, params.url));

    req.on('error', onError);

    const queue = params.queue;
    if (queue) {
      req.on('data', queue.stillActive.bind(queue));
    }

    const process = params.process;
    if (process) {
      req.on('response', res => {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          return;
        }

        const description = `${res.statusCode} ${http.STATUS_CODES[res.statusCode]}`;
        reject(new ResponseError(this.reporter.lang('requestFailed', description), res.statusCode));

        req.abort();
      });
      process(req, resolve, reject);
    }
  }

  /**
   * Remove an item from the queue. Create it's request options and execute it.
   */

  shiftQueue() {
    if (this.running >= this.max || !this.queue.length) {
      return;
    }

    const opts = this.queue.shift();

    this.running++;
    this.execute(opts);
  }

  saveHar(filename: string) {
    if (!this.captureHar) {
      throw new Error(this.reporter.lang('requestManagerNotSetupHAR'));
    }
    // No request may have occurred at all.
    this._getRequestModule();
    invariant(this._requestCaptureHar != null, 'request-capture-har not setup');
    this._requestCaptureHar.saveHar(filename);
  }
}


/* @flow */

import {sortAlpha} from '../util/misc.js';
import {LOCKFILE_VERSION} from '../constants.js';

import {version as YARN_VERSION} from '../../package.json';
const NODE_VERSION = process.version;

function shouldWrapKey(str: string): boolean {
  return (
    str.indexOf('true') === 0 ||
    str.indexOf('false') === 0 ||
    /[:\s\n\\",\[\]]/g.test(str) ||
    /^[0-9]/g.test(str) ||
    !/^[a-zA-Z]/g.test(str)
  );
}

function maybeWrap(str: string | boolean | number): string {
  if (typeof str === 'boolean' || typeof str === 'number' || shouldWrapKey(str)) {
    return JSON.stringify(str);
  } else {
    return str;
  }
}

const priorities: {[key: string]: number} = {
  name: 1,
  version: 2,
  uid: 3,
  resolved: 4,
  integrity: 5,
  registry: 6,
  dependencies: 7,
};

function priorityThenAlphaSort(a: string, b: string): number {
  if (priorities[a] || priorities[b]) {
    return (priorities[a] || 100) > (priorities[b] || 100) ? 1 : -1;
  } else {
    return sortAlpha(a, b);
  }
}

type Options = {
  indent: string,
  topLevel?: boolean,
};

function _stringify(obj: {[key: string]: mixed}, options: Options): string {
  if (typeof obj !== 'object') {
    throw new TypeError();
  }

  const indent = options.indent;
  const lines = [];

  // Sorting order needs to be consistent between runs, we run native sort by name because there are no
  // problems with it being unstable because there are no to keys the same
  // However priorities can be duplicated and native sort can shuffle things from run to run
  const keys = Object.keys(obj).sort(priorityThenAlphaSort);

  let addedKeys = [];

  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];
    const val = obj[key];
    if (val == null || addedKeys.indexOf(key) >= 0) {
      continue;
    }

    const valKeys = [key];

    // get all keys that have the same value equality, we only want this for objects
    if (typeof val === 'object') {
      for (let j = i + 1; j < keys.length; j++) {
        const key = keys[j];
        if (val === obj[key]) {
          valKeys.push(key);
        }
      }
    }

    const keyLine = valKeys.sort(sortAlpha).map(maybeWrap).join(', ');

    if (typeof val === 'string' || typeof val === 'boolean' || typeof val === 'number') {
      lines.push(`${keyLine} ${maybeWrap(val)}`);
    } else if (typeof val === 'object') {
      lines.push(`${keyLine}:\n${_stringify(val, {indent: indent + '  '})}` + (options.topLevel ? '\n' : ''));
    } else {
      throw new TypeError();
    }

    addedKeys = addedKeys.concat(valKeys);
  }

  return indent + lines.join(`\n${indent}`);
}

export default function stringify(obj: Object, noHeader?: boolean, enableVersions?: boolean): string {
  const val = _stringify(obj, {
    indent: '',
    topLevel: true,
  });
  if (noHeader) {
    return val;
  }

  const lines = [];
  lines.push('# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.');
  lines.push(`# yarn lockfile v${LOCKFILE_VERSION}`);
  if (enableVersions) {
    lines.push(`# yarn v${YARN_VERSION}`);
    lines.push(`# node ${NODE_VERSION}`);
  }
  lines.push('\n');
  lines.push(val);

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


'$0. Mirror tarball appears to be corrupt. You can resolve this by running:\n\n  rm -rf $1\n  yarn install',

errorExtractingTarball: 'Extracting tar content of $1 failed, the file appears to be corrupt: $0',

hostedGitResolveError: 'Error connecting to repository. Please, check the url.',

unknownFetcherFor: 'Unknown fetcher for $0',

downloadGitWithoutCommit: 'Downloading the git repo $0 over plain git without a commit hash',

downloadHTTPWithoutCommit: 'Downloading the git repo $0 over HTTP without a commit hash',

keep keep keep add keep keep keep keep keep

unauthorizedResponse: 'Received a 401 from $0. $1',


/* @flow */

import type {Manifest} from '../../types.js';
import type PackageRequest from '../../package-request.js';
import {MessageError} from '../../errors.js';
import ExoticResolver from './exotic-resolver.js';

export default class RegistryResolver extends ExoticResolver {
  constructor(request: PackageRequest, fragment: string) {
    super(request, fragment);

    const match = fragment.match(/^(\S+):(@?.*?)(@(.*?)|)$/);
    if (match) {
      this.range = match[4] || 'latest';
      this.name = match[2];
    } else {
      throw new MessageError(this.reporter.lang('invalidFragment', fragment));
    }

    // $FlowFixMe
    this.registry = this.constructor.protocol;
  }

  static factory: Function;
  name: string;
  range: string;

  resolve(): Promise<Manifest> {
    return this.fork(this.constructor.factory, false, this.name, this.range);
  }
}


err.message = `${params.url}: ${err.message}`;

const attempts = params.retryAttempts || 0;

if (attempts >= this.maxRetryAttempts - 1) {

keep keep keep keep add keep keep keep keep keep

const rejectWithoutUrl = function(err) {


The MIT License (MIT)

Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


if (res.statusCode === 401 && res.headers['www-authenticate']) {

const authMethods = res.headers['www-authenticate'].split(/,\s*/).map(s => s.toLowerCase());

if (res.statusCode === 401 && res.caseless && res.caseless.get('server') === 'GitHub.com') {

const message = `${res.body.message}. If using GITHUB_TOKEN in your env, check that it is valid.`;

rejectWithoutUrl(new Error(this.reporter.lang('unauthorizedResponse', res.caseless.get('server'), message)));

/* @flow */

import type {Reporter} from '../../reporters/index.js';
import type Config from '../../config.js';
import buildSubCommands from './_build-sub-commands.js';
import {getRcConfigForFolder} from '../../rc.js';
import * as fs from '../../util/fs.js';
import {stringify} from '../../lockfile';

const chalk = require('chalk');
const invariant = require('invariant');
const path = require('path');
const semver = require('semver');

type ReleaseAsset = {|
  id: any,

  name: string,
  browser_download_url: string,
|};

type Release = {|
  id: any,

  draft: boolean,
  prerelease: boolean,

  tag_name: string,
  version: {|
    version: string,
  |},

  assets: Array<ReleaseAsset>,
|};

function getBundleAsset(release: Release): ?ReleaseAsset {
  return release.assets.find(asset => {
    return asset.name.match(/^yarn-[0-9]+\.[0-9]+\.[0-9]+\.js$/);
  });
}

type FetchReleasesOptions = {|
  includePrereleases: boolean,
|};

async function fetchReleases(
  config: Config,
  {includePrereleases = false}: FetchReleasesOptions = {},
): Promise<Array<Release>> {
  const token = process.env.GITHUB_TOKEN;
  const tokenUrlParameter = token ? `?access_token=${token}` : '';
  const request: Array<Release> = await config.requestManager.request({
    url: `https://api.github.com/repos/yarnpkg/yarn/releases${tokenUrlParameter}`,
    json: true,
  });

  const releases = request.filter(release => {
    if (release.draft) {
      return false;
    }

    if (release.prerelease && !includePrereleases) {
      return false;
    }

    // $FlowFixMe
    release.version = semver.coerce(release.tag_name);

    if (!release.version) {
      return false;
    }

    if (!getBundleAsset(release)) {
      return false;
    }

    return true;
  });

  releases.sort((a, b) => {
    // $FlowFixMe
    return -semver.compare(a.version, b.version);
  });

  return releases;
}

function fetchBundle(config: Config, url: string): Promise<Buffer> {
  return config.requestManager.request({
    url,
    buffer: true,
  });
}

export function hasWrapper(flags: Object, args: Array<string>): boolean {
  return false;
}

const {run, setFlags, examples} = buildSubCommands('policies', {
  async setVersion(config: Config, reporter: Reporter, flags: Object, args: Array<string>): Promise<void> {
    let range = args[0] || 'latest';
    let allowRc = flags.rc;

    reporter.log(`Resolving ${chalk.yellow(range)} to a url...`);

    if (range === 'rc') {
      range = 'latest';
      allowRc = true;
    }

    if (range === 'latest') {
      range = '*';
    }

    let bundleUrl;
    let bundleVersion;
    let isV2 = false;

    if (range === 'nightly' || range === 'nightlies') {
      bundleUrl = 'https://nightly.yarnpkg.com/latest.js';
      bundleVersion = 'nightly';
    } else if (range === 'berry' || range === 'v2' || range === '2') {
      bundleUrl = 'https://github.com/yarnpkg/berry/raw/master/packages/berry-cli/bin/berry.js';
      bundleVersion = 'berry';
      isV2 = true;
    } else {
      const releases = await fetchReleases(config, {
        includePrereleases: allowRc,
      });

      const release = releases.find(release => {
        // $FlowFixMe
        return semver.satisfies(release.version, range);
      });

      if (!release) {
        throw new Error(`Release not found: ${range}`);
      }

      const asset = getBundleAsset(release);
      invariant(asset, 'The bundle asset should exist');

      bundleUrl = asset.browser_download_url;
      bundleVersion = release.version.version;
    }

    reporter.log(`Downloading ${chalk.green(bundleUrl)}...`);

    const bundle = await fetchBundle(config, bundleUrl);

    const yarnPath = path.resolve(config.lockfileFolder, `.yarn/releases/yarn-${bundleVersion}.js`);
    reporter.log(`Saving it into ${chalk.magenta(yarnPath)}...`);
    await fs.mkdirp(path.dirname(yarnPath));
    await fs.writeFile(yarnPath, bundle);
    await fs.chmod(yarnPath, 0o755);

    const targetPath = path.relative(config.lockfileFolder, yarnPath).replace(/\\/g, '/');

    if (isV2) {
      const rcPath = `${config.lockfileFolder}/.yarnrc.yml`;
      reporter.log(`Updating ${chalk.magenta(rcPath)}...`);

      await fs.writeFilePreservingEol(rcPath, `yarnPath: ${JSON.stringify(yarnPath)}\n`);
    } else {
      const rcPath = `${config.lockfileFolder}/.yarnrc`;
      reporter.log(`Updating ${chalk.magenta(rcPath)}...`);

      const rc = getRcConfigForFolder(config.lockfileFolder);
      rc['yarn-path'] = targetPath;

      await fs.writeFilePreservingEol(rcPath, `${stringify(rc)}\n`);
    }

    reporter.log(`Done!`);
  },
});

export {run, setFlags, examples};


// Fork of 2.5.0, deleted large unused files in dist directory.

Package.describe({
  name: 'coffeescript',
  summary: 'JavaScript dialect with fewer braces and semicolons',
  // This package version should track the version of the `coffeescript-compiler`
  // package, because people will likely only have this one added to their apps;
  // so bumping the version of this package will be how they get newer versions
  // of `coffeescript-compiler`. If you change this, make sure to also update
  // ../coffeescript-compiler/package.js to match.
  version: '2.3.1_2'
});

Package.registerBuildPlugin({
  name: 'compile-coffeescript',
  use: ['caching-compiler@1.1.12', 'ecmascript@0.11.1', 'coffeescript-compiler@2.3.1_2'],
  sources: ['compile-coffeescript.js'],
  npmDependencies: {
    // A breaking change was introduced in @babel/runtime@7.0.0-beta.56
    // with the removal of the @babel/runtime/helpers/builtin directory.
    // Since the compile-coffeescript plugin is bundled and published with
    // a specific version of babel-compiler and babel-runtime, it also
    // needs to have a reliable version of the @babel/runtime npm package,
    // rather than delegating to the one installed in the application's
    // node_modules directory, so the coffeescript package can work in
    // Meteor 1.7.1 apps as well as 1.7.0.x and earlier.
    '@babel/runtime': '7.0.0-rc.2'
  }
});

Package.onUse(function (api) {
  api.use('isobuild:compiler-plugin@1.0.0');

  // Because the CoffeeScript plugin now calls
  // BabelCompiler.prototype.processOneFileForTarget for any ES2015+
  // JavaScript or JavaScript enclosed by backticks, it must provide the
  // same runtime environment that the 'ecmascript' package provides.
  // The following api.imply calls should match those in ../../ecmascript/package.js,
  // except that coffeescript does not api.imply('modules').
  api.imply('ecmascript-runtime@0.7.0');
  api.imply('babel-runtime@1.2.4');
  api.imply('promise@0.11.1');
  api.imply('dynamic-import@0.4.1');
});

Package.onTest(function (api) {
  api.use(['coffeescript', 'tinytest', 'modern-browsers']);
  api.use(['coffeescript-test-helper', 'ecmascript'], ['client', 'server']); // Need ecmascript to compile tests/es2015_module.js
  api.addFiles('tests/bare_test_setup.coffee', ['client'], {bare: true});
  api.addFiles('tests/bare_tests.js', ['client']);
  api.addFiles([
    'tests/coffeescript_test_setup.js',
    'tests/coffeescript_tests.coffee',
    'tests/coffeescript_strict_tests.coffee',
    'tests/coffeescript_module.coffee',
    'tests/es2015_module.js',
    'tests/litcoffeescript_tests.litcoffee',
    'tests/litcoffeescript_tests.coffee.md',
    'tests/coffeescript_tests.js'
  ], ['client', 'server']);
  api.addFiles('tests/modern_browsers.coffee', ['server']);
});


import { RateLimiter } from 'meteor/rate-limit';

// Rate Limiter built into DDP with a default error message. See README or
// online documentation for more details.
const DDPRateLimiter = {};

let errorMessage = (rateLimitResult) => {
  return 'Error, too many requests. Please slow down. You must wait ' +
    `${Math.ceil(rateLimitResult.timeToReset / 1000)} seconds before ` +
    'trying again.';
};

const rateLimiter = new RateLimiter();

DDPRateLimiter.getErrorMessage = (rateLimitResult) => {
  if (typeof errorMessage === 'function') {
    return errorMessage(rateLimitResult);
  } else {
    return errorMessage;
  }
};

/**
 * @summary Set error message text when method or subscription rate limit
 * exceeded.
 * @param {string|function} message Functions are passed in an object with a
 * `timeToReset` field that specifies the number of milliseconds until the next
 * method or subscription is allowed to run. The function must return a string
 * of the error message.
 * @locus Server
 */
DDPRateLimiter.setErrorMessage = (message) => {
  errorMessage = message;
};

/**
 * @summary
 * Add a rule that matches against a stream of events describing method or
 * subscription attempts. Each event is an object with the following
 * properties:
 *
 * - `type`: Either "method" or "subscription"
 * - `name`: The name of the method or subscription being called
 * - `userId`: The user ID attempting the method or subscription
 * - `connectionId`: A string representing the user's DDP connection
 * - `clientAddress`: The IP address of the user
 *
 * Returns unique `ruleId` that can be passed to `removeRule`.
 *
 * @param {Object} matcher
 *   Matchers specify which events are counted towards a rate limit. A matcher
 *   is an object that has a subset of the same properties as the event objects
 *   described above. Each value in a matcher object is one of the following:
 *
 *   - a string: for the event to satisfy the matcher, this value must be equal
 *   to the value of the same property in the event object
 *
 *   - a function: for the event to satisfy the matcher, the function must
 *   evaluate to true when passed the value of the same property
 *   in the event object
 *
 * Here's how events are counted: Each event that satisfies the matcher's
 * filter is mapped to a bucket. Buckets are uniquely determined by the
 * event object's values for all properties present in both the matcher and
 * event objects.
 *
 * @param {number} numRequests  number of requests allowed per time interval.
 * Default = 10.
 * @param {number} timeInterval time interval in milliseconds after which
 * rule's counters are reset. Default = 1000.
 * @param {function} callback function to be called after a rule is executed.
 * @locus Server
 */
DDPRateLimiter.addRule = (matcher, numRequests, timeInterval, callback) =>
  rateLimiter.addRule(matcher, numRequests, timeInterval, callback);

DDPRateLimiter.printRules = () => rateLimiter.rules;

/**
 * @summary Removes the specified rule from the rate limiter. If rule had
 * hit a rate limit, that limit is removed as well.
 * @param  {string} id 'ruleId' returned from `addRule`
 * @return {boolean}    True if a rule was removed.
 * @locus Server
 */
DDPRateLimiter.removeRule = id => rateLimiter.removeRule(id);

// This is accessed inside livedata_server.js, but shouldn't be called by any
// user.
DDPRateLimiter._increment = (input) => {
  rateLimiter.increment(input);
};

DDPRateLimiter._check = input => rateLimiter.check(input);

export { DDPRateLimiter };


// White box tests of invalidation crossbar matching function.
// Note: the current crossbar match function is designed specifically
// to ensure that a modification that targets a specific ID does not
// notify a query that is watching a different specific ID. (And to
// keep separate collections separate.) Other than that, there's no
// deep meaning to the matching function, and it could be changed later
// as long as it preserves that property.
Tinytest.add('livedata - crossbar', function (test) {
  var crossbar = new DDPServer._Crossbar;
  test.isTrue(crossbar._matches({collection: "C"},
                                {collection: "C"}));
  test.isTrue(crossbar._matches({collection: "C", id: "X"},
                                {collection: "C"}));
  test.isTrue(crossbar._matches({collection: "C"},
                                {collection: "C", id: "X"}));
  test.isTrue(crossbar._matches({collection: "C", id: "X"},
                                {collection: "C"}));

  test.isFalse(crossbar._matches({collection: "C", id: "X"},
                                 {collection: "C", id: "Y"}));

  // Test that stopped listens definitely don't fire.
  var calledFirst = false;
  var calledSecond = false;
  var trigger = {collection: "C"};
  var secondHandle;
  crossbar.listen(trigger, function (notification) {
    // This test assumes that listeners will be called in the order
    // registered. It's not wrong for the crossbar to do something different,
    // but the test won't be valid in that case, so make it fail so we notice.
    calledFirst = true;
    if (calledSecond) {
      test.fail({
        type: "test_assumption_failed",
        message: "test assumed that listeners would be called in the order registered"
      });
    } else {
      secondHandle.stop();
    }
  });
  secondHandle = crossbar.listen(trigger, function (notification) {
    // This should not get invoked, because it should be stopped by the other
    // listener!
    calledSecond = true;
  });
  crossbar.fire(trigger);
  test.isTrue(calledFirst);
  test.isFalse(calledSecond);
});


Package.describe({
summary: 'CSS minifier',
version: '1.3.1'
});
Npm.depends({
postcss: '6.0.13',

Package.describe({
  name: 'standard-minifier-css',
  version: '1.4.1',
  summary: 'Standard css minifier used with Meteor apps by default.',
  documentation: 'README.md'
});

Package.registerBuildPlugin({
  name: "minifyStdCSS",
  use: [
    'minifier-css'
  ],
  npmDependencies: {
    "source-map": "0.5.6",
    "lru-cache": "4.0.1"
  },
  sources: [
    'plugin/minify-css.js'
  ]
});

Package.onUse(function(api) {
  api.use('isobuild:minifier-plugin@1.0.0');
});


var doc = document;
var head = doc.getElementsByTagName("head").item(0);

exports.addStyles = function (css) {
  var style = doc.createElement("style");

  style.setAttribute("type", "text/css");

  // https://msdn.microsoft.com/en-us/library/ms535871(v=vs.85).aspx
  var internetExplorerSheetObject =
    style.sheet || // Edge/IE11.
    style.styleSheet; // Older IEs.

  if (internetExplorerSheetObject) {
    internetExplorerSheetObject.cssText = css;
  } else {
    style.appendChild(doc.createTextNode(css));
  }

  return head.appendChild(style);
};


var fs = require("fs");
var files = require("../fs/mini-files.js");

exports.makeLink = function (target, linkPath) {
  target = files.convertToOSPath(target);
  linkPath = files.convertToOSPath(linkPath);

  var tempPath = linkPath + "-" + Math.random().toString(36).slice(2);

  try {
    fs.symlinkSync(target, tempPath, "junction");
  } catch (e) {
    fs.writeFileSync(tempPath, target, "utf8");
  }

  try {
    fs.renameSync(tempPath, linkPath);
  } catch (e) {
    // If renaming fails, try unlinking first.
    fs.unlinkSync(linkPath);
    fs.renameSync(tempPath, linkPath);
  }
};

// Note: this function returns an OS-specific path!
exports.readLink = function (linkPath) {
  linkPath = files.convertToOSPath(linkPath);

  var stat = fs.lstatSync(linkPath);
  if (stat.isSymbolicLink()) {
    linkPath = fs.realpathSync(linkPath);
  } else if (stat.isFile()) {
    linkPath = fs.readFileSync(linkPath, "utf8");
  }

  return linkPath;
};


// rather than delegating to the one installed in the application's

// node_modules directory, so the coffeescript package can work in

// Meteor 1.7.1 apps as well as 1.7.0.x and earlier.

keep keep keep replace keep keep keep keep keep


Package.describe({
  summary: "DEPRECATED - Use google-oauth instead - Google OAuth flow",
  version: "1.2.0"
});

Package.onUse(function(api) {
  api.use('google-oauth');
  api.use('google-config-ui', 'client');
  api.imply('google-oauth');

  api.addFiles('deprecation_notice.js');
});


export function check(obj) {
  return obj?.foo?.bar?.baz.qux;
}


// Import client startup through a single index entry point

import './routes.js';


summary: 'Standard css minifier used with Meteor apps by default.',

Package.describe({
  name: 'less',
  version: '2.8.0-rc171.8',
  summary: 'Leaner CSS language',
  documentation: 'README.md'
});

Package.registerBuildPlugin({
  name: "compileLessBatch",
  use: ['caching-compiler', 'ecmascript', 'underscore'],
  sources: [
    'plugin/compile-less.js'
  ],
  npmDependencies: {
    // Fork of 2.5.0, deleted large unused files in dist directory.
    "less": "https://github.com/meteor/less.js/tarball/8130849eb3d7f0ecf0ca8d0af7c4207b0442e3f6"
  }
});

Package.onUse(function (api) {
  api.use('isobuild:compiler-plugin@1.0.0');
});

Package.onTest(function(api) {
  api.use('less');
  api.use(['tinytest', 'test-helpers']);
  api.addFiles(['tests/top.import.less',
                'tests/top3.import.less',
                'tests/dir/in-dir.import.less',
                'tests/dir/in-dir2.import.less',
                'tests/dir/root.less',
                'tests/dir/subdir/in-subdir.import.less']);

  api.addFiles('tests/imports/not-included.less', 'client', {
    lazy: true
  });

  api.addFiles('tests/top2.less', 'client', {isImport: true});

  api.addFiles('less_tests.js', 'client');
});


import { Tinytest } from "./tinytest.js";
import {
  ServerTestResultsSubscription,
  ServerTestResultsCollection,
} from "./model.js";

const hasOwn = Object.prototype.hasOwnProperty;

export { Tinytest };

// Like Tinytest._runTests, but runs the tests on both the client and
// the server. Sets a 'server' flag on test results that came from the
// server.
//
// Options:
//   serial     if true, will not run tests in parallel.  Currently this means
//              running the server tests before running the client tests.
//              Default is currently true (serial operation), but we will likely
//              change this to false in future.
Tinytest._runTestsEverywhere = function (onReport, onComplete, pathPrefix, options) {
  var runId = Random.id();
  var localComplete = false;
  var localStarted = false;
  var remoteComplete = false;
  var done = false;

  options = {
    serial: true,
    ...options,
  };

  var serial = !!options.serial;

  var maybeDone = function () {
    if (!done && localComplete && remoteComplete) {
      done = true;
      onComplete && onComplete();
    }
    if (serial && remoteComplete && !localStarted) {
      startLocalTests();
    }
  };

  var startLocalTests = function() {
    localStarted = true;
    Tinytest._runTests(onReport, function () {
      localComplete = true;
      maybeDone();
    }, pathPrefix);
  };

  var handle;

  Meteor.connection.registerStore(ServerTestResultsCollection, {
    update(msg) {
      // We only should call _runTestsEverywhere once per client-page-load, so
      // we really only should see one runId here.
      if (msg.id !== runId) {
        return;
      }

      if (! msg.fields) {
        return;
      }

      // This will only work for added & changed messages.
      // hope that is all you get.
      Object.keys(msg.fields).forEach(key => {
        // Skip the 'complete' report (deal with it last)
        if (key === 'complete') {
          return;
        }
        const report = msg.fields[key];
        report.events.forEach(event => {
          delete event.cookie; // can't debug a server test on the client..
        });
        report.server = true;
        onReport(report);
      });

      // Now that we've processed all the other messages,
      // check if we have the 'complete' message
      if (hasOwn.call(msg.fields, 'complete')) {
        remoteComplete = true;
        handle.stop();
        Meteor.call('tinytest/clearResults', runId);
        maybeDone();
      }
    }
  });

  handle = Meteor.subscribe(ServerTestResultsSubscription, runId);

  Meteor.call('tinytest/run', runId, pathPrefix, function (error, result) {
    if (error) {
      // XXX better report error
      throw new Error("Test server returned an error");
    }
  });

  if (!serial) {
    startLocalTests();
  }
};


Package.describe({
  summary: 'JS simulation of MongoDB ObjectIDs',
  version: '1.0.7',
  documentation: null
});

Package.onUse(function (api) {
  api.export('MongoID');
  api.use(['ejson', 'id-map', 'random', 'ecmascript']);
  api.mainModule('id.js');
});


'minifier-css'
],
npmDependencies: {
"source-map": "0.5.6",
"lru-cache": "4.0.1"
},
sources: [
'plugin/minify-css.js'


# underscore
[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/underscore) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/underscore)
***

Underscore.js is a popular library that provides various utility functions to
simplify processing arrays, objects and functions.

To learn more about underscore, see
[the underscore project site](http://underscorejs.org/).


autoupdate@1.2.2-plugins.0
babel-compiler@5.8.3-plugins.0_5
babel-runtime@0.1.2
base64@1.0.4-plugins.0
binary-heap@1.0.4-plugins.0
blaze@2.1.3-plugins.0
blaze-tools@1.0.4-plugins.0
boilerplate-generator@1.0.4-plugins.0
caching-compiler@1.0.0-plugins.1
caching-html-compiler@1.0.0
callback-hook@1.0.4-plugins.0
check@1.0.6-plugins.0
ddp@1.2.0-plugins.0
ddp-client@1.2.0-plugins.0
ddp-common@1.2.0-plugins.0
ddp-server@1.2.0-plugins.1
deps@1.0.8-plugins.0
diff-sequence@1.0.0-plugins.0
ecmascript@0.1.3-plugins.1
ejson@1.0.7-plugins.0
geojson-utils@1.0.4-plugins.0
html-tools@1.0.5-plugins.0
htmljs@1.0.5-plugins.0
http@1.1.1-plugins.0
id-map@1.0.4-plugins.0
jquery@1.11.3-plugins.0_3
json@1.0.4-plugins.0
logging@1.0.8-plugins.0
meteor@1.1.7-plugins.1
minifiers@1.1.6-plugins.0
minimongo@1.0.9-plugins.0
mongo@1.1.1-plugins.0
mongo-id@1.0.0-plugins.0
npm-mongo@1.4.32-plugins.0_1
observe-sequence@1.0.7-plugins.0
ordered-dict@1.0.4-plugins.0
promise@0.4.2-plugins.0
random@1.0.4-plugins.0
reactive-var@1.0.6-plugins.0
reload@1.1.4-plugins.0
retry@1.0.4-plugins.0
routepolicy@1.0.6-plugins.0
spacebars@1.0.7-plugins.0
spacebars-compiler@1.0.7-plugins.1
static-html@1.0.12
templating@1.1.2-plugins.1
templating-tools@0.0.1
tracker@1.0.8-plugins.0
ui@1.0.7-plugins.0
underscore@1.0.4-plugins.0
url@1.0.5-plugins.0
webapp@1.2.1-plugins.0
webapp-hashing@1.0.4-plugins.0


npmDependencies: {
"@babel/runtime": "7.0.0"
},

Package.describe({
  name: 'standard-minifier-js',
  version: '2.4.0-rc171.8',
  summary: 'Standard javascript minifiers used with Meteor apps by default.',
  documentation: 'README.md',
});

Package.registerBuildPlugin({
  name: "minifyStdJS",
  use: [
    'minifier-js',
    'babel-compiler',
    'ecmascript'
  ],
  sources: [
    'plugin/minify-js.js',
    'plugin/stats.js',
    'plugin/utils.js',
  ],
});

Package.onUse(function(api) {
  api.use('isobuild:minifier-plugin@1.0.0');
});


p, .topnotes .nodespec { margin: 0.8em; }
.topnotes {
    background: #ffd;
    margin-bottom: 2em;
    margin-left: 1em;
    margin-right: 1em;
    border: 2px solid #ccc;
}

.str { font-weight: bold; }
.token { font-family: monospace; font-size: 110%; font-weight: bold; }
.token, .tokentype { background: #ddd; padding: 2px 5px; }
.tokentype { font-size: 85%; }
.ref { font-style: italic; }
.punc { font-size: 140%; }
.comma { color: #fff; }
.or { padding-left: 3px; padding-right: 3px; }

.nodespec {
  font-size: 16px;
  margin: 1em 0;
  line-height: 20px;
}

.indent {
  margin-left: 2em;
}

.explan {
  margin-left: 20em;
  margin-right: 2em;
  border: 1px solid #ccc;
}

#page { max-width: 50em; margin: 0 auto; }

.spacer { text-align: center; font-size:50%; font-weight: bold; border-top: 1px solid #999;
         margin-left: 6em; margin-right: 6em;
        }

code { font-weight: bold; }


# Meteor packages used by this project, one per line.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.

meteor-base
blaze-html-templates
local-plugin
local-plugin-2


description: A log of significant changes to the Meteor Guide.

- 2016/04/16: Switch order of Code Style and Application structure sections. [PR #381](https://github.com/meteor/guide/pull/383)

- 2016/04/16: Added "Writing Packages - Creating an npm package" and "Using Packages - Overriding packages - npm". [PR #381](https://github.com/meteor/guide/pull/381)

- 2016/04/07: Add more examples and details on application structure using imports. [PR #356](https://github.com/meteor/guide/pull/356)

- 2016/04/04: Add more content on writing and publishing Atmosphere packages. [PR #339](https://github.com/meteor/guide/pull/339)

- 2016/04/03: Add back in build tool default loading order rules. [PR #340](https://github.com/meteor/guide/pull/340)

- 2016/04/01: Added CoffeeScript exports syntax. [PR #328](https://github.com/meteor/guide/pull/328)

keep keep keep replace replace keep keep keep keep

- 2016/04/16: Switch order of Code Style and Application structure sections. [PR #383](https://github.com/meteor/guide/pull/383)

- 2016/04/16: Added [Writing Packages - Creating an npm package](https://guide.meteor.com/writing-packages.html#creating-npm) and [Using Packages - Overriding packages - npm](https://guide.meteor.com/using-packages.html#npm-overriding). [PR #381](https://github.com/meteor/guide/pull/381)

---
title: React
order: 23
description: How to use React, Facebook's frontend rendering library, with Meteor.
discourseTopicId: 20192
---

After reading this guide, you'll know:

1. What React is, and why you would consider using it with Meteor.
2. How to install React in your Meteor application, and how to use it correctly.
3. How to integrate React with Meteor's realtime data layer.
4. How to route in a React/Meteor application.

Introduction

[React](https://facebook.github.io/react/) is a JavaScript library for building reactive user interfaces developed and distributed by the Facebook team. React is one of the three rendering libraries supported by Meteor; it is an alterative to [Blaze](blaze.html) and [Angular](angular.html).

React has a vibrant and growing ecosystem and is used widely in production in a variety of combinations with different frameworks.

To learn more about using React in general and coming up to speed with the library, you should check out the [React documentation](https://facebook.github.io/react/docs/getting-started.html), especially the [thinking in React](https://facebook.github.io/react/docs/thinking-in-react.html) post, which explains the React philosophy well.

To get started with React in Meteor, you can follow along the [React tutorial](https://www.meteor.com/tutorials/react/creating-an-app). To see an example of a more complete Meteor application built with React, check out the [`react` branch](https://github.com/meteor/todos/tree/react) of the Todos exmaple application. Where applicable, code examples in this article will reference that app.

Installing and using React

To install React in Meteor 1.3 you should simply add it as an npm dependency:

```sh
npm install --save react react-dom
```

This will install `react` into your project and allow you to access it within your files with `import React from 'react'`. Most React code is written in [JSX](https://facebook.github.io/react/docs/jsx-in-depth.html), which you can use by [default in Meteor](http://guide.meteor.com/build-tool.html#react-jsx) if you include the `ecmascript` package (which is installed in all Meteor apps by default).

```js
import React from 'react';

export default class HelloWorld extends React.Component {
  render() {
    return (
      <h1>Hello World</h1>
    );
  }
}
```

You can render a component heirarchy to the DOM using the `react-dom` package:

```js
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import HelloWorld from './HelloWorld.jsx';

Meteor.startup(() => {
  render(<HelloWorld/>, document.getElementById('app'));
});
```

You need to include a `<div id="app"></div>` in your body's HTML somewhere of course.

Every new Meteor app includes Blaze, Meteor's default templating system, by default. If you are not planning on [using React and Blaze together](#using-with-blaze), you can remove Blaze from your project by running:

```sh
meteor remove blaze-html-templates
meteor add static-html
```

Using 3rd party packages

If you'd like to use a third party React component that has been published on npm (such as the ones you find on the [React Components site](http://react-components.com)), you can simple `npm install --save` them and `import` from within your app.

For example, to use the excellent [Griddle](http://griddlegriddle.github.io/Griddle/) React package, you could run

```sh
npm install --save griddle-react
```

Then, like with any other [npm package](using-packages.html#npm), you can import the component in your application:

```js
import React from 'react';
import Griddle from 'griddle-react';

export default class MyGriddler extends React.Component {
  render() {
    return (<Griddle ..../>);
  }
}
```

If you are looking to write an Atmosphere package that wraps such a component, you need to take some [further steps](#atmosphere-packages).

React Components in Blaze

If you'd like to use React within a larger app built with [Blaze](#blaze.html) (which is a good strategy if you'd like to incrementally migrate an app from Blaze to React), you can use the [`react-template-helper`](https://atmospherejs.com/meteor/react-template-helper) component which renders a react component inside a Blaze template. First run `meteor add react-template-helper`, then use the `React` helper in your template:

```html
<template name="userDisplay">
  <div>Hello, {{username}}</div>
  <div>{{> React component=UserAvatar userId=_id}}</div>
</template>
```

You will need to pass in the component class with a helper:

```js
import { Template } from 'meteor/templating';

import './userDisplay.html';
import UserAvatar from './UserAvatar.jsx';

Template.userDisplay.helpers({
  UserAvatar() {
    return UserAvatar;
  }
})
```

The `component` argument is the React component to include, which should be passed in with a helper.

Every other argument is passed as a prop to the component when it is rendered.

Note that there a few caveats:

- React components must be the only thing in the wrapper element. Due to a limitation of React (see facebook/react [#1970](https://github.com/facebook/react/issues/1970), [#2484](https://github.com/facebook/react/issues/2484)), a React component must be rendered as the only child of its parent node, meaning it cannot have any siblings.

- This means a React component also can't be the only thing in a Blaze template, because it's impossible to tell where the template will be used.

Passing callbacks to a React component

To pass a callback to a React component that you are including with this helper, simply make a [template helper that returns a function](http://guide.meteor.com/blaze.html#pass-callbacks), and pass it in as a prop, like so:

```js
Template.userDisplay.helpers({
  onClick() {
    const instance = Template.instance();

    // Return a function from this helper, where the template instance is in
    // a closure
    return () => {
      instance.hasBeenClicked.set(true)
    }
  }
});
```

To use it in Blaze:

```html
<template name="userDisplay">
  <div>
    {{> React component=UserAvatar userId=_id onClick=onClick}}
  </div>
</template>
```

Blaze Templates in React

Just like we can use React components in Blaze templates, we can also use Blaze templates in React components.  This is simularly useful for a gradual transition strategy; but more importantly, it allows us to continue to use the multitude of Atmosphere packages built for Blaze in our React projects, as well as core packages like `accounts-ui`.

One easy way to do this is with the [`gadicc:blaze-react-component`](https://atmospherejs.com/gadicc/blaze-react-component) package.  First run `meteor add gadicc:blaze-react-component`, then import and use it in your components as follows:

```js
import React from 'react';
import Blaze from 'meteor/gadicc:blaze-react-component';

const App = () => (
  <div>
    <Blaze template="itemsList" items={items} />
  </div>
);
```

The `<Blaze template="itemsList" items={items} />` line is the same as if you had written `{% raw %}{{> itemsList items=items}}{% endraw %}` inside of a Blaze template.  For other options and further information, see the package's [project page](https://github.com/gadicc/meteor-blaze-react-component).

Using Meteor's data system

React is a front-end rendering library and as such doesn't concern itself with how data gets into and out of components. On the other hand, Meteor has strong opinions about data! Meteor operates in terms of [publications](data-loading.html) and [methods](methods.html), used to subscribe to and modify the data in your application.

To integrate the two systems, we've developed a [`react-meteor-data`](https://atmospherejs.com/meteor/react-meteor-data) package which allows React components to respond to data changes via Meteor's [Tracker](https://www.meteor.com/tracker) reactivity system.

Using `createContainer`

Once you've run `meteor add react-meteor-data`, you'll be able to import the `createContainer` function, which allows you to create a [container component](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.by86emv9b) which provides data to your presentational components.

> Note that "container components" are analogous to the "smart components" and "presentational components" to the "reusable components" in the pattern we document in the [UI/UX article](http://guide.meteor.com/ui-ux.html#components), if you'd like to read more about how this philosophy relates to Meteor.

For example, in the Todos example app, we have a `ListPage` component, which renders list metadata and the tasks in the list. In order to do so, it needs to [subscribe](data-loading.html#subscriptions) to the `todos.inList` publication, check that subscription's readiness, then fetch the list of todos from the `Todos` collection.

It also needs to be responsive to reactive changes in the state of those actions (for instance if a todo changes due to the action of another user). All this data loading complexity is a typical use-case for a container-presentational component split, and the `createContainer()` function makes it simple to do this.

We simply define the `ListPage` component as a presentational component that expects its data to be passed in using React `props`:

```js
import React from 'react';

export default class ListPage extends React.Component {
  ...
}

ListPage.propTypes = {
  list: React.PropTypes.object,
  todos: React.PropTypes.array,
  loading: React.PropTypes.bool,
  listExists: React.PropTypes.bool,
};
```

Then we create a `ListContainer` container component which wraps it and provides a data source:

```js
import { Meteor } from 'meteor/meteor';
import { Lists } from '../../api/lists/lists.js';
import { createContainer } from 'meteor/react-meteor-data';
import ListPage from '../pages/ListPage.jsx';

export default createContainer(({ params }) => {
  const { id } = params;
  const todosHandle = Meteor.subscribe('todos.inList', id);
  const loading = !todosHandle.ready();
  const list = Lists.findOne(id);
  const listExists = !loading && !!list;
  return {
    loading,
    list,
    listExists,
    todos: listExists ? list.todos().fetch() : [],
  };
}, ListPage);
```

The container component created by `createContainer()` will reactively rerender the wrapped component in response to any changes to [reactive data sources](https://atmospherejs.com/meteor/tracker) accessed from inside the function provided to it.

Although this `ListContainer` container is intended to be instantiated by the React Router (which passes in the props automatically), if we did ever want to create one manually, we would need to pass in the props to the container component (which then get passed into our data function above):

```js
<ListContainer params={{id: '7'}}/>
```

Preventing re-renders

Sometimes changes in your data can trigger re-computations which you know won't affect your UI. Although React is in general quite efficient in the face of unnecessary re-renders, if you need to control re-rendering, the above pattern allows you to easily use React's [`shouldComponentUpdate`](https://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate) on the presentational component to avoid re-renders.

Routing

There are two main options for routing with Meteor and React. Either way, we recommend consulting our [Routing article](routing.html) for some general principles of routing in Meteor before writing your app.

- [`kadira:flow-router`](https://atmospherejs.com/kadira/flow-router) is a Meteor specific router that can be used both with React and Blaze. It is documented in detail in the [Routing article](routing.html).

- [`react-router`](https://www.npmjs.com/package/react-router) is a React-specific router very popular in the React community. It can also be used easily with Meteor.

Flow Router

Using Flow Router with React is very similar to using it with Blaze. The only difference is that in your route actions, you should use the [`react-mounter`](https://www.npmjs.com/package/react-mounter) package to mount components with a layout. Once you `npm install --save react-mounter`, you can do the following:

```js
import { FlowRouter } from 'meteor/kadira:flow-router';
import { mount } from 'react-mounter';

import AppContainer from '../../ui/containers/AppContainer.jsx';
import ListContainer from '../../ui/containers/ListContainer.jsx';

FlowRouter.route('/lists/:_id', {
  name: 'Lists.show',
  action() {
    mount(AppContainer, {
      main: <ListContainer/>,
    });
  },
});
```

Note that `react-mounter` automatically mounts the layout component on a `#react-root` node, which you can change by using the `withOptions()` function.

In this example, your `App` component would receive a `main` prop with a instantiated React Element to render:

```js
const App = (props) => (
  <div>
    <section id="menu"><..></section>
    {props.main}
  </div>
);

export default AppContainer = createContainer(props => {
  // props here will have `main`, passed from the router
  // anything we return from this function will be *added* to it
  return {
    user: Meteor.user(),
  };
}, App);
```

React Router

Using React Router is also straightforward. Once you `npm install --save react-router`, you can simply export a list of nested routes as you would in any other React Router driven React application:

```js
import React from 'react';
import { Router, Route, browserHistory } from 'react-router';

// route components
import AppContainer from '../../ui/containers/AppContainer.jsx';
import ListContainer from '../../ui/containers/ListContainer.jsx';
import AuthPageSignIn from '../../ui/pages/AuthPageSignIn.jsx';
import AuthPageJoin from '../../ui/pages/AuthPageJoin.jsx';
import NotFoundPage from '../../ui/pages/NotFoundPage.jsx';

export const renderRoutes = () => (
  <Router history={browserHistory}>
    <Route path="/" component={AppContainer}>
      <Route path="lists/:id" component={ListContainer}/>
      <Route path="signin" component={AuthPageSignIn}/>
      <Route path="join" component={AuthPageJoin}/>
      <Route path="*" component={NotFoundPage}/>
    </Route>
  </Router>
);
```

With React Router, you'll also need to explicity render the exported routes in a startup function:

```js
import { Meteor } from 'meteor/meteor';
import { render } from 'react-dom';
import { renderRoutes } from '../imports/startup/client/routes.jsx';

Meteor.startup(() => {
  render(renderRoutes(), document.getElementById('app'));
});
```

When using React Router in Meteor, you can follow roughly the [same principles](routing.html) as when using Flow Router, but you should also consider the idioms outlined in React Router's own  [documentation](https://github.com/reactjs/react-router/blob/latest/docs/Introduction.md).

These include some notable differences like:
 - React Router encourages you to couple your URL design and layout hierarchy in the route definition. Flow Router is more flexible, although it can involve much more boilerplate as a result.
 - React Router embraces React-specific functionality like the use of [context](https://facebook.github.io/react/docs/context.html), although you can also explicitly pass your FlowRouter instance around in context if you'd like (in fact this is probably the best thing to do).

Meteor and React

Using React in Atmosphere Packages

If you are writing an Atmosphere package and want to depend on React or an npm package that itself depends on React, you can't use `Npm.depends()` and `Npm.require()`, as this will result in *2* copies of React being installed into the application (and besides `Npm.require()` only works on the server).

Instead, you need to ask your users to install the correct npm packages in the application itself. This will ensure that only one copy of React is shipped to the client and there are no version conflicts.

In order to check that a user has installed the correct versions of npm packages, you can use the [`tmeasday:check-npm-versions`](https://atmospherejs.com/tmeasday/check-npm-versions) package to check dependency versions at runtime.