var fs = require('fs');
var path = require('path');
var _ = require('underscore');
var semver = require('semver');
var sourcemap = require('source-map');

var files = require('./files.js');
var utils = require('./utils.js');
var watch = require('./watch.js');
var buildmessage = require('./buildmessage.js');
var meteorNpm = require('./meteor-npm.js');
var Builder = require('./builder.js');
var archinfo = require('./archinfo.js');
var release = require('./release.js');
var catalog = require('./catalog.js');

// XXX: This is a medium-term hack, to avoid having the user set a package name
// & test-name in package.describe. We will change this in the new control file
// world in some way.
var AUTO_TEST_PREFIX = "local-test:";
var isTestName = function (name) {
  var nameStart = name.slice(0, AUTO_TEST_PREFIX.length);
  return nameStart === AUTO_TEST_PREFIX;
};
var genTestName = function (name) {
  return AUTO_TEST_PREFIX + name;
};

// Given a semver version string, return the earliest semver for which
// we are a replacement. This is used to compute the default
// earliestCompatibleVersion.
// XXX: move to utils?
var earliestCompatible = function (version) {
  // This is not the place to check to see if version parses as
  // semver. That should have been done when we first received it from
  // the user.
  var parsed = semver.parse(version);
  if (! parsed)
    throw new Error("not a valid version: " + version);
  return parsed.major + ".0.0";
};

// Returns a sort comparator to order files into load order.
// templateExtensions should be a list of extensions like 'html'
// which should be loaded before other extensions.
var loadOrderSort = function (templateExtensions) {
  var templateExtnames = {};
  _.each(templateExtensions, function (extension) {
    templateExtnames['.' + extension] = true;
  });

  return function (a, b) {
    // XXX MODERATELY SIZED HACK --
    // push template files ahead of everything else. this is
    // important because the user wants to be able to say
    //   Template.foo.events = { ... }
    // in a JS file and not have to worry about ordering it
    // before the corresponding .html file.
    //
    // maybe all of the templates should go in one file?
    var isTemplate_a = _.has(templateExtnames, path.extname(a));
    var isTemplate_b = _.has(templateExtnames, path.extname(b));
    if (isTemplate_a !== isTemplate_b) {
      return (isTemplate_a ? -1 : 1);
    }

    // main.* loaded last
    var ismain_a = (path.basename(a).indexOf('main.') === 0);
    var ismain_b = (path.basename(b).indexOf('main.') === 0);
    if (ismain_a !== ismain_b) {
      return (ismain_a ? 1 : -1);
    }

    // /lib/ loaded first
    var islib_a = (a.indexOf(path.sep + 'lib' + path.sep) !== -1 ||
                   a.indexOf('lib' + path.sep) === 0);
    var islib_b = (b.indexOf(path.sep + 'lib' + path.sep) !== -1 ||
                   b.indexOf('lib' + path.sep) === 0);
    if (islib_a !== islib_b) {
      return (islib_a ? -1 : 1);
    }

    // deeper paths loaded first.
    var len_a = a.split(path.sep).length;
    var len_b = b.split(path.sep).length;
    if (len_a !== len_b) {
      return (len_a < len_b ? 1 : -1);
    }

    // otherwise alphabetical
    return (a < b ? -1 : 1);
  };
};

// XXX We currently have a 1 to 1 mapping between 'where' and 'arch'.
// In the future, we may let people specify different 'where' and 'arch'.
var mapWhereToArch = function (where) {
  if (where === 'server') {
    return 'os';
  } else {
    // Transform client.* into web.*
    return 'web.' + where.split('.').slice(1).join('.');
  }
};

///////////////////////////////////////////////////////////////////////////////
// SourceArch
///////////////////////////////////////////////////////////////////////////////

// Options:
// - name [required]
// - arch [required]
// - uses
// - implies
// - getSourcesFunc
// - declaredExports
// - watchSet
//
// Do not include the source files in watchSet. They will be
// added at compile time when the sources are actually read.
var SourceArch = function (pkg, options) {
  var self = this;
  options = options || {};
  self.pkg = pkg;

  // Name for this sourceArchitecture. At the moment, there are really two
  // options -- main and plugin. We use these in linking
  self.archName = options.name;

  // The architecture (fully or partially qualified) that can use this
  // unibuild.
  self.arch = options.arch;

  // Packages used. The ordering is significant only for determining
  // import symbol priority (it doesn't affect load order), and a
  // given package could appear more than once in the list, so code
  // that consumes this value will need to guard appropriately. Each
  // element in the array has keys:
  // - package: the package name
  // - constraint: the constraint on the version of the package to use,
  //   as a string (may be null)
  // - unordered: If true, we don't want the package's imports and we
  //   don't want to force the package to load before us. We just want
  //   to ensure that it loads if we load.
  // - weak: If true, we don't *need* to load the other package, but
  //   if the other package ends up loaded in the target, it must
  //   be forced to load before us. We will not get its imports
  //   or plugins.
  // It is an error for both unordered and weak to be true, because
  // such a dependency would have no effect.
  //
  // In most places, instead of using 'uses' directly, you want to use
  // something like compiler.eachUsedUnibuild so you also take into
  // account implied packages.
  self.uses = options.uses || [];

  // Packages which are "implied" by using this package. If a unibuild X
  // uses this unibuild Y, and Y implies Z, then X will effectively use Z
  // as well (and get its imports and plugins).  An array of objects
  // of the same type as the elements of self.uses (although for now
  // unordered and weak are not allowed).
  self.implies = options.implies || [];

  // A function that returns the source files for this architecture. Array of
  // objects with keys "relPath" and "fileOptions". Null if loaded from
  // unipackage.
  //
  // fileOptions is optional and represents arbitrary options passed
  // to "api.add_files"; they are made available on to the plugin as
  // compileStep.fileOptions.
  //
  // This is a function rather than a literal array because for an
  // app, we need to know the file extensions registered by the
  // plugins in order to compute the sources list, so we have to wait
  // until build time (after we have loaded any plugins, including
  // local plugins in this package) to compute this.
  self.getSourcesFunc = options.getSourcesFunc || null;

  // Symbols that this architecture should export. List of symbols (as
  // strings).
  self.declaredExports = options.declaredExports || null;

  // Files and directories that we want to monitor for changes in
  // development mode, as a watch.WatchSet. In the latest refactoring
  // of the code, this does not include source files or directories,
  // but only control files such as package.js and .meteor/packages,
  // since the rest are not determined until compile time.
  self.watchSet = options.watchSet || new watch.WatchSet;

  // See the field of the same name in PackageSource.
  self.noSources = false;
};

///////////////////////////////////////////////////////////////////////////////
// PackageSource
///////////////////////////////////////////////////////////////////////////////

var PackageSource = function (catalog) {
  var self = this;

  // Which catalog this PackageSource works with.
  if (!catalog)
    throw Error("Must provide catalog");
  self.catalog = catalog;

  // The name of the package, or null for an app pseudo-package or
  // collection. The package's exports will reside in Package.<name>.
  // When it is null it is linked like an application instead of like
  // a package.
  self.name = null;

  // The path relative to which all source file paths are interpreted
  // in this package. Also used to compute the location of the
  // package's .npm directory (npm shrinkwrap state).
  self.sourceRoot = null;

  // Path that will be prepended to the URLs of all resources emitted
  // by this package (assuming they don't end up getting
  // concatenated). For non-web targets, the only effect this will
  // have is to change the actual on-disk paths of the files in the
  // bundle, for those that care to open up the bundle and look (but
  // it's still nice to get it right).
  self.serveRoot = null;

  // Package metadata. Keys are 'summary' and 'git'. Currently all of these are
  // optional.
  self.metadata = {};

  // Package version as a semver string. Optional; not all packages
  // (for example, the app) have versions.
  // XXX when we have names, maybe we want to say that all packages
  // with names have versions? certainly the reverse is true
  self.version = null;

  // The earliest version for which this package is supposed to be a
  // compatible replacement. Set if and only if version is set.
  self.earliestCompatibleVersion = null;

  // Available architectures of this package. Array of SourceArch.
  self.architectures = [];

  // The information necessary to build the plugins in this
  // package. Map from plugin name to object with keys 'name', 'use',
  // 'sources', and 'npmDependencies'.
  self.pluginInfo = {};

  // Analogous to watchSet in SourceArch but for plugins. At this
  // stage will typically contain just 'package.js'.
  self.pluginWatchSet = new watch.WatchSet;

  // npm packages used by this package (on os.* architectures only).
  // Map from npm package name to the required version of the package
  // as a string.
  self.npmDependencies = {};

  // Absolute path to a directory on disk that serves as a cache for
  // the npm dependencies, so we don't have to fetch them on every
  // build. Required not just if we have npmDependencies, but if we
  // ever could have had them in the past.
  self.npmCacheDirectory = null;

  // Dependency versions that we used last time that we built this package. If
  // the constraint solver thinks that they are still a valid set of
  // dependencies, we will use them again to build this package. This makes
  // building packages slightly more efficient and ensures repeatable builds.
  self.dependencyVersions = {dependencies: {}, pluginDependencies: {}};

  // If this package has a corresponding test package (for example,
  // underscore-test), defined in the same package.js file, store its value
  // here.
  self.testName = null;

  // Test packages are dealt with differently in the linker (and not published
  // to the catalog), so we need to keep track of them.
  self.isTest = false;

  // If this is set, we will take the currently running git checkout and bundle
  // the meteor tool from it inside this package as a tool. We will include
  // built unipackages for all the packages in uniload.ROOT_PACKAGES as well as
  // their transitive (strong) dependencies.
  self.includeTool = false;

  // If this is true, then this package has no source files. (But the converse
  // is not true: this is only set to true by one particular constructor.) This
  // is specifically so that a few pieces of code can detect the wrapper "load"
  // package that uniload uses and not do extra work that doesn't make sense in
  // the uniload context.
  self.noSources = false;

  // If this is true, the package source comes from the package server, and
  // should be treated as immutable. The only reason that we have it is so we
  // can build it, and we should expect to use exactly the same inputs
  // (package.js and version lock file) as we did when it was created. If we
  // ever need to modify it, we should throw instead.
  self.immutable = false;

  // Is this a core package? Core packages don't record version files, because
  // core packages are only ever run from checkout. For the preview release,
  // core packages do not need to specify their versions at publication (since
  // there isn't likely to be any exciting version skew yet), but we will
  // specify the correct restrictions at 0.90.
  // XXX: 0.90 package versions.
  self.isCore = false;

  // Alternatively, we can also specify noVersionFile directly. Useful for not
  // recording version files for js images of plugins, since those go into the
  // overall package versions file (if one exists). In the future, we can make
  // this option transparent to the user in package.js.
  self.noVersionFile = false;

  // The list of where that we can target. Doesn't include 'client' because
  // it is expanded into 'client.*'.
  self.allWheres = ['server', 'client.browser', 'client.cordova'];
};

_.extend(PackageSource.prototype, {
  // Make a dummy (empty) packageSource that contains nothing of interest.
  // XXX: Do we need this
  initEmpty: function (name) {
    var self = this;
    self.name = name;
  },

  // Programmatically initialize a PackageSource from scratch.
  //
  // Unlike user-facing methods of creating a package
  // (initFromPackageDir, initFromAppDir) this does not implicitly add
  // a dependency on the 'meteor' package. If you want such a
  // dependency then you must add it yourself.
  //
  // If called inside a buildmessage job, it will keep going if things
  // go wrong. Be sure to call jobHasMessages to see if it actually
  // succeeded.
  //
  // The architecture is hardcoded to be "os".
  //
  // Note that this does not set a version on the package!
  //
  // Options:
  // - sourceRoot (required if sources present)
  // - serveRoot (required if sources present)
  // - use
  // - sources (array of paths or relPath/fileOptions objects)
  // - npmDependencies
  // - npmDir
  // - dependencyVersions
  initFromOptions: function (name, options) {
    var self = this;
    self.name = name;

    if (options.sources && ! _.isEmpty(options.sources) &&
        (! options.sourceRoot || ! options.serveRoot))
      throw new Error("When source files are given, sourceRoot and " +
                      "serveRoot must be specified");
    self.sourceRoot = options.sourceRoot || path.sep;
    self.serveRoot = options.serveRoot || path.sep;

    var nodeModulesPath = null;
    meteorNpm.ensureOnlyExactVersions(options.npmDependencies);
    self.npmDependencies = options.npmDependencies;
    self.npmCacheDirectory = options.npmDir;

    var sources = _.map(options.sources, function (source) {
      if (typeof source === "string")
        return {relPath: source};
      return source;
    });

    var sourceArch = new SourceArch(self, {
      name: options.archName,
      arch: "os",
      uses: _.map(options.use, utils.splitConstraint),
      getSourcesFunc: function () { return sources; },
      nodeModulesPath: nodeModulesPath
    });

    if (!sources.length) {
      self.noSources = true;
      sourceArch.noSources = true;
    }

    self.architectures.push(sourceArch);

    if (! self._checkCrossUnibuildVersionConstraints())
      throw new Error("only one unibuild, so how can consistency check fail?");

    self.dependencyVersions = options.dependencyVersions ||
        {dependencies: {}, pluginDependencies: {}};

    self.noVersionFile = options.noVersionFile;
  },

  // Initialize a PackageSource from a package.js-style package directory. Uses
  // the name field provided and the name/test fields in the package.js file to
  // figre out if this is a test package (load from on_test) or a use package
  // (load from on_use).
  //
  // name: name of the package.
  // dir: location of directory on disk.
  // options:
  //   -requireVersion: This is a package that is going in a catalog or being
  //    published to the server. It must have a version. (as opposed to, for
  //    example, a program)
  //   -defaultVersion: The default version if none is specified. Only assigned
  //    if the version is required.
  //   -immutable: This package source is immutable. Do not write anything,
  //    including version files. Instead, its only purpose is to be used as
  //    guideline for a repeatable build.
  initFromPackageDir: function (name, dir, options) {
    var self = this;
    buildmessage.assertInCapture();
    var isPortable = true;
    options = options || {};

    self.name = name;
    self.sourceRoot = dir;
    self.serveRoot = path.join(path.sep, 'packages', name);
    self.isTest = isTestName(name);

    // If we are running from checkout we may be looking at a core package. If
    // we are, let's remember this for things like not recording version files.
    if (files.inCheckout()) {
      var packDir = path.join(files.getCurrentToolsDir(), 'packages');
      if (path.dirname(self.sourceRoot) === packDir) {
        self.isCore = true;
      }
    }

    if (!utils.validPackageName(self.name)) {
      buildmessage.error("Package name invalid: " + self.name);
      return;
    }

    if (! fs.existsSync(self.sourceRoot))
      throw new Error("putative package directory " + dir + " doesn't exist?");

    var fileAndDepLoader = null;
    var npmDependencies = null;

    var packageJsPath = path.join(self.sourceRoot, 'package.js');
    var code = fs.readFileSync(packageJsPath);
    var packageJsHash = Builder.sha1(code);

    var releaseRecord = null;

    // Any package that depends on us needs to be rebuilt if our package.js file
    // changes, because a change to package.js might add or remove a plugin,
    // which could change a file from being handled by extension vs treated as
    // an asset.
    self.pluginWatchSet.addFile(packageJsPath, packageJsHash);

    // == 'Package' object visible in package.js ==
    var Package = {
      // Set package metadata. Options:
      // - summary: for 'meteor list' & package server
      // - version: package version string (semver)
      // - earliestCompatibleVersion: version string
      // There used to be a third option documented here,
      // 'environments', but it was never implemented and no package
      // ever used it.
      describe: function (options) {
        _.each(options, function (value, key) {
          if (key === "summary" ||
              key === "git") {
            self.metadata[key] = value;
          } else if (key === "version") {
            // XXX validate that version parses -- and that it doesn't
            // contain a +!
            self.version = value;
          } else if (key === "earliestCompatibleVersion") {
            self.earliestCompatibleVersion = value;
          } else if (key === "name") {
            // this key is special because we really don't want you to think that
            // you are successfully overriding directory name right now. Someday, you
            // may be though, so it is reserved.
            buildmessage.error("reserved key " + key + " in package description.");
          }
          else {
          // Do nothing. We might want to add some keys later, and we should err on
          // the side of backwards compatibility.
          }
        });
      },

      onUse: function (f) {
        if (!self.isTest) {
          if (fileAndDepLoader) {
            buildmessage.error("duplicate onUse handler; a package may have " +
                               "only one", { useMyCaller: true });
            // Recover by ignoring the duplicate
            return;
          }
          fileAndDepLoader = f;
        }
      },

      // Backwards compatibility for old interfaces.
      on_use: function (f) {
        this.onUse(f);
      },

      onTest: function (f) {
        // If we are not initializing the test package, then we are initializing
        // the normal package and have now noticed that it has tests. So, let's
        // register the test. This is a medium-length hack until we have new
        // control files.
        if (!self.isTest) {
          self.testName = genTestName(self.name);
          return;
        }

        // We are initializing the test, so proceed as normal.
        if (self.isTest) {
          if (fileAndDepLoader) {
            buildmessage.error("duplicate onTest handler; a package may have " +
                               "only one", { useMyCaller: true });
            // Recover by ignoring the duplicate
            return;
          }
          fileAndDepLoader = f;
        }
      },

      // Backwards compatibility for old interfaces.
      on_test: function (f) {
        this.onTest(f);
      },

      // Define a plugin. A plugin extends the build process for
      // targets that use this package. For example, a Coffeescript
      // compiler would be a plugin. A plugin is its own little
      // program, with its own set of source files, used packages, and
      // npm dependencies.
      //
      // This is an experimental API and for now you should assume
      // that it will change frequently and radically (thus the
      // '_transitional_'). For maximum R&D velocity and for the good
      // of the platform, we will push changes that break your
      // packages that use this API. You've been warned.
      //
      // Options:
      // - name: a name for this plugin. required (cosmetic -- string)
      // - use: package to use for the plugin (names, as strings)
      // - sources: sources for the plugin (array of string)
      // - npmDependencies: map from npm package name to required
      //   version (string)
      _transitional_registerBuildPlugin: function (options) {
        // Tests don't have plugins; plugins initialized in the control file
        // belong to the package and not to the test. (This will be less
        // confusing in the new control file format).
        if (self.isTest) {
          return;
        }

        if (! ('name' in options)) {
          buildmessage.error("build plugins require a name",
                             { useMyCaller: true });
          // recover by ignoring plugin
          return;
        }

        if (options.name in self.pluginInfo) {
          buildmessage.error("this package already has a plugin named '" +
                             options.name + "'",
                             { useMyCaller: true });
          // recover by ignoring plugin
          return;
        }

        if (options.name.match(/\.\./) || options.name.match(/[\\\/]/)) {
          buildmessage.error("bad plugin name", { useMyCaller: true });
          // recover by ignoring plugin
          return;
        }

        // XXX probably want further type checking
        self.pluginInfo[options.name] = options;
      },

      includeTool: function () {
        if (!files.inCheckout()) {
          buildmessage.error("Package.includeTool() can only be used with a " +
                             "checkout of meteor");
        } else if (self.includeTool) {
          buildmessage.error("Duplicate includeTool call");
        } else {
          self.includeTool = true;
        }
      }
    };

    // == 'Npm' object visible in package.js ==
    var Npm = {
      depends: function (_npmDependencies) {
        // XXX make npmDependencies be separate between use and test, so that
        // production doesn't have to ship all of the npm modules used by test
        // code
        if (npmDependencies) {
          buildmessage.error("Npm.depends may only be called once per package",
                             { useMyCaller: true });
          // recover by ignoring the Npm.depends line
          return;
        }
        if (typeof _npmDependencies !== 'object') {
          buildmessage.error("the argument to Npm.depends should be an " +
                             "object, like this: {gcd: '0.0.0'}",
                             { useMyCaller: true });
          // recover by ignoring the Npm.depends line
          return;
        }

        // don't allow npm fuzzy versions so that there is complete
        // consistency when deploying a meteor app
        //
        // XXX use something like seal or lockdown to have *complete*
        // confidence we're running the same code?
        try {
          meteorNpm.ensureOnlyExactVersions(_npmDependencies);
        } catch (e) {
          buildmessage.error(e.message, { useMyCaller: true, downcase: true });
          // recover by ignoring the Npm.depends line
          return;
        }

        npmDependencies = _npmDependencies;
      },

      require: function (name) {
        var nodeModuleDir = path.join(self.sourceRoot,
                                      '.npm', 'package', 'node_modules', name);
        if (fs.existsSync(nodeModuleDir)) {
          return require(nodeModuleDir);
        } else {
          try {
            return require(name); // from the dev bundle
          } catch (e) {
            buildmessage.error("can't find npm module '" + name +
                               "'. Did you forget to call 'Npm.depends'?",
                               { useMyCaller: true });
            // recover by, uh, returning undefined, which is likely to
            // have some knock-on effects
            return undefined;
          }
        }
      }
    };

    try {
      files.runJavaScript(code.toString('utf8'), {
        filename: 'package.js',
        symbols: { Package: Package, Npm: Npm }
      });
    } catch (e) {
      buildmessage.exception(e);

      // Could be a syntax error or an exception. Recover by
      // continuing as if package.js is empty. (Pressing on with
      // whatever handlers were registered before the exception turns
      // out to feel pretty disconcerting -- definitely violates the
      // principle of least surprise.) Leave the metadata if we have
      // it, though.
      fileAndDepLoader = null;
      self.pluginInfo = {};
      npmDependencies = null;
    }

    if (self.version === null && options.requireVersion) {
      if (options.defaultVersion) {
        self.version = options.defaultVersion;
      } else if (! buildmessage.jobHasMessages()) {
        // Only write the error if there have been no errors so
        // far. (Otherwise if there is a parse error we'll always get
        // this message, because we won't have been able to run any
        // code.)
        buildmessage.error("A version must be specified for the package. " +
                           "Set it with Package.describe.");
      }
      // Recover by leaving the version unset. This is sort of
      // unfortunate (it means that whereever we work with Package
      // objects, we need to consider the possibility that their
      // version is not set) but short of failing the build we have no
      // alternative. Using a dummy version like "1.0.0" would cause
      // endless confusion and a fake version like "unknown" wouldn't
      // parse as semver. Anyway, apps don't have versions, so it's
      // not like we didn't already have to think about this case.
    }

    if (self.version !== null && typeof(self.version) !== "string") {
      if (!buildmessage.jobHasMessages()) {
        buildmessage.error("The package version (specified with "
                           + "Package.describe) must be a string.");
      }
      // Recover by pretending there was no version (see above).
      self.version = null;
    }

    if (self.version !== null) {
      var parsedVersion = semver.parse(self.version);
      if (!parsedVersion) {
        if (!buildmessage.jobHasMessages()) {
          buildmessage.error(
            "The package version (specified with Package.describe) must be "
              + "valid semver (see http://semver.org/).");
        }
        // Recover by pretending there was no version (see above).
        self.version = null;
      } else if (parsedVersion.build.length) {
        if (!buildmessage.jobHasMessages()) {
          buildmessage.error(
            "The package version (specified with Package.describe) may not "
            + "contain a plus-separated build ID.");
        }
        // Recover by pretending there was no version (see above).
        self.version = null;
      }
    }

    if (self.version !== null && ! self.earliestCompatibleVersion) {
      self.earliestCompatibleVersion =
        earliestCompatible(self.version);
    }

    // source files used
    var sources = {};

    // symbols exported
    var exports = {};

    // packages used and implied (keys are 'package', 'unordered', and
    // 'weak').  an "implied" package is a package that will be used by a unibuild
    // which uses us.
    var uses = {};
    var implies = {};

    _.each(self.allWheres, function (where) {
      sources[where] = [];
      exports[where] = [];
      uses[where] = [];
      implies[where] = [];
    });

    // Iterates over the list of target archs and calls f(arch) for all archs
    // that match an element of 'wheres'.
    var forAllMatchingWheres = function (wheres, f) {
      _.each(wheres, function (where) {
        _.each(self.allWheres, function (matchWhere) {
          if (archinfo.matches(matchWhere, where)) {
            f(matchWhere);
          }
        });
      });
    };

    // For this old-style, on_use/on_test/where-based package, figure
    // out its dependencies by calling its on_xxx functions and seeing
    // what it does.
    //
    // We have a simple strategy. Call its on_xxx handler with no
    // 'where', which is what happens when the package is added
    // directly to an app, and see what files it adds to the client
    // and the server. When a package is used, include it in both the client
    // and the server by default. This simple strategy doesn't capture even
    // 10% of the complexity possible with on_use, on_test, and where, but
    // probably is sufficient for virtually all packages that actually
    // exist in the field, if not every single one. #OldStylePackageSupport

    if (fileAndDepLoader) {
      var toArray = function (x) {
        if (x instanceof Array)
          return x;
        return x ? [x] : [];
      };

      var toWhereArray = function (where) {
        if (!(where instanceof Array)) {
          where = where ? [where] : self.allWheres;
        }
        where = _.uniq(where);
        _.each(where, function (inputWhere) {
          var isMatch = _.any(_.map(self.allWheres, function (actualWhere) {
            return archinfo.matches(actualWhere, inputWhere);
          }));
          if (! isMatch) {
            buildmessage.error(
              "Invalid 'where' argument: '" + inputWhere + "'",
              // skip toWhereArray in addition to the actual API function
              {useMyCaller: 2});
          }
        });
        return where;
      };

      var api = {
        // Called when this package wants to make another package be
        // used. Can also take literal package objects, if you have
        // anonymous packages you want to use (eg, app packages)
        //
        // @param where 'web', 'web.browser', 'web.cordova', 'server',
        // or an array of those.
        // The default is ['web', 'server'].
        //
        // options can include:
        //
        // - unordered: if true, don't require this package to load
        //   before us -- just require it to be loaded anytime. Also
        //   don't bring this package's imports into our
        //   namespace. If false, override a true value specified in
        //   a previous call to use for this package name. (A
        //   limitation of the current implementation is that this
        //   flag is not tracked per-environment or per-role.)  This
        //   option can be used to resolve circular dependencies in
        //   exceptional circumstances, eg, the 'meteor' package
        //   depends on 'handlebars', but all packages (including
        //   'handlebars') have an implicit dependency on
        //   'meteor'. Internal use only -- future support of this
        //   is not guaranteed. #UnorderedPackageReferences
        //
        // - weak: if true, don't require this package to load at all, but if
        //   it's going to load, load it before us.  Don't bring this
        //   package's imports into our namespace and don't allow us to use
        //   its plugins. (Has the same limitation as "unordered" that this
        //   flag is not tracked per-environment or per-role; this may
        //   change.)
        use: function (names, where, options) {
          // Support `api.use(package, {weak: true})` without where.
          if (_.isObject(where) && !_.isArray(where) && !options) {
            options = where;
            where = null;
          }
          options = options || {};

          names = toArray(names);
          where = toWhereArray(where);

          // A normal dependency creates an ordering constraint and a "if I'm
          // used, use that" constraint. Unordered dependencies lack the
          // former; weak dependencies lack the latter. There's no point to a
          // dependency that lacks both!
          if (options.unordered && options.weak) {
            buildmessage.error(
              "A dependency may not be both unordered and weak.",
              { useMyCaller: true });
            // recover by ignoring
            return;
          }

          _.each(names, function (name) {
            forAllMatchingWheres(where, function (w) {
              uses[w].push(_.extend(utils.splitConstraint(name), {
                unordered: options.unordered || false,
                weak: options.weak || false
              }));
            });
          });
        },

        // Called when this package wants packages using it to also use
        // another package.  eg, for umbrella packages which want packages
        // using them to also get symbols or plugins from their components.
        imply: function (names, where) {
          names = toArray(names);
          where = toWhereArray(where);

          _.each(names, function (name) {
            forAllMatchingWheres(where, function (w) {
              // We don't allow weak or unordered implies, since the main
              // purpose of imply is to provide imports and plugins.
              implies[w].push(utils.splitConstraint(name));
            });
          });
        },

        // Top-level call to add a source file to a package. It will
        // be processed according to its extension (eg, *.coffee
        // files will be compiled to JavaScript).
        addFiles: function (paths, where, fileOptions) {
          paths = toArray(paths);
          where = toWhereArray(where);

          _.each(paths, function (path) {
            forAllMatchingWheres(where, function (w) {
              var source = {relPath: path};
              if (fileOptions)
                source.fileOptions = fileOptions;
              sources[w].push(source);
            });
          });
        },

        // Use this release to resolve unclear dependencies for this package. If
        // you don't fill in dependencies for some of your implies/uses, we will
        // look at the packages listed in the release to figure that out.
        versionsFrom: function (release) {
          if (releaseRecord) {
            buildmessage.error("api.versionsFrom may only be specified once.",
                               { useMyCaller: true });
            // recover by ignoring
            return;
          }

          // If you don't specify a track, use our default.
          if (release.indexOf('@') === -1) {
            release = catalog.DEFAULT_TRACK + "@" + release;
          }

          var relInf = release.split('@');
          // XXX: Error handling
          if (relInf.length !== 2)
            throw new Error("Incorrect release spec");
          // XXX: We pass in true to override the fact that we know that teh
          // catalog may not be initialized, but we are pretty sure that the
          // releases are there anyway. This is not the right way to do this
          // long term.
          releaseRecord = catalog.official.getReleaseVersion(
            relInf[0], relInf[1], true);
          if (!releaseRecord) {
            buildmessage.error("Unknown release "+ release);
           }
        },

        // Export symbols from this package.
        //
        // @param symbols String (eg "Foo") or array of String
        // @param where 'web', 'server', 'web.browser', 'web.cordova'
        // or an array of those.
        // The default is ['web', 'server'].
        // @param options 'testOnly', boolean.
        export: function (symbols, where, options) {
          // Support `api.export("FooTest", {testOnly: true})` without
          // where.
          if (_.isObject(where) && !_.isArray(where) && !options) {
            options = where;
            where = null;
          }
          options = options || {};

          symbols = toArray(symbols);
          where = toWhereArray(where);

          _.each(symbols, function (symbol) {
            // XXX be unicode-friendlier
            if (!symbol.match(/^([_$a-zA-Z][_$a-zA-Z0-9]*)$/)) {
              buildmessage.error("Bad exported symbol: " + symbol,
                                 { useMyCaller: true });
              // recover by ignoring
              return;
            }
            forAllMatchingWheres(where, function (w) {
              exports[w].push({name: symbol, testOnly: !!options.testOnly});
            });
          });
        }
      };

      // XXX COMPAT WITH 0.8.x
      api.add_files = api.addFiles;

      try {
        fileAndDepLoader(api);
      } catch (e) {
        buildmessage.exception(e);
        // Recover by ignoring all of the source files in the
        // packages and any remaining handlers. It violates the
        // principle of least surprise to half-run a handler
        // and then continue.
        sources = {};
        _.each(self.allWheres, function (where) {
          sources[where] = [];
        });

        fileAndDepLoader = null;
        self.pluginInfo = {};
        npmDependencies = null;
      }
    }

    // If we have specified a release, then we should go through the
    // dependencies and fill in the unspecified constraints with the versions in
    // the release (if possible).
    if (releaseRecord) {
      var packages = releaseRecord.packages;

      // Given a dependency object with keys package (the name of the package)
      // and constraint (the version constraint), if the constraint is null,
      // look in the packages field in the release record and fill in from
      // there.
      var setFromRel = function (dep) {
        if (! dep.constraint && _.has(packages, dep.package)) {
          dep.constraint = packages[dep.package];
        };
        return dep;
      };

      // For all implies and uses, fill in the unspecified dependencies from the
      // release.
      _.each(self.allWheres, function (label) {
        uses[label] = _.map(uses[label], setFromRel);
        implies[label] = _.map(implies[label], setFromRel);
      });
     };

    // Make sure that if a dependency was specified in multiple
    // unibuilds, the constraint is exactly the same.
    if (! self._checkCrossUnibuildVersionConstraints()) {
      // A build error was written. Recover by ignoring the
      // fact that we have differing constraints.
    }

    // Save information about npm dependencies. To keep metadata
    // loading inexpensive, we won't actually fetch them until build
    // time.

    // We used to put the cache directly in .npm, but in linker-land,
    // the package's own NPM dependencies go in .npm/package and build
    // plugin X's goes in .npm/plugin/X. Notably, the former is NOT an
    // ancestor of the latter, so that a build plugin does NOT see the
    // package's node_modules.  XXX maybe there should be separate NPM
    // dirs for use vs test?
    self.npmCacheDirectory =
      path.resolve(path.join(self.sourceRoot, '.npm', 'package'));
    self.npmDependencies = npmDependencies;

    // If this package was previously built with pre-linker versions,
    // it may have files directly inside `.npm` instead of nested
    // inside `.npm/package`. Clean them up if they are there. (Kind
    // of grody to do this here but it'll be fine for now, especially
    // since this is only for compatibility with very old versions of
    // Meteor.)
    var preLinkerFiles = [
      'npm-shrinkwrap.json', 'README', '.gitignore', 'node_modules'];
    _.each(preLinkerFiles, function (f) {
      files.rm_recursive(path.join(self.sourceRoot, '.npm', f));
    });

    // Create source architectures, one for the server and one for each web
    // arch.
    _.each(self.allWheres, function (where) {
      var arch = mapWhereToArch(where);

      // Everything depends on the package 'meteor', which sets up
      // the basic environment) (except 'meteor' itself, and js-analyze
      // which needs to be loaded by the linker).
      // XXX add a better API for js-analyze to declare itself here
      if (name !== "meteor" && name !== "js-analyze" &&
          !process.env.NO_METEOR_PACKAGE) {
        // Don't add the dependency if one already exists. This allows the
        // package to create an unordered dependency and override the one that
        // we'd add here. This is necessary to resolve the circular dependency
        // between meteor and underscore (underscore has an unordered
        // dependency on meteor dating from when the .js extension handler was
        // in the "meteor" package).
        var alreadyDependsOnMeteor =
              !! _.find(uses[where], function (u) {
                return u.package === "meteor";
              });
        if (! alreadyDependsOnMeteor)
          uses[where].unshift({ package: "meteor" });
      }

      // Each unibuild has its own separate WatchSet. This is so that, eg, a test
      // unibuild's dependencies doesn't end up getting merged into the
      // pluginWatchSet of a package that uses it: only the use unibuild's
      // dependencies need to go there!
      var watchSet = new watch.WatchSet();
      watchSet.addFile(packageJsPath, packageJsHash);

      self.architectures.push(new SourceArch(self, {
        name: "main",
        arch: arch,
        uses: uses[where],
        implies: implies[where],
        getSourcesFunc: function () { return sources[where]; },
        declaredExports: exports[where],
        watchSet: watchSet
      }));
    });

    // If we have built this before, read the versions that we ended up using.
    var versionsFile = self.versionsFilePath();
    if (versionsFile && fs.existsSync(versionsFile)) {
      try {
        var data = fs.readFileSync(versionsFile, 'utf8');
        var dependencyData = JSON.parse(data);
        self.dependencyVersions = {
          "pluginDependencies": _.object(dependencyData.pluginDependencies),
          "dependencies": _.object(dependencyData.dependencies),
          "toolVersion": dependencyData.toolVersion
          };
      } catch (err) {
        // We 'recover' by not reading the dependency versions. Log a line about
        // it in case it is unexpected. We don't buildmessage because it doesn't
        // really interrupt our workflow, but the user might want to know about
        // it anyway. We shouldn't get here unless, for example, the user tried
        // to manually edit the json file incorrectly, or there is some bizarre
        // ondisk corruption.
        console.log("Could not read versions file for " + self.name +
                    ". Recomputing dependency versions from scratch.");
      }
    };

    // If immutable is set, then we should make a note to never mutate this
    // packageSource. We should never change its dependency versions, for
    // example.
    if (options.immutable) {
      self.immutable = true;
    };

  },

  // Initialize a package from an application directory (has .meteor/packages).
  initFromAppDir: function (appDir, ignoreFiles) {
    var self = this;
    appDir = path.resolve(appDir);
    self.name = null;
    self.sourceRoot = appDir;
    self.serveRoot = path.sep;

    _.each(self.allWheres, function (where) {
      // Determine used packages
      var project = require('./project.js').project;
      var names = project.getConstraints();
      var arch = mapWhereToArch(where);
      // XXX what about /client.browser/* etc, these directories could also
      // be for specific client targets.

      // Create unibuild
      var sourceArch = new SourceArch(self, {
        name: where,
        arch: arch,
        uses: _.map(names, utils.dealConstraint)
      });
      self.architectures.push(sourceArch);

      // Watch control files for changes
      // XXX this read has a race with the actual reads that are used
      _.each([path.join(appDir, '.meteor', 'packages'),
              path.join(appDir, '.meteor', 'release')], function (p) {
                watch.readAndWatchFile(sourceArch.watchSet, p);
              });

      // Determine source files
      sourceArch.getSourcesFunc = function (extensions, watchSet) {
        var sourceInclude = _.map(
          extensions,
          function (isTemplate, ext) {
            return new RegExp('\\.' + utils.quotemeta(ext) + '$');
          }
        );
        var sourceExclude = [/^\./].concat(ignoreFiles);

        // Wrapper around watch.readAndWatchDirectory which takes in and returns
        // sourceRoot-relative directories.
        var readAndWatchDirectory = function (relDir, filters) {
          filters = filters || {};
          var absPath = path.join(self.sourceRoot, relDir);
          var contents = watch.readAndWatchDirectory(watchSet, {
            absPath: absPath,
            include: filters.include,
            exclude: filters.exclude
          });
          return _.map(contents, function (x) {
            return path.join(relDir, x);
          });
        };

        // Read top-level source files.
        var sources = readAndWatchDirectory('', {
          include: sourceInclude,
          exclude: sourceExclude
        });

        var otherUnibuildRegExp =
              (where === "server" ? /^client\/$/ : /^server\/$/);

        // The paths that we've called checkForInfiniteRecursion on.
        var seenPaths = {};
        // Used internally by fs.realpathSync as an optimization.
        var realpathCache = {};
        var checkForInfiniteRecursion = function (relDir) {
          var absPath = path.join(self.sourceRoot, relDir);
          try {
            var realpath = fs.realpathSync(absPath, realpathCache);
          } catch (e) {
            if (!e || e.code !== 'ELOOP')
              throw e;
            // else leave realpath undefined
          }
          if (realpath === undefined || _.has(seenPaths, realpath)) {
            buildmessage.error("Symlink cycle detected at " + relDir);
            // recover by returning no files
            return true;
          }
          seenPaths[realpath] = true;
          return false;
        };

        // Read top-level subdirectories. Ignore subdirectories that have
        // special handling.
        var sourceDirectories = readAndWatchDirectory('', {
          include: [/\/$/],
          exclude: [/^packages\/$/, /^programs\/$/, /^tests\/$/,
                    /^public\/$/, /^private\/$/,
                    otherUnibuildRegExp].concat(sourceExclude)
        });
        checkForInfiniteRecursion('');

        while (!_.isEmpty(sourceDirectories)) {
          var dir = sourceDirectories.shift();

          // remove trailing slash
          dir = dir.substr(0, dir.length - 1);

          if (checkForInfiniteRecursion(dir))
            return [];  // pretend we found no files

          // Find source files in this directory.
          Array.prototype.push.apply(sources, readAndWatchDirectory(dir, {
            include: sourceInclude,
            exclude: sourceExclude
          }));

          // Find sub-sourceDirectories. Note that we DON'T need to ignore the
          // directory names that are only special at the top level.
          Array.prototype.push.apply(sourceDirectories, readAndWatchDirectory(dir, {
            include: [/\/$/],
            exclude: [/^tests\/$/, otherUnibuildRegExp].concat(sourceExclude)
          }));
        }

        // We've found all the source files. Sort them!
        var templateExtensions = [];
        _.each(extensions, function (isTemplate, ext) {
          isTemplate && templateExtensions.push(ext);
        });
        sources.sort(loadOrderSort(templateExtensions));

        // Convert into relPath/fileOptions objects.
        sources = _.map(sources, function (relPath) {
          var sourceObj = {relPath: relPath};

          // Special case: on the client, JavaScript files in a
          // `client/compatibility` directory don't get wrapped in a closure.
          if (archinfo.matches(arch, "web") && relPath.match(/\.js$/)) {
            var clientCompatSubstr =
                  path.sep + 'client' + path.sep + 'compatibility' + path.sep;
            if ((path.sep + relPath).indexOf(clientCompatSubstr) !== -1)
              sourceObj.fileOptions = {bare: true};
          }
          return sourceObj;
        });

        // Now look for assets for this unibuild.
        var assetDir = archinfo.matches(arch, "web") ? "public" : "private";
        var assetDirs = readAndWatchDirectory('', {
          include: [new RegExp('^' + assetDir + '/$')]
        });

        if (!_.isEmpty(assetDirs)) {
          if (!_.isEqual(assetDirs, [assetDir + '/']))
            throw new Error("Surprising assetDirs: " + JSON.stringify(assetDirs));

          while (!_.isEmpty(assetDirs)) {
            dir = assetDirs.shift();
            // remove trailing slash
            dir = dir.substr(0, dir.length - 1);

            if (checkForInfiniteRecursion(dir))
              return [];  // pretend we found no files

            // Find asset files in this directory.
            var assetsAndSubdirs = readAndWatchDirectory(dir, {
              include: [/.?/],
              // we DO look under dot directories here
              exclude: ignoreFiles
            });

            _.each(assetsAndSubdirs, function (item) {
              if (item[item.length - 1] === '/') {
                // Recurse on this directory.
                assetDirs.push(item);
              } else {
                // This file is an asset.
                sources.push({
                  relPath: item,
                  fileOptions: {
                    isAsset: true
                  }
                });
              }
            });
          }
        }

        return sources;
      };
    });

    if (! self._checkCrossUnibuildVersionConstraints()) {
      // should never happen since we created the unibuilds from
      // .meteor/packages, which doesn't have a way to express
      // different constraints for different unibuilds
      throw new Error("conflicting constraints in a package?");
    }
  },

  // True if the package defines any plugins.
  containsPlugins: function () {
    var self = this;
    return ! _.isEmpty(self.pluginInfo);
  },

  // Return dependency metadata for all unibuilds, in the format needed
  // by the package catalog.
  //
  // Options:
  // - logError: if true, if something goes wrong, log a buildmessage
  //   and return null rather than throwing an exception.
  // - skipWeak: omit weak dependencies
  // - skipUnordered: omit unordered dependencies
  getDependencyMetadata: function (options) {
    var self = this;
    options = options || {};
    var ret = self._computeDependencyMetadata(options);
    if (! ret) {
      if (options.logError)
        return null;
      else
        throw new Error("inconsistent dependency constraint across unibuilds?");
    }
    return ret;
  },

  // Record the versions of the dependencies that we used to actually build the
  // package on disk and save them into the packageSource. Next time we build
  // the package, we will look at them for optimization & repeatable builds.
  //
  // constraints:
  // - dependencies: results of running the constraint solver on the dependency
  //   metadata of this package
  // - pluginDependenciess: results of running the constraint solver on the
  //   plugin dependency data for this package.
  // currentTool: string of the tool version that we are using
  //  (ex: meteor-tool@1.0.0)
  recordDependencyVersions: function (constraints, currentTool) {
    var self = this;
    var versions = _.extend(constraints, {"toolVersion": currentTool });

    // If we don't have a versions file path (because, probably, we are not
    // supposed to record one for this package), then we clearly cannot record
    // on.
    var versionsFile = self.versionsFilePath();
    if (!versionsFile) {
      return;
    }

    // If nothing has changed, don't bother rewriting the versions file.
    if (_.isEqual(self.dependencyVersions, versions)) return;

    // If something has changed, and this is an immutable package source, then
    // we have done something terribly, terribly wrong. Throw.
    if (self.immutable) {
      throw new Error(
        "Version lock for " + self.name + " should never change. Recorded as "
          + JSON.stringify(self.dependencyVersions) + ", calculated as "
          + JSON.stringify(versions));
    };

    // In case we need to rebuild from this package Source, it will be
    // convenient to keep the results on hand and not reread from disk.
    self.dependencyVersions = _.clone(versions);

    // There is always a possibility that we might want to change the format of
    // this file, so let's keep track of what it is.
    versions["format"] = "1.0";

    // When we write versions to disk, we want to alphabetize by package name,
    // both for readability and also for consistency (so two packages built with
    // the same versions have the exact same versions file).
    //
    // This takes in an object mapping key to value and returns an array of
    // <key, value> pairs, alphabetized by key.
    var alphabetize = function (object) {
      return _.sortBy(_.pairs(object),
        function (pair) {
           return pair[0];
        });
    };

    // Both plugins and direct dependencies are objects mapping package name to
    // version number. When we write them on disk, we will convert them to
    // arrays of <packageName, version> and alphabetized by packageName.
    versions["dependencies"] = alphabetize(versions["dependencies"]);
    versions["pluginDependencies"]
      = alphabetize(versions["pluginDependencies"]);

    try {
      // Currently, unnamed packages are apps, and apps have a different
      // versions file format and semantics. So, we don't need to and cannot
      // record dependencyVersions for those, and that's OK for now.
      //
      // Uniload sets its sourceRoot to "/", which is a little strange. Uniload
      // does not need to store dependency versions either.
      if (self.name && self.sourceRoot !== "/") {
        fs.writeFileSync(versionsFile, JSON.stringify(versions, null, 2), 'utf8');
      }
    } catch (e) {
      // We 'recover' by not saving the dependency versions. Log a line about it
      // in case it is unexpected. We don't buildmessage because it doesn't
      // really interrupt our workflow, but the user might want to know about it
      // anyway.
      console.log("Could not write versions file for ", self.name);
    }
 },

  // Returns the filepath to the file containing the version lock for this
  // package, or null if we don't think that this package should have
  // a versions file.
  versionsFilePath : function () {
    var self = this;
    // If we are running from checkout and looking at a core package,
    // don't record its versions. We know what its versions are, and having
    // those extra version lock files is kind of annoying.
    //
    // (This is a medium-term hack. We can build something more modular if
    //  there is any demand for it)
    // See #PackageVersionFilesHack
    if (self.isCore) {
      return null;
    }

    // If we have specified to not record a version file for this package,
    // don't. Currently used to avoid recording version files for separately
    // compiled plugins.
    if (self.noVersionFile) {
      return null;
    }

    // Lastly, we don't record versions files for test packages because we don't
    // see any particularly good reason to do it, and it is confusing to the
    // users.
    if (self.isTest) {
      return null;
    }

    // All right, fine, return a path to the versions file.
    return path.join(self.sourceRoot, "versions.json");
  },

  // If dependencies aren't consistent across unibuilds, return false and
  // also log a buildmessage error if inside a buildmessage job. Else
  // return true.
  // XXX: Check that this is used when refactoring is done.
  _checkCrossUnibuildVersionConstraints: function () {
    var self = this;
    return !! self._computeDependencyMetadata({ logError: true });
  },

  // Compute the return value for getDependencyMetadata, or return
  // null if there is a dependency that doesn't have the same
  // constraint across all unibuilds (and, if logError is true, log a
  // buildmessage error).
  //
  // For options, see getDependencyMetadata.
  _computeDependencyMetadata: function (options) {
    var self = this;
    options = options || {};

    var dependencies = {};
    var allConstraints = {}; // for error reporting. package name to array
    var failed = false;

    _.each(self.architectures, function (arch) {
      // We need to iterate over both uses and implies, since implied packages
      // also constitute dependencies.
      var processUse = function (implied, use) {
        // We can't really have a weak implies (what does that even mean?) but
        // we check for that elsewhere.
        if ((use.weak && options.skipWeak) ||
            (use.unordered && options.skipUnordered))
          return;

        if (!_.has(dependencies, use.package)) {
          dependencies[use.package] = {
            constraint: null,
            references: []
          };
          allConstraints[use.package] = [];
        }
        var d = dependencies[use.package];

        if (use.constraint) {
          allConstraints[use.package].push(use.constraint);

          if (d.constraint === null) {
            d.constraint = use.constraint;
          } else if (d.constraint !== use.constraint) {
            failed = true;
          }
        }

        var reference = {
          arch: archinfo.withoutSpecificOs(arch.arch)
        };
        if (use.weak) {
          reference.weak = true;
        }
        if (use.unordered) {
          reference.unordered = true;
        }
        if (implied) {
          reference.implied = true;
        }
        d.references.push(reference);
      };
      _.each(arch.uses, _.partial(processUse, false));
      _.each(arch.implies, _.partial(processUse, true));
    });

    if (failed && options.logError) {
      _.each(allConstraints, function (constraints, name) {
        constraints = _.uniq(constraints);
        if (constraints.length > 1) {
          buildmessage.error(
            "The version constraint for a dependency must be the same " +
              "at every place it is mentioned in a package. " +
              "'" + name + "' is constrained both as "  +
              constraints.join(' and ') + ". Change them to match.");
          // recover by returning false (the caller had better detect
          // this and use its own recovery logic)
        }
      });
    }

    return failed ? null : dependencies;
  }
});

module.exports = PackageSource;


summary: "Update the client when new client code is available",

<template name='serverFacts'>

    {{#each factsByPackage}}

{{ _id }}

          {{#each facts}}

{{ name }}

{{ value }}

          {{/each}}

    {{/each}}

</template>


summary: "Meteor UI Components framework",

Package.describe({
  summary: "Meteor's latency-compensated distributed data framework",
  version: '1.0.4'
});

// We use 'faye-websocket' for connections in server-to-server DDP, mostly
// because it's the same library used as a server in sockjs, and it's easiest to
// deal with a single websocket implementation.  (Plus, its maintainer is easy
// to work with on pull requests.)
//
// (By listing faye-websocket first, it's more likely that npm deduplication
// will prevent a second copy of faye-websocket from being installed inside
// sockjs.)
Npm.depends({
  // A fork fixing https://github.com/faye/websocket-driver-node/pull/8 (ie
  // "open from inactive client" errors). Note that sockjs won't use this fork,
  // but the bug only affects the websocket client, not the server.
  "faye-websocket": "https://github.com/meteor/faye-websocket-node/tarball/ccc180998b1396093c24d0df7ebc1d199c276552",
  sockjs: "0.3.9"
});

Package.on_use(function (api) {
  api.use(['check', 'random', 'ejson', 'json', 'underscore', 'deps',
           'logging', 'retry'],
          ['client', 'server']);

  // It is OK to use this package on a server architecture without making a
  // server (in order to do server-to-server DDP as a client). So these are only
  // included as weak dependencies.
  // XXX split this package into multiple packages or multiple slices instead
  api.use(['webapp', 'routepolicy'], 'server', {weak: true});

  // Detect whether or not the user wants us to audit argument checks.
  api.use(['audit-argument-checks'], 'server', {weak: true});

  // Allow us to detect 'autopublish', so we can print a warning if the user
  // runs Meteor.publish while it's loaded.
  api.use('autopublish', 'server', {weak: true});

  // If the facts package is loaded, publish some statistics.
  api.use('facts', 'server', {weak: true});

  api.use('callback-hook', 'server');

  api.export('DDP');
  api.export('DDPServer', 'server');

  api.export('LivedataTest', {testOnly: true});

  // Transport
  api.use('reload', 'client', {weak: true});
  api.add_files('common.js');
  api.add_files(['sockjs-0.3.4.js', 'stream_client_sockjs.js'], 'client');
  api.add_files('stream_client_nodejs.js', 'server');
  api.add_files('stream_client_common.js', ['client', 'server']);
  api.add_files('stream_server.js', 'server');

  // we depend on LocalCollection._diffObjects, _applyChanges,
  // _idParse, _idStringify.
  api.use('minimongo', ['client', 'server']);

  api.add_files('heartbeat.js', ['client', 'server']);

  api.add_files('livedata_server.js', 'server');

  api.add_files('writefence.js', 'server');
  api.add_files('crossbar.js', 'server');

  api.add_files('livedata_common.js', ['client', 'server']);
  api.add_files('random_stream.js', ['client', 'server']);

  api.add_files('livedata_connection.js', ['client', 'server']);

  api.add_files('client_convenience.js', 'client');
  api.add_files('server_convenience.js', 'server');
});

Package.on_test(function (api) {
  api.use('livedata', ['client', 'server']);
  api.use('mongo-livedata', ['client', 'server']);
  api.use('test-helpers', ['client', 'server']);
  api.use(['underscore', 'tinytest', 'random', 'deps', 'minimongo']);

  api.add_files('stub_stream.js');
  api.add_files('livedata_server_tests.js', 'server');
  api.add_files('livedata_connection_tests.js', ['client', 'server']);
  api.add_files('livedata_tests.js', ['client', 'server']);
  api.add_files('livedata_test_service.js', ['client', 'server']);
  api.add_files('session_view_tests.js', ['server']);
  api.add_files('crossbar_tests.js', ['server']);
  api.add_files('random_stream_tests.js', ['client', 'server']);

  api.use('http', 'client');
  api.add_files(['stream_tests.js'], 'client');
  api.add_files('stream_client_tests.js', 'server');
  api.use('check', ['client', 'server']);
});


var Scanner = HTMLTools.Scanner;
var getCharacterReference = HTMLTools.Parse.getCharacterReference;

Tinytest.add("html-tools - entities", function (test) {
  var succeed = function (input, match, codepoints) {
    if (typeof input === 'string')
      input = {input: input};

    // match arg is optional; codepoints is never a string
    if (typeof match !== 'string') {
      codepoints = match;
      match = input.input;
    }

    var scanner = new Scanner(input.input);
    var result = getCharacterReference(scanner, input.inAttribute, input.allowedChar);
    test.isTrue(result);
    test.equal(scanner.pos, match.length);
    test.equal(result, {
      t: 'CharRef',
      v: match,
      cp: _.map(codepoints,
                function (x) { return (typeof x === 'string' ?
                                       x.charCodeAt(0) : x); })
    });
  };

  var ignore = function (input) {
    if (typeof input === 'string')
      input = {input: input};

    var scanner = new Scanner(input.input);
    var result = getCharacterReference(scanner, input.inAttribute, input.allowedChar);
    test.isFalse(result);
    test.equal(scanner.pos, 0);
  };

  var fatal = function (input, messageContains) {
    if (typeof input === 'string')
      input = {input: input};

    var scanner = new Scanner(input.input);
    var error;
    try {
      getCharacterReference(scanner, input.inAttribute, input.allowedChar);
    } catch (e) {
      error = e;
    }
    test.isTrue(error);
    if (error)
      test.isTrue(messageContains && error.message.indexOf(messageContains) >= 0, error.message);
  };

  ignore('a');
  ignore('&');
  ignore('&&');
  ignore('&\t');
  ignore('& ');
  fatal('&#', 'Invalid numerical character reference starting with &#');
  ignore('&a');
  fatal('&a;', 'Invalid character reference: &a;');
  ignore({input: '&"', allowedChar: '"'});
  ignore('&"');

  succeed('>', ['>']);
  fatal('&gt', 'Character reference requires semicolon');
  ignore('&aaa');
  fatal('&gta', 'Character reference requires semicolon');
  ignore({input: '&gta', inAttribute: true});
  fatal({input: '&gt=', inAttribute: true}, 'Character reference requires semicolon: &gt');

  succeed('>;', '>', ['>']);

  fatal('&asdflkj;', 'Invalid character reference: &asdflkj;');
  fatal('&A0asdflkj;', 'Invalid character reference: &A0asdflkj;');
  ignore('&A0asdflkj');

  succeed('𝕫', [120171]);
  succeed('∾̳', [8766, 819]);

  succeed('
', [10]);
  fatal('&#10', 'Invalid numerical character reference starting with &#');
  fatal('&#xg;', 'Invalid numerical character reference starting with &#');
  fatal('&#;', 'Invalid numerical character reference starting with &#');
  fatal('&#a;', 'Invalid numerical character reference starting with &#');
  fatal('&#a', 'Invalid numerical character reference starting with &#');
  fatal('&#z', 'Invalid numerical character reference starting with &#');
  succeed('&#000000000000010;', [10]);
  fatal('&#0001000000000010;', 'Numerical character reference too large: 1000000000010');
  succeed('&#x00000000000000000000a;', [10]);
  fatal('&#x000100000000000a;', 'Numerical character reference too large: 0x100000000000a');
  succeed('
', [10]);
  succeed('
', [10]);
  succeed('
', [10]);
  succeed('
', [10]);
  succeed('
', [10]);

  fatal('�', 'Illegal codepoint in numerical character reference: �');
  fatal('�', 'Illegal codepoint in numerical character reference: �');

  fatal('', 'Illegal codepoint in numerical character reference: ');
  succeed('�', [12]);
  fatal('', 'Illegal codepoint in numerical character reference: ');
  succeed('�', [12]);

  fatal('', 'Illegal codepoint in numerical character reference');
  fatal('', 'Illegal codepoint in numerical character reference');
  succeed('􏿽', [0x10fffd]);

  fatal('', 'Illegal codepoint in numerical character reference');
  fatal('', 'Illegal codepoint in numerical character reference');
  succeed('􏿽', [0x10fffd]);

});


Package.describe({
  summary: "route policy declarations",
  version: '1.0.0'
});

Package.on_use(function (api) {
  api.use('underscore', 'server');
  // Resolve circular dependency with webapp. We can only use WebApp via
  // Package.webapp and only after initial load.
  api.use('webapp', 'server', {unordered: true});
  api.export('RoutePolicy', 'server');
  api.export('RoutePolicyTest', 'server', {testOnly: true});
  api.add_files('routepolicy.js', 'server');
});

Package.on_test(function (api) {
  api.use(['routepolicy', 'tinytest']);
  api.add_files(['routepolicy_tests.js'], 'server');
});


summary: "Given the set of the constraints, picks a satisfying configuration",

Package.describe({
  summary: "The Meteor command-line tool",
  version: '1.0.12'
});

Package.includeTool();


Here is some text used by the static file serving test.


// new Blaze.DOMMaterializer(options)
//
// An HTML.Visitor that turns HTMLjs into DOM nodes and DOMRanges.
//
// Options: `parentView`
Blaze.DOMMaterializer = HTML.Visitor.extend();
Blaze.DOMMaterializer.def({
  visitNull: function (x, intoArray) {
    return intoArray;
  },
  visitPrimitive: function (primitive, intoArray) {
    var string = String(primitive);
    intoArray.push(document.createTextNode(string));
    return intoArray;
  },
  visitCharRef: function (charRef, intoArray) {
    return this.visitPrimitive(charRef.str, intoArray);
  },
  visitArray: function (array, intoArray) {
    for (var i = 0; i < array.length; i++)
      this.visit(array[i], intoArray);
    return intoArray;
  },
  visitComment: function (comment, intoArray) {
    intoArray.push(document.createComment(comment.sanitizedValue));
    return intoArray;
  },
  visitRaw: function (raw, intoArray) {
    // Get an array of DOM nodes by using the browser's HTML parser
    // (like innerHTML).
    var nodes = Blaze.DOMBackend.parseHTML(raw.value);
    for (var i = 0; i < nodes.length; i++)
      intoArray.push(nodes[i]);

    return intoArray;
  },
  visitTag: function (tag, intoArray) {
    var self = this;
    var tagName = tag.tagName;
    var elem;
    if ((HTML.isKnownSVGElement(tagName) || isSVGAnchor(tag))
        && document.createElementNS) {
      // inline SVG
      elem = document.createElementNS('http://www.w3.org/2000/svg', tagName);
    } else {
      // normal elements
      elem = document.createElement(tagName);
    }

    var rawAttrs = tag.attrs;
    var children = tag.children;
    if (tagName === 'textarea' && tag.children.length &&
        ! (rawAttrs && ('value' in rawAttrs))) {
      // Provide very limited support for TEXTAREA tags with children
      // rather than a "value" attribute.
      // Reactivity in the form of Views nested in the tag's children
      // won't work.  Compilers should compile textarea contents into
      // the "value" attribute of the tag, wrapped in a function if there
      // is reactivity.
      if (typeof rawAttrs === 'function' ||
          HTML.isArray(rawAttrs)) {
        throw new Error("Can't have reactive children of TEXTAREA node; " +
                        "use the 'value' attribute instead.");
      }
      rawAttrs = _.extend({}, rawAttrs || null);
      rawAttrs.value = Blaze._expand(children, self.parentView);
      children = [];
    }

    if (rawAttrs) {
      var attrUpdater = new ElementAttributesUpdater(elem);
      var updateAttributes = function () {
        var parentView = self.parentView;
        var expandedAttrs = Blaze._expandAttributes(rawAttrs, parentView);
        var flattenedAttrs = HTML.flattenAttributes(expandedAttrs);
        var stringAttrs = {};
        for (var attrName in flattenedAttrs) {
          stringAttrs[attrName] = Blaze.toText(flattenedAttrs[attrName],
                                               parentView,
                                               HTML.TEXTMODE.STRING);
        }
        attrUpdater.update(stringAttrs);
      };
      var updaterComputation;
      if (self.parentView) {
        updaterComputation = self.parentView.autorun(updateAttributes);
      } else {
        updaterComputation = Deps.nonreactive(function () {
          return Deps.autorun(function () {
            Deps.withCurrentView(self.parentView, updateAttributes);
          });
        });
      }
      Blaze.DOMBackend.Teardown.onElementTeardown(elem, function attrTeardown() {
        updaterComputation.stop();
      });
    }

    var childNodesAndRanges = self.visit(children, []);
    for (var i = 0; i < childNodesAndRanges.length; i++) {
      var x = childNodesAndRanges[i];
      if (x instanceof Blaze.DOMRange)
        x.attach(elem);
      else
        elem.appendChild(x);
    }

    intoArray.push(elem);

    return intoArray;
  },
  visitObject: function (x, intoArray) {
    if (Blaze.isTemplate(x))
      x = Blaze.runTemplate(x);
    if (x instanceof Blaze.View) {
      intoArray.push(Blaze.materializeView(x, this.parentView));
      return intoArray;
    }

    // throw the default error
    return HTML.Visitor.prototype.visitObject.call(this, x);
  }
});

var isSVGAnchor = function (node) {
  // We generally aren't able to detect SVG <a> elements because
  // if "A" were in our list of known svg element names, then all
  // <a> nodes would be created using
  // `document.createElementNS`. But in the special case of <a
  // xlink:href="...">, we can at least detect that attribute and
  // create an SVG <a> tag in that case.
  //
  // However, we still have a general problem of knowing when to
  // use document.createElementNS and when to use
  // document.createElement; for example, font tags will always
  // be created as SVG elements which can cause other
  // problems. #1977
  return (node.tagName === "a" &&
          node.attrs &&
          node.attrs["xlink:href"] !== undefined);
};


# Meteor

Meteor is an ultra-simple environment for building modern web
applications.

With Meteor you write apps:

* in pure Javascript
* that send data over the wire, rather than HTML
* using your choice of popular open-source libraries

Documentation is available at http://docs.meteor.com/

## Quick Start

Install Meteor:

    curl https://install.meteor.com | /bin/sh

Create a project:

    meteor create try-meteor

Run it:

    cd try-meteor
    meteor

Deploy it to the world, for free:

    meteor deploy try-meteor.meteor.com

## Slow Start (for developers)

If you want to run on the bleeding edge, or help develop Meteor, you
can run Meteor directly from a git checkout.

    git clone git://github.com/meteor/meteor.git
    cd meteor

If you're the sort of person who likes to build everything from scratch,
you can build all the Meteor dependencies (node.js, npm, mongodb, etc)
with the provided script. This requires git, a C and C++ compiler,
autotools, and scons. If you do not run this script, Meteor will
automatically download pre-compiled binaries when you first run it.

    # OPTIONAL
    ./scripts/generate-dev-bundle.sh

Now you can run meteor directly from the checkout (if you did not
build the dependency bundle above, this will take a few moments to
download a pre-build version).

    ./meteor --help

From your checkout, you can read the docs locally. The `/docs` directory is a
meteor application, so simply change into the `/docs` directory and launch
the app:

    cd docs/
    ../meteor

You'll then be able to read the docs locally in your browser at
`http://localhost:3000/`

Note that if you run Meteor from a git checkout, you cannot pin apps to specific
Meteor releases or run using different Meteor releases using `--release`.

## Uninstalling Meteor

Aside from a short launcher shell script, Meteor installs itself inside your
home directory. To uninstall Meteor, run:

    rm -rf ~/.meteor/
    sudo rm /usr/local/bin/meteor

## Developer Resources

Building an application with Meteor?

* Announcement list: sign up at http://www.meteor.com/
* Ask a question: http://stackoverflow.com/questions/tagged/meteor
* Meteor help and discussion mailing list: https://groups.google.com/group/meteor-talk
* IRC: `#meteor` on `irc.freenode.net`

Interested in contributing to Meteor?

* Core framework design mailing list: https://groups.google.com/group/meteor-core
* Contribution guidelines: https://github.com/meteor/meteor/tree/devel/Contributing.md

We are hiring!  Visit https://www.meteor.com/jobs/working-at-meteor to
learn more about working full-time on the Meteor project.


summary: "Meteor's latency-compensated distributed data framework",

// We use 'faye-websocket' for connections in server-to-server DDP, mostly

// because it's the same library used as a server in sockjs, and it's easiest to

// deal with a single websocket implementation.  (Plus, its maintainer is easy

keep keep replace keep keep keep keep keep

Package.describe({
  summary: "Meteor UI Components framework",
  version: '1.0.2'
});

Package.on_use(function (api) {
  api.export(['Blaze']);
  api.use('jquery'); // should be a weak dep, by having multiple "DOM backends"
  api.use('deps');
  api.use('underscore'); // only the subset in microscore.js
  api.use('htmljs');
  api.use('observe-sequence');

  api.add_files([
    'preamble.js'
  ]);

  // client-only files
  api.add_files([
    'dombackend.js',
    'domrange.js',
    'events.js',
    'attrs.js',
    'materializer.js'
  ], 'client');

  // client and server
  api.add_files([
    'exceptions.js',
    'reactivevar.js',
    'view.js',
    'builtins.js',
    'lookup.js'
  ]);
});

Package.on_test(function (api) {
  api.use('tinytest');
  api.use('jquery'); // strong dependency, for testing jQuery backend
  api.use('blaze');
  api.use(['test-helpers', 'underscore'], 'client');

  // ...
});


Meteor release, supported by MDG.


var selftest = require('../selftest.js');
var Sandbox = selftest.Sandbox;
var utils = require('../utils.js');
var Future = require('fibers/future');
var net = require('net');
var _ = require('underscore');
var files = require('../files.js');

selftest.define("css hot code push", function (options) {
  var s = new Sandbox({
    clients: options.clients,
  });

  s.createApp("myapp", "css-injection-test");
  s.cd("myapp");
  s.testWithAllClients(function (run) {
    run.baseTimeout = 20;
    run.match("myapp");
    run.match("proxy");
    run.match("MongoDB");
    run.match("running at");
    run.match("localhost");

    run.connectClient();

    run.waitSecs(20);
    run.match("client connected");

    // 'numCssChanges' variable is set to 0 on a client refresh.
    // Since CSS changes should not trigger a client refresh, numCssChanges
    // should never reset.

    // The css file is initially empty.
    run.match("numCssChanges: 0");

    // Some browsers represent no background as 'transparent', others use
    // rgba(0, 0, 0, 0).
    run.match(/background-color: (transparent|rgba\(0, 0, 0, 0\))/);

    // The server does NOT restart if a new css file is added.
    s.write("test.css", "body { background-color: red; }");
    run.match("Client modified -- refreshing");
    run.match("numCssChanges: 1");
    run.match("background-color: rgb(255, 0, 0)");

    s.write("test.css", "body { background-color: blue; }");
    run.match("Client modified -- refreshing");
    run.match("numCssChanges: 2");
    run.match("background-color: rgb(0, 0, 255)");

    // The server does NOT restart if a css file is removed.
    s.unlink("test.css");
    run.match("Client modified -- refreshing");
    run.match("numCssChanges: 3");
    run.match(/background-color: (transparent|rgba\(0, 0, 0, 0\))/);
    run.stop();
  });
});

selftest.define("versioning hot code push", function (options) {
  var s = new Sandbox();

  s.set("AUTOUPDATE_VERSION", "1.0");
  s.createApp("myapp", "hot-code-push-test");
  s.cd("myapp");

  s.testWithAllClients(function (run) {
    run.baseTimeout = 20;
    run.match("myapp");
    run.match("proxy");
    run.match("MongoDB");
    run.match("running at");
    run.match("localhost");
    run.connectClient();
    run.waitSecs(20);

    run.match("client connected: 0");

    run.forbidAll("Error listening");

    run.stop();
  });
});

selftest.define("javascript hot code push", function (options) {
  var s = new Sandbox({
    clients: options.clients,
  });

  s.createApp("myapp", "hot-code-push-test");
  s.cd("myapp");
  s.testWithAllClients(function (run) {
    run.baseTimeout = 20;
    run.match("myapp");
    run.match("proxy");
    run.match("MongoDB");
    run.match("running at");
    run.match("localhost");

    run.connectClient();
    run.waitSecs(20);

    // There is initially no JavaScript file.
    run.match("client connected: 0");
    run.match("jsVar: undefined");

    // The server and client both restart if a shared js file is added
    // or removed.
    s.write("test.js", "jsVar = 'foo'");
    run.match("server restarted");
    run.match("client connected: 0");
    run.match("jsVar: foo");

    s.unlink("test.js");
    run.match("server restarted");
    run.match("client connected: 0");
    run.match("jsVar: undefined");

    // Only the client should refresh if a client js file is added. Thus,
    // "client connected" variable will be incremented.
    s.mkdir("client");
    s.write("client/test.js", "jsVar = 'bar'");
    run.match("client connected: 1");
    run.match("jsVar: bar");

    s.unlink("client/test.js");
    run.match("client connected: 2");
    run.match("jsVar: undefined");

    // When we change a server file the client should not refresh. We observe
    // this by changing a server file and then a client file and verifying
    // that the client has only connected once.
    s.mkdir("server");
    s.write("server/test.js", "jsVar = 'bar'");
    run.match("server restarted");

    s.write("client/empty.js", "");
    run.match("client connected: 0");
    // We should not be able to access a server variable from the client.
    run.match("jsVar: undefined");

    s.unlink("client/empty.js");
    run.waitSecs(5);
    run.match("client connected: 1");
    run.match("jsVar: undefined");

    // Break the HTML file. This should kill the server, and print errors.
    // (It would be reasonable behavior for this to NOT kill the server, since
    // it only affects the client. But this is a regression test for a bug where
    // fixing the HTML file wouldn't actually restart the server; that's the
    // important part of this test.)
    s.write("hot-code-push-test.html", ">");
    run.match("Errors prevented startup");
    run.match("bad formatting in HTML template");
    // Fix it. It should notice, and restart. The client will restart too.
    s.write("hot-code-push-test.html", "");
    run.match("server restarted");
    run.match("client connected: 0");
    // Write something else to it. The client should restart.
    s.write("hot-code-push-test.html", "<head><title>foo</title></head>");
    run.match("Client modified -- refreshing");
    run.match("client connected: 1");
    run.match("jsVar: undefined");

    s.write("client/test.js", "jsVar = 'bar'");
    run.match("client connected: 2");
    run.match("jsVar: bar");

    s.write("client/test.js", "jsVar = 'baz'");
    run.match("client connected: 3");
    run.match("jsVar: baz");

    s.unlink("client/test.js");

    // Setting the autoupdateVersion to a different string should also
    // force the client to restart.
    s.write("server/test.js",
            "Package.autoupdate.Autoupdate.autoupdateVersion = 'random'");
    run.match("server restarted");
    run.match("client connected: 0");
    run.match("jsVar: undefined");

    s.unlink("server/test.js");
    run.match("server restarted");

    run.stop();
  });
});


Package.describe({
  summary: "Handlebars-like template language for Meteor",
  version: '1.0.0'
});

// For more, see package `spacebars-compiler`, which is used by
// the build plugin and not shipped to the client unless you
// ask for it by name.
//
// The Spacebars build plugin is in package `templating`.
//
// Additional tests are in `spacebars-tests`.

Package.on_use(function (api) {
  api.export('Spacebars');

  api.use('htmljs');
  api.use('ui');
  api.use('observe-sequence');
  api.use('templating');
  api.add_files(['spacebars-runtime.js']);
  api.add_files(['dynamic.html', 'dynamic.js'], 'client');
});

Package.on_test(function (api) {
  api.use(["spacebars", "tinytest", "test-helpers"]);
  api.use("templating", "client");
  api.add_files(["dynamic_tests.html", "dynamic_tests.js"], "client");
});


summary: "The Meteor command-line tool",


Tinytest.add("core - debug", function (test) {

  // Just run a log statement and make sure it doesn't explode.
  Meteor._suppress_log(3);
  Meteor._debug();
  Meteor._debug("test one arg");
  Meteor._debug("this", "is", "a", "test");
  test.ok();

});


require("./words.js");

var BOGGLE_DICE = ['pchoas', 'oattow', 'lrytte', 'vthrwe',
                   'eghwne', 'seotis', 'anaeeg', 'idsytt',
                   'mtoicu', 'afpkfs', 'xlderi', 'ensieu',
                   'yldevr', 'znrnhl', 'nmiqhu', 'obbaoj'];

var FLAGS = [0x1, 0x2, 0x4, 0x8,
             0x10, 0x20, 0x40, 0x80,
             0x100, 0x200, 0x400, 0x800,
             0x1000, 0x2000, 0x4000, 0x8000];

var MASKS = {};

// generate masks for all one, two, three, and four-letter combinations
var make_masks = function () {
  var mask_count = 0;

  // recursive helper
  var check_mask = function (word, index, used) {
    for (var i = 0; i < 16; i += 1) {
      for (var j = 0; j < 6; j += 1) {
        // if die i is still available, and it has a letter that
        // matches what we need, we can make use of this.
        if (!(used & FLAGS[i]) && (BOGGLE_DICE[i][j] === word[index])) {
          if (word.length === index + 1) {
            // this is the end of the word, we have a valid mask!
            if (!MASKS[word])
              MASKS[word] = [];
            if (MASKS[word].indexOf(used | FLAGS[i]) === -1) {
              MASKS[word].push(used | FLAGS[i]);
              mask_count += 1;
            }
            // console.log("MASK", word, used | FLAGS[i]);
          } else {
            // descend into searching rest of word w/ remaining dice.
            check_mask(word,
                       index + 1,
                       used | FLAGS[i]);
          }
        }
      }
    }
  }

  process.stderr.write("CALCULATING MASKS FOR ");
  for (var a = 97; a <= 97; a += 1) {
    process.stderr.write(String.fromCharCode(a));
    check_mask(String.fromCharCode(a), 0, 0x0);
    for (var b = 97; b <= 122; b += 1) {
      check_mask(String.fromCharCode(a,b), 0, 0x0);
      for (var c = 97; c <= 122; c += 1) {
        check_mask(String.fromCharCode(a,b,c), 0, 0x0);
        for (var d = 97; d <= 122; d += 1) {
          check_mask(String.fromCharCode(a,b,c,d), 0, 0x0);
        }
      }
    }
  }
  process.stderr.write(" DONE [" + mask_count + " MASKS]\n");
};

make_masks();

function check (word, index, used) {
  //console.log('CHECK', word, index, used);

  var length;
  var masks;

  // check up to 4 chars at a time
  length = (word.length - index > 4) ? 4 : word.length - index;
  masks = MASKS[word.slice(index, length + index)] || [];

  for (var i = 0; i < masks.length; i += 1) {
    if (!(used & masks[i])) {
      // masks[i] has no overlap w/ tiles we already consumed.
      if (word.length === index + length)
        // we consumed the whole word.
        return true;
      else if (check(word, index + length, used | masks[i]))
        // some descendant consumed the whole word
        return true;
    }
  }

  // none of the available masks returned true.  there's no match.
  return false;
};

var dict_len = DICTIONARY.length
for (var i = 0; i < dict_len; i+=1) {
  var word = DICTIONARY[i];

  // reject words that have q followed by non-u.  those can't be made
  // in boggle.  otherwise, strip the q -- our dictionary won't
  // include the u.

  if (word.match(/q/)) {
    if (word.match(/q[^u]/)) {
      process.stderr.write('Q REJECT ' + word + '\n');
      continue;
    } else {
      word = word.replace('qu', 'q');
      process.stderr.write('Q REPLACED ' + word + '\n');
    }
  }

  if (check(word, 0, 0x0))
    console.log(word);
  else
    process.stderr.write('REJECT ' + DICTIONARY[i] + '\n');
};


if (Meteor.isServer)
  var Future = Npm.require('fibers/future');

if (typeof __meteor_runtime_config__ === 'object' &&
    __meteor_runtime_config__.meteorRelease)
  Meteor.release = __meteor_runtime_config__.meteorRelease;

// XXX find a better home for these? Ideally they would be _.get,
// _.ensure, _.delete..

_.extend(Meteor, {
  // _get(a,b,c,d) returns a[b][c][d], or else undefined if a[b] or
  // a[b][c] doesn't exist.
  //
  _get: function (obj /*, arguments */) {
    for (var i = 1; i < arguments.length; i++) {
      if (!(arguments[i] in obj))
        return undefined;
      obj = obj[arguments[i]];
    }
    return obj;
  },

  // _ensure(a,b,c,d) ensures that a[b][c][d] exists. If it does not,
  // it is created and set to {}. Either way, it is returned.
  //
  _ensure: function (obj /*, arguments */) {
    for (var i = 1; i < arguments.length; i++) {
      var key = arguments[i];
      if (!(key in obj))
        obj[key] = {};
      obj = obj[key];
    }

    return obj;
  },

  // _delete(a, b, c, d) deletes a[b][c][d], then a[b][c] unless it
  // isn't empty, then a[b] unless it isn't empty.
  //
  _delete: function (obj /*, arguments */) {
    var stack = [obj];
    var leaf = true;
    for (var i = 1; i < arguments.length - 1; i++) {
      var key = arguments[i];
      if (!(key in obj)) {
        leaf = false;
        break;
      }
      obj = obj[key];
      if (typeof obj !== "object")
        break;
      stack.push(obj);
    }

    for (var i = stack.length - 1; i >= 0; i--) {
      var key = arguments[i+1];

      if (leaf)
        leaf = false;
      else
        for (var other in stack[i][key])
          return; // not empty -- we're done

      delete stack[i][key];
    }
  },

  // _wrapAsync can wrap any function that takes some number of arguments that
  // can't be undefined, followed by some optional arguments, where the callback
  // is the last optional argument.
  // e.g. fs.readFile(pathname, [callback]),
  // fs.open(pathname, flags, [mode], [callback])
  // For maximum effectiveness and least confusion, wrapAsync should be used on
  // functions where the callback is the only argument of type Function.
  //
  _wrapAsync: function (fn) {
    return function (/* arguments */) {
      var self = this;
      var callback;
      var fut;
      var newArgs = _.toArray(arguments);

      var logErr = function (err) {
        if (err)
          return Meteor._debug("Exception in callback of async function",
                               err.stack ? err.stack : err);
      };

      // Pop off optional args that are undefined
      while (newArgs.length > 0 &&
             typeof(newArgs[newArgs.length - 1]) === "undefined") {
        newArgs.pop();
      }
      // If we have any left and the last one is a function, then that's our
      // callback; otherwise, we don't have one.
      if (newArgs.length > 0 &&
          newArgs[newArgs.length - 1] instanceof Function) {
        callback = newArgs.pop();
      } else {
        if (Meteor.isClient) {
          callback = logErr;
        } else {
          fut = new Future();
          callback = fut.resolver();
        }
      }
      newArgs.push(Meteor.bindEnvironment(callback));
      var result = fn.apply(self, newArgs);
      if (fut)
        return fut.wait();
      return result;
    };
  },

  // Sets child's prototype to a new object whose prototype is parent's
  // prototype. Used as:
  //   Meteor._inherits(ClassB, ClassA).
  //   _.extend(ClassB.prototype, { ... })
  // Inspired by CoffeeScript's `extend` and Google Closure's `goog.inherits`.
  _inherits: function (Child, Parent) {
    // copy Parent static properties
    for (var key in Parent) {
      // make sure we only copy hasOwnProperty properties vs. prototype
      // properties
      if (_.has(Parent, key))
        Child[key] = Parent[key];
    }

    // a middle member of prototype chain: takes the prototype from the Parent
    var Middle = function () {
      this.constructor = Child;
    };
    Middle.prototype = Parent.prototype;
    Child.prototype = new Middle();
    Child.__super__ = Parent.prototype;
    return Child;
  }
});


// XXX We should revisit how we factor MongoDB support into (1) the
// server-side node.js driver [which you might use independently of
// livedata, after all], (2) minimongo [ditto], and (3) Collection,
// which is the class that glues the two of them to Livedata, but also
// is generally the "public interface for newbies" to Mongo in the
// Meteor universe. We want to allow the components to be used
// independently, but we don't want to overwhelm the user with
// minutiae.

Package.describe({
  summary: "Adaptor for using MongoDB and Minimongo over DDP",
  version: '1.0.2'
});

Npm.depends({
  mongodb: "https://github.com/meteor/node-mongodb-native/tarball/cbd6220ee17c3178d20672b4a1df80f82f97d4c1"
});

Package.on_use(function (api) {
  api.use(['random', 'ejson', 'json', 'underscore', 'minimongo', 'logging',
           'livedata', 'deps', 'application-configuration'],
          ['client', 'server']);
  api.use('check', ['client', 'server']);

  // Binary Heap data structure is used to optimize oplog observe driver
  // performance.
  api.use('binary-heap', 'server');

  // Allow us to detect 'insecure'.
  api.use('insecure', {weak: true});

  // Allow us to detect 'autopublish', and publish collections if it's loaded.
  api.use('autopublish', 'server', {weak: true});

  // Allow us to detect 'disable-oplog', which turns off oplog tailing for your
  // app even if it's configured in the environment. (This package will be
  // probably be removed before 1.0.)
  api.use('disable-oplog', 'server', {weak: true});

  // defaultRemoteCollectionDriver gets its deployConfig from something that is
  // (for questionable reasons) initialized by the webapp package.
  api.use('webapp', 'server', {weak: true});

  // If the facts package is loaded, publish some statistics.
  api.use('facts', 'server', {weak: true});

  api.use('callback-hook', 'server');

  // Stuff that should be exposed via a real API, but we haven't yet.
  api.export('MongoInternals', 'server');
  // For tests only.
  api.export('MongoTest', 'server', {testOnly: true});

  api.add_files(['mongo_driver.js', 'oplog_tailing.js',
                 'observe_multiplex.js', 'doc_fetcher.js',
                 'polling_observe_driver.js','oplog_observe_driver.js'],
                'server');
  api.add_files('local_collection_driver.js', ['client', 'server']);
  api.add_files('remote_collection_driver.js', 'server');
  api.add_files('collection.js', ['client', 'server']);
});

Package.on_test(function (api) {
  api.use('mongo-livedata');
  api.use('check');
  api.use(['tinytest', 'underscore', 'test-helpers', 'ejson', 'random',
           'livedata']);
  // XXX test order dependency: the allow_tests "partial allow" test
  // fails if it is run before mongo_livedata_tests.
  api.add_files('mongo_livedata_tests.js', ['client', 'server']);
  api.add_files('allow_tests.js', ['client', 'server']);
  api.add_files('collection_tests.js', ['client', 'server']);
  api.add_files('observe_changes_tests.js', ['client', 'server']);
  api.add_files('oplog_tests.js', 'server');
  api.add_files('doc_fetcher_tests.js', 'server');
});


summary: "Parses Meteor Smart Package version string",

Package.describe({
  summary: "Update the client when new client code is available",
  version: '1.0.3'
});

Package.on_use(function (api) {
  api.use('webapp', 'server');
  api.use(['deps', 'retry'], 'client');
  api.use(['livedata', 'mongo-livedata', 'underscore'], ['client', 'server']);
  api.use('deps', 'client');
  api.use('reload', 'client', {weak: true});

  api.export('Autoupdate');
  api.add_files('autoupdate_server.js', 'server');
  api.add_files('autoupdate_client.js', 'client');
});