## SYNOPSIS
- npm list
- npm ls
- npm la
- npm ll
+ npm list [<pkg> ...]
+ npm ls [<pkg> ...]
+ npm la [<pkg> ...]
+ npm ll [<pkg> ...]
## DESCRIPTION
This command will print to stdout all the versions of packages that are
installed, as well as their dependencies, in a tree-structure.
-It does not take positional arguments, though you may set config flags
-like with any other command, such as `-g` to list global packages.
+Positional arguments are `name@version-range` identifiers, which will
+limit the results to only the paths to the packages named. Note that
+nested packages will *also* show the paths to the specified packages.
+For example, running `npm ls promzard` in npm's source tree will show:
-It will print out extraneous, missing, and invalid packages.
+ npm@@VERSION@ /path/to/npm
+ └─┬ init-package-json@0.0.4
+ └── promzard@0.1.5
+
+It will show print out extraneous, missing, and invalid packages.
When run as `ll` or `la`, it shows extended information by default.
<p>This function should not be used programmatically. Instead, just refer
to the <code>npm.bin</code> member.</p>
</div>
-<p id="footer">bin — npm@1.1.36</p>
+<p id="footer">bin — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This command will launch a browser, so this command may not be the most
friendly for programmatic use.</p>
</div>
-<p id="footer">bugs — npm@1.1.36</p>
+<p id="footer">bugs — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/index.html">index(1)</a></li></ul>
</div>
-<p id="footer">commands — npm@1.1.36</p>
+<p id="footer">commands — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/npm.html">npm(3)</a></li></ul>
</div>
-<p id="footer">config — npm@1.1.36</p>
+<p id="footer">config — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/publish.html">publish(3)</a></li><li><a href="../api/unpublish.html">unpublish(3)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">deprecate — npm@1.1.36</p>
+<p id="footer">deprecate — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This command will launch a browser, so this command may not be the most
friendly for programmatic use.</p>
</div>
-<p id="footer">docs — npm@1.1.36</p>
+<p id="footer">docs — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Since this command opens an editor in a new process, be careful about where
and how this is used.</p>
</div>
-<p id="footer">edit — npm@1.1.36</p>
+<p id="footer">edit — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>The first element in the 'args' parameter must be a package name. After that is the optional command, which can be any number of strings. All of the strings will be combined into one, space-delimited command.</p>
</div>
-<p id="footer">explore — npm@1.1.36</p>
+<p id="footer">explore — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>The silent parameter is not neccessary not used, but it may in the future.</p>
</div>
-<p id="footer">help-search — npm@1.1.36</p>
+<p id="footer">help-search — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p><a href="../doc/json.html">json(1)</a></p>
</div>
-<p id="footer">init — npm@1.1.36</p>
+<p id="footer">init — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Finally, 'callback' is a function that will be called when all packages have been
installed or when an error has been encountered.</p>
</div>
-<p id="footer">install — npm@1.1.36</p>
+<p id="footer">install — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Now, any changes to the redis package will be reflected in
the package in the current working directory</p>
</div>
-<p id="footer">link — npm@1.1.36</p>
+<p id="footer">link — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>For a list of all the available command-line configs, see <code>npm help config</code></p>
</div>
-<p id="footer">load — npm@1.1.36</p>
+<p id="footer">load — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
This means that if a submodule a same dependency as a parent module, then the
dependency will only be output once.</p>
</div>
-<p id="footer">ls — npm@1.1.36</p>
+<p id="footer">ls — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h2 id="VERSION">VERSION</h2>
-<p>1.1.36</p>
+<p>1.1.39</p>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
<pre><code>var cmd = npm.deref("unp") // cmd === "unpublish"</code></pre>
</div>
-<p id="footer">npm — npm@1.1.36</p>
+<p id="footer">npm — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>If the 'packages' parameter is left out, npm will check all packages.</p>
</div>
-<p id="footer">outdated — npm@1.1.36</p>
+<p id="footer">outdated — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/publish.html">publish(3)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">owner — npm@1.1.36</p>
+<p id="footer">owner — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>If no arguments are supplied, then npm packs the current package folder.</p>
</div>
-<p id="footer">pack — npm@1.1.36</p>
+<p id="footer">pack — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This function is not useful programmatically</p>
</div>
-<p id="footer">prefix — npm@1.1.36</p>
+<p id="footer">prefix — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Extraneous packages are packages that are not listed on the parent
package's dependencies list.</p>
</div>
-<p id="footer">prune — npm@1.1.36</p>
+<p id="footer">prune — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../api/owner.html">owner(3)</a></li></ul>
</div>
-<p id="footer">publish — npm@1.1.36</p>
+<p id="footer">publish — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>See <code>npm help build</code></p>
</div>
-<p id="footer">rebuild — npm@1.1.36</p>
+<p id="footer">rebuild — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../api/start.html">start(3)</a></li><li><a href="../api/stop.html">stop(3)</a></li></ul>
</div>
-<p id="footer">restart — npm@1.1.36</p>
+<p id="footer">restart — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This function is not useful programmatically.</p>
</div>
-<p id="footer">root — npm@1.1.36</p>
+<p id="footer">root — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../api/test.html">test(3)</a></li><li><a href="../api/start.html">start(3)</a></li><li><a href="../api/restart.html">restart(3)</a></li><li><a href="../api/stop.html">stop(3)</a></li></ul>
</div>
-<p id="footer">run-script — npm@1.1.36</p>
+<p id="footer">run-script — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
and doesn't try to read your mind (it doesn't do any verb tense matching or the
like).</p>
</div>
-<p id="footer">search — npm@1.1.36</p>
+<p id="footer">search — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Finally, 'callback' is a function that will be called when the shrinkwrap has
been saved.</p>
</div>
-<p id="footer">shrinkwrap — npm@1.1.36</p>
+<p id="footer">shrinkwrap — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>npm can run tests on multiple packages. Just specify multiple packages
in the <code>packages</code> parameter.</p>
</div>
-<p id="footer">start — npm@1.1.36</p>
+<p id="footer">start — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>npm can run stop on multiple packages. Just specify multiple packages
in the <code>packages</code> parameter.</p>
</div>
-<p id="footer">stop — npm@1.1.36</p>
+<p id="footer">stop — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li>npm help json</li><li>git help submodule</li></ul>
</div>
-<p id="footer">submodule — npm@1.1.36</p>
+<p id="footer">submodule — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
used. For more information about how to set this config, check
<code>man 3 npm-config</code> for programmatic usage or <code>man npm-config</code> for cli usage.</p>
</div>
-<p id="footer">tag — npm@1.1.36</p>
+<p id="footer">tag — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>npm can run tests on multiple packages. Just specify multiple packages
in the <code>packages</code> parameter.</p>
</div>
-<p id="footer">test — npm@1.1.36</p>
+<p id="footer">test — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>Finally, 'callback' is a function that will be called when all packages have been
uninstalled or when an error has been encountered.</p>
</div>
-<p id="footer">uninstall — npm@1.1.36</p>
+<p id="footer">uninstall — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>If no version is specified, or if all versions are removed then
the root package entry is removed from the registry entirely.</p>
</div>
-<p id="footer">unpublish — npm@1.1.36</p>
+<p id="footer">unpublish — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>The 'packages' argument is an array of packages to update. The 'callback' parameter will be called when done or when an error occurs.</p>
</div>
-<p id="footer">update — npm@1.1.36</p>
+<p id="footer">update — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
parameter. The difference, however, is this function will fail if it does
not have exactly one element. The only element should be a version number.</p>
</div>
-<p id="footer">version — npm@1.1.36</p>
+<p id="footer">version — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>corresponding to the list of fields selected.</p>
</div>
-<p id="footer">view — npm@1.1.36</p>
+<p id="footer">view — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p>This function is not useful programmatically</p>
</div>
-<p id="footer">whoami — npm@1.1.36</p>
+<p id="footer">whoami — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/help.html">help(1)</a></li><li><a href="../doc/index.html">index(1)</a></li></ul>
</div>
-<p id="footer"><a href="../doc/README.html">README</a> — npm@1.1.36</p>
+<p id="footer"><a href="../doc/README.html">README</a> — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li><li><a href="../doc/whoami.html">whoami(1)</a></li></ul>
</div>
-<p id="footer">adduser — npm@1.1.36</p>
+<p id="footer">adduser — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/prefix.html">prefix(1)</a></li><li><a href="../doc/root.html">root(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">bin — npm@1.1.36</p>
+<p id="footer">bin — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/docs.html">docs(1)</a></li><li><a href="../doc/view.html">view(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">bugs — npm@1.1.36</p>
+<p id="footer">bugs — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/link.html">link(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">build — npm@1.1.36</p>
+<p id="footer">build — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">bundle — npm@1.1.36</p>
+<p id="footer">bundle — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/pack.html">pack(1)</a></li></ul>
</div>
-<p id="footer">cache — npm@1.1.36</p>
+<p id="footer">cache — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li></ul>
</div>
-<p id="footer">changelog — npm@1.1.36</p>
+<p id="footer">changelog — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li></ul>
</div>
-<p id="footer">coding-style — npm@1.1.36</p>
+<p id="footer">coding-style — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li></ul>
</div>
-<p id="footer">completion — npm@1.1.36</p>
+<p id="footer">completion — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li></ul>
</div>
-<p id="footer">config — npm@1.1.36</p>
+<p id="footer">config — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">deprecate — npm@1.1.36</p>
+<p id="footer">deprecate — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/init.html">init(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li></ul>
</div>
-<p id="footer">developers — npm@1.1.36</p>
+<p id="footer">developers — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li></ul>
</div>
-<p id="footer">disputes — npm@1.1.36</p>
+<p id="footer">disputes — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/view.html">view(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">docs — npm@1.1.36</p>
+<p id="footer">docs — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/explore.html">explore(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">edit — npm@1.1.36</p>
+<p id="footer">edit — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/submodule.html">submodule(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/edit.html">edit(1)</a></li><li><a href="../doc/rebuild.html">rebuild(1)</a></li><li><a href="../doc/build.html">build(1)</a></li><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">explore — npm@1.1.36</p>
+<p id="footer">explore — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li></ul>
</div>
-<p id="footer">faq — npm@1.1.36</p>
+<p id="footer">faq — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/pack.html">pack(1)</a></li><li><a href="../doc/cache.html">cache(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li></ul>
</div>
-<p id="footer">folders — npm@1.1.36</p>
+<p id="footer">folders — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/help.html">help(1)</a></li></ul>
</div>
-<p id="footer">help-search — npm@1.1.36</p>
+<p id="footer">help-search — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/npm.html">npm(1)</a></li><li><a href="../doc/README.html">README</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/help-search.html">help-search(1)</a></li><li><a href="../doc/index.html">index(1)</a></li></ul>
</div>
-<p id="footer">help — npm@1.1.36</p>
+<p id="footer">help — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<p> Display npm username</p>
</div>
-<p id="footer">index — npm@1.1.36</p>
+<p id="footer">index — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="https://github.com/isaacs/init-package-json">https://github.com/isaacs/init-package-json</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/version.html">version(1)</a></li></ul>
</div>
-<p id="footer">init — npm@1.1.36</p>
+<p id="footer">init — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/update.html">update(1)</a></li><li><a href="../doc/link.html">link(1)</a></li><li><a href="../doc/rebuild.html">rebuild(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/build.html">build(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/tag.html">tag(1)</a></li><li><a href="../doc/rm.html">rm(1)</a></li><li><a href="../doc/shrinkwrap.html">shrinkwrap(1)</a></li></ul>
</div>
-<p id="footer">install — npm@1.1.36</p>
+<p id="footer">install — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/semver.html">semver(1)</a></li><li><a href="../doc/init.html">init(1)</a></li><li><a href="../doc/version.html">version(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/help.html">help(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/rm.html">rm(1)</a></li></ul>
</div>
-<p id="footer">json — npm@1.1.36</p>
+<p id="footer">json — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">link — npm@1.1.36</p>
+<p id="footer">link — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h2 id="SYNOPSIS">SYNOPSIS</h2>
-<pre><code>npm list
-npm ls
-npm la
-npm ll</code></pre>
+<pre><code>npm list [<pkg> ...]
+npm ls [<pkg> ...]
+npm la [<pkg> ...]
+npm ll [<pkg> ...]</code></pre>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
<p>This command will print to stdout all the versions of packages that are
installed, as well as their dependencies, in a tree-structure.</p>
-<p>It does not take positional arguments, though you may set config flags
-like with any other command, such as <code>-g</code> to list global packages.</p>
+<p>Positional arguments are <code>name@version-range</code> identifiers, which will
+limit the results to only the paths to the packages named. Note that
+nested packages will <em>also</em> show the paths to the specified packages.
+For example, running <code>npm ls promzard</code> in npm's source tree will show:</p>
-<p>It will print out extraneous, missing, and invalid packages.</p>
+<pre><code>npm@1.1.39 /path/to/npm
+└─┬ init-package-json@0.0.4
+ └── promzard@0.1.5</code></pre>
+
+<p>It will show print out extraneous, missing, and invalid packages.</p>
<p>When run as <code>ll</code> or <code>la</code>, it shows extended information by default.</p>
<ul><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/link.html">link(1)</a></li><li><a href="../doc/prune.html">prune(1)</a></li><li><a href="../doc/outdated.html">outdated(1)</a></li><li><a href="../doc/update.html">update(1)</a></li></ul>
</div>
-<p id="footer">list — npm@1.1.36</p>
+<p id="footer">list — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<h2 id="VERSION">VERSION</h2>
-<p>1.1.36</p>
+<p>1.1.39</p>
<h2 id="DESCRIPTION">DESCRIPTION</h2>
<ul><li><a href="../doc/help.html">help(1)</a></li><li><a href="../doc/faq.html">faq(1)</a></li><li><a href="../doc/README.html">README</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/index.html">index(1)</a></li><li><a href="../api/npm.html">npm(3)</a></li></ul>
</div>
-<p id="footer">npm — npm@1.1.36</p>
+<p id="footer">npm — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/update.html">update(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li></ul>
</div>
-<p id="footer">outdated — npm@1.1.36</p>
+<p id="footer">outdated — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/disputes.html">disputes(1)</a></li></ul>
</div>
-<p id="footer">owner — npm@1.1.36</p>
+<p id="footer">owner — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/cache.html">cache(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">pack — npm@1.1.36</p>
+<p id="footer">pack — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/root.html">root(1)</a></li><li><a href="../doc/bin.html">bin(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">prefix — npm@1.1.36</p>
+<p id="footer">prefix — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/rm.html">rm(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/list.html">list(1)</a></li></ul>
</div>
-<p id="footer">prune — npm@1.1.36</p>
+<p id="footer">prune — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li><li><a href="../doc/deprecate.html">deprecate(1)</a></li><li><a href="../doc/tag.html">tag(1)</a></li></ul>
</div>
-<p id="footer">publish — npm@1.1.36</p>
+<p id="footer">publish — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/build.html">build(1)</a></li><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">rebuild — npm@1.1.36</p>
+<p id="footer">rebuild — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/disputes.html">disputes(1)</a></li></ul>
</div>
-<p id="footer">registry — npm@1.1.36</p>
+<p id="footer">registry — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/README.html">README</a></li><li><a href="../doc/rm.html">rm(1)</a></li><li><a href="../doc/prune.html">prune(1)</a></li></ul>
</div>
-<p id="footer">removing-npm — npm@1.1.36</p>
+<p id="footer">removing-npm — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">restart — npm@1.1.36</p>
+<p id="footer">restart — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/prefix.html">prefix(1)</a></li><li><a href="../doc/bin.html">bin(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">root — npm@1.1.36</p>
+<p id="footer">root — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">run-script — npm@1.1.36</p>
+<p id="footer">run-script — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/developers.html">developers(1)</a></li><li><a href="../doc/install.html">install(1)</a></li></ul>
</div>
-<p id="footer">scripts — npm@1.1.36</p>
+<p id="footer">scripts — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/view.html">view(1)</a></li></ul>
</div>
-<p id="footer">search — npm@1.1.36</p>
+<p id="footer">search — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/json.html">json(1)</a></li></ul>
</div>
-<p id="footer">semver — npm@1.1.36</p>
+<p id="footer">semver — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/list.html">list(1)</a></li></ul>
</div>
-<p id="footer">shrinkwrap — npm@1.1.36</p>
+<p id="footer">shrinkwrap — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/view.html">view(1)</a></li><li><a href="../doc/whoami.html">whoami(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li></ul>
</div>
-<p id="footer">star — npm@1.1.36</p>
+<p id="footer">star — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">start — npm@1.1.36</p>
+<p id="footer">start — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/test.html">test(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li></ul>
</div>
-<p id="footer">stop — npm@1.1.36</p>
+<p id="footer">stop — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/json.html">json(1)</a></li><li>git help submodule</li></ul>
</div>
-<p id="footer">submodule — npm@1.1.36</p>
+<p id="footer">submodule — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">tag — npm@1.1.36</p>
+<p id="footer">tag — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/run-script.html">run-script(1)</a></li><li><a href="../doc/scripts.html">scripts(1)</a></li><li><a href="../doc/start.html">start(1)</a></li><li><a href="../doc/restart.html">restart(1)</a></li><li><a href="../doc/stop.html">stop(1)</a></li></ul>
</div>
-<p id="footer">test — npm@1.1.36</p>
+<p id="footer">test — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/prune.html">prune(1)</a></li><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/config.html">config(1)</a></li></ul>
</div>
-<p id="footer">uninstall — npm@1.1.36</p>
+<p id="footer">uninstall — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/deprecate.html">deprecate(1)</a></li><li><a href="../doc/publish.html">publish(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li><li><a href="../doc/owner.html">owner(1)</a></li></ul>
</div>
-<p id="footer">unpublish — npm@1.1.36</p>
+<p id="footer">unpublish — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/install.html">install(1)</a></li><li><a href="../doc/outdated.html">outdated(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/folders.html">folders(1)</a></li><li><a href="../doc/list.html">list(1)</a></li></ul>
</div>
-<p id="footer">update — npm@1.1.36</p>
+<p id="footer">update — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/init.html">init(1)</a></li><li><a href="../doc/json.html">json(1)</a></li><li><a href="../doc/semver.html">semver(1)</a></li></ul>
</div>
-<p id="footer">version — npm@1.1.36</p>
+<p id="footer">version — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/search.html">search(1)</a></li><li><a href="../doc/registry.html">registry(1)</a></li><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/docs.html">docs(1)</a></li></ul>
</div>
-<p id="footer">view — npm@1.1.36</p>
+<p id="footer">view — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
<ul><li><a href="../doc/config.html">config(1)</a></li><li><a href="../doc/adduser.html">adduser(1)</a></li></ul>
</div>
-<p id="footer">whoami — npm@1.1.36</p>
+<p id="footer">whoami — npm@1.1.39</p>
<script>
;(function () {
var wrapper = document.getElementById("wrapper")
, {name:name, range:range, hasData:!!data})
engineFilter(data)
- if (npm.config.get("registry")) return next_()
-
- cachedFilter(data, range, function (er) {
- if (er) return cb(er)
- if (Object.keys(data.versions).length === 0) {
- return cb(new Error( "Can't fetch, and not cached: "
- + data.name + "@" + range))
- }
- next_()
- })
- }
-
- function next_ () {
log.silly("addNameRange", "versions"
, [data.name, Object.keys(data.versions || {})])
}
}
-// filter the versions down based on what's already in cache.
-function cachedFilter (data, range, cb) {
- log.silly("cachedFilter", data.name)
- ls_(data.name, 1, function (er, files) {
- if (er) {
- log.error("cachedFilter", "Not in cache, can't fetch", data.name)
- return cb(er)
- }
- files = files.map(function (f) {
- return path.basename(f.replace(/(\\|\/)$/, ""))
- }).filter(function (f) {
- return semver.valid(f) && semver.satisfies(f, range)
- })
-
- if (files.length === 0) {
- return cb(new Error("Not in cache, can't fetch: "+data.name+"@"+range))
- }
-
- log.silly("cached", [data.name, files])
- Object.keys(data.versions).forEach(function (v) {
- if (files.indexOf(v) === -1) delete data.versions[v]
- })
-
- if (Object.keys(data.versions).length === 0) {
- log.error("cachedFilter", "Not in cache, can't fetch", data.name)
- return cb(new Error("Not in cache, can't fetch: "+data.name+"@"+range))
- }
-
- log.silly("filtered", [data.name, Object.keys(data.versions)])
- cb(null, data)
- })
-}
-
function installTargetsError (requested, data) {
var targets = Object.keys(data["dist-tags"]).filter(function (f) {
return (data.versions || {}).hasOwnProperty(f)
, log = require("npmlog")
, path = require("path")
, archy = require("archy")
+ , semver = require("semver")
ls.usage = "npm ls"
+ls.completion = require("./utils/completion/installed-deep.js")
+
function ls (args, silent, cb) {
if (typeof cb !== "function") cb = silent, silent = false
- if (args.length) {
- // TODO: it would actually be nice to maybe show the locally
- // installed packages only matching the argument names.
- log.warn("ls doesn't take positional args. Try the 'search' command")
- }
-
var dir = path.resolve(npm.dir, "..")
+ // npm ls 'foo@~1.3' bar 'baz@<2'
+ if (!args) args = []
+ else args = args.map(function (a) {
+ var nv = a.split("@")
+ , name = nv.shift()
+ , ver = semver.validRange(nv.join("@")) || ""
+
+ return [ name, ver ]
+ })
+
readInstalled(dir, npm.config.get("depth"), function (er, data) {
- var lite = getLite(bfsify(data))
+ var bfs = bfsify(data, args)
+ , lite = getLite(bfs)
if (er || silent) return cb(er, data, lite)
var long = npm.config.get("long")
, out
if (json) {
var seen = []
- var d = long ? bfsify(data) : lite
+ var d = long ? bfs : lite
// the raw data can be circular
out = JSON.stringify(d, function (k, o) {
if (typeof o === "object") {
return o
}, 2)
} else if (npm.config.get("parseable")) {
- out = makeParseable(bfsify(data), long, dir)
+ out = makeParseable(bfs, long, dir)
} else if (data) {
- out = makeArchy(bfsify(data), long, dir)
+ out = makeArchy(bfs, long, dir)
}
output.write(out, function (er) { cb(er, data, lite) })
})
}
+// only include
+function filter (data, args) {
+
+}
+
function alphasort (a, b) {
a = a.toLowerCase()
b = b.toLowerCase()
return lite
}
-function bfsify (root, current, queue, seen) {
+function bfsify (root, args, current, queue, seen) {
// walk over the data, and turn it from this:
// +-- a
// | `-- b
// +-- a
// `-- b
// which looks nicer
+ args = args || []
current = current || root
queue = queue || []
seen = seen || [root]
queue.push(dep)
seen.push(dep)
})
- if (!queue.length) return root
- return bfsify(root, queue.shift(), queue, seen)
+
+ if (!queue.length) {
+ // if there were args, then only show the paths to found nodes.
+ return filterFound(root, args)
+ }
+ return bfsify(root, args, queue.shift(), queue, seen)
}
+function filterFound (root, args) {
+ if (!args.length) return root
+ var deps = root.dependencies
+ if (deps) Object.keys(deps).forEach(function (d) {
+ var dep = filterFound(deps[d], args)
+
+ // see if this one itself matches
+ var found = false
+ for (var i = 0; !found && i < args.length; i ++) {
+ if (d === args[i][0]) {
+ found = semver.satisfies(dep.version, args[i][1])
+ }
+ }
+ // included explicitly
+ if (found) dep._found = true
+ // included because a child was included
+ if (dep._found && !root._found) root._found = 1
+ // not included
+ if (!dep._found) delete deps[d]
+ })
+ if (!root._found) root._found = false
+ return root
+}
function makeArchy (data, long, dir) {
var out = makeArchy_(data, long, dir, 0)
var out = {}
// the top level is a bit special.
- out.label = data._id ? data._id + " " : ""
- if (data.link) out.label += "-> " + data.link
+ out.label = data._id || ""
+ if (data._found === true && data._id) {
+ out.label = "\033[33;40m" + out.label.trim() + "\033[m "
+ }
+ if (data.link) out.label += " -> " + data.link
if (data.invalid) {
if (data.realName !== data.name) out.label += " ("+data.realName+")"
.sort(alphasort).map(function (d) {
return makeParseable(data.dependencies[d], long, dir, depth + 1, data, d)
}))
+ .filter(function (x) { return x })
.join("\n")
}
function makeParseable_ (data, long, dir, depth, parent, d) {
+ if (data.hasOwnProperty("_found") && data._found !== true) return ""
+
if (typeof data === "string") {
if (data.depth < npm.config.get("depth")) {
var p = parent.link || parent.path
// at this point the configs are all set.
// go ahead and spin up the registry client.
+ var token
+ try { token = JSON.parse(npm.config.get("_token")) }
+ catch (er) { token = null }
+
npm.registry = new RegClient(
{ registry: npm.config.get("registry")
, cache: npm.config.get("cache")
, auth: npm.config.get("_auth")
+ , token: token
, alwaysAuth: npm.config.get("always-auth")
, email: npm.config.get("email")
, proxy: npm.config.get("proxy")
, retryFactor: npm.config.get("fetch-retry-factor")
, retryMinTimeout: npm.config.get("fetch-retry-mintimeout")
, retryMaxTimeout: npm.config.get("fetch-retry-maxtimeout")
+ , cacheMin: npm.config.get("cache-min")
+ , cacheMax: npm.config.get("cache-max")
})
+ // save the token cookie in the config file
+ if (npm.registry.couchLogin) {
+ npm.registry.couchLogin.tokenSet = function (tok, cb) {
+ ini.set("_token", JSON.stringify(tok), "user")
+ // ignore save error. best effort.
+ ini.save("user", function () {})
+ }
+ }
+
var umask = parseInt(conf.umask, 8)
npm.modes = { exec: 0777 & (~umask)
, file: 0666 & (~umask)
function test (args, cb) {
testCmd(args, function (er) {
if (!er) return cb()
- return cb("Test failed. See above for more details.")
+ if (er.code === "ELIFECYCLE") {
+ return cb("Test failed. See above for more details.")
+ }
+ return cb(er)
})
}
var opts = { url: remote
, proxy: proxy
- , follow: false
, strictSSL: npm.config.get("strict-ssl")
, ca: remote.host === regHost ? npm.config.get("ca") : undefined
, headers: { "user-agent": npm.config.get("user-agent") }}
.SH "SYNOPSIS"
.
.nf
-npm list
-npm ls
-npm la
-npm ll
+npm list [<pkg> \.\.\.]
+npm ls [<pkg> \.\.\.]
+npm la [<pkg> \.\.\.]
+npm ll [<pkg> \.\.\.]
.
.fi
.
installed, as well as their dependencies, in a tree\-structure\.
.
.P
-It does not take positional arguments, though you may set config flags
-like with any other command, such as \fB\-g\fR to list global packages\.
+Positional arguments are \fBname@version\-range\fR identifiers, which will
+limit the results to only the paths to the packages named\. Note that
+nested packages will \fIalso\fR show the paths to the specified packages\.
+For example, running \fBnpm ls promzard\fR in npm\'s source tree will show:
+.
+.IP "" 4
+.
+.nf
+npm@1.1.39 /path/to/npm
+└─┬ init\-package\-json@0\.0\.4
+ └── promzard@0\.1\.5
+.
+.fi
+.
+.IP "" 0
.
.P
-It will print out extraneous, missing, and invalid packages\.
+It will show print out extraneous, missing, and invalid packages\.
.
.P
When run as \fBll\fR or \fBla\fR, it shows extended information by default\.
.fi
.
.SH "VERSION"
-1.1.36
+1.1.39
.
.SH "DESCRIPTION"
npm is the package manager for the Node JavaScript platform\. It puts
.fi
.
.SH "VERSION"
-1.1.36
+1.1.39
.
.SH "DESCRIPTION"
This is the API documentation for npm\.
--- /dev/null
+# couch-login
+
+This module lets you log into couchdb to get a session token, then make
+requests using that session. It is basically just a thin wrapper around
+[@mikeal's request module](https://github.com/mikeal/request).
+
+This is handy if you want a user to take actions in a couchdb database
+on behalf of a user, without having to store their couchdb username and
+password anywhere. (You do need to store the AuthSession token
+somewhere, though.)
+
+## Usage
+
+```javascript
+var CouchLogin = require('couch-login')
+
+// Nothing about this module is http-server specific of course.
+// You could also use it to do authenticated requests against
+// a couchdb using sessions and storing the token somewhere else.
+
+http.createServer(function (req, res) {
+ var couch = new CouchLogin('http://my-couch.iriscouch.com:5984/')
+
+ // .. look up the token in the user's session or whatever ..
+ // Look at couch.decorate(req, res) for more on doing that
+ // automatically, below.
+
+ if (sessionToken) {
+ // this user already logged in.
+ couch.token = sessionToken
+
+ // now we can do things on their behalf, like:
+ // 1. View their session info.
+ // like doing request.get({ uri: couch + '/_session', ... })
+ // but with the cookie and whatnot
+
+ couch.get('/_session', function (er, resp, data) {
+ // er = some kind of communication error.
+ // resp = response object from the couchdb request.
+ // data = parsed JSON response body.
+ if (er || resp.statusCode !== 200) {
+ res.statusCode = resp.statusCode || 403
+ return res.end('Invalid login or something')
+ }
+
+ // now we have the session info, we know who this user is.
+ // hitting couchdb for this on every request is kinda costly,
+ // so maybe you should store the username wherever you're storing
+ // the sessionToken. RedSess is a good util for this, if you're
+ // into redis. And if you're not into redis, you're crazy,
+ // because it is awesome.
+
+ // now let's get the user record.
+ // note that this will 404 for anyone other than the user,
+ // unless they're a server admin.
+ couch.get('/_users/org.couchdb.user:' + data.userCtx.name, etc)
+
+ // PUTs and DELETEs will also use their session, of course, so
+ // your validate_doc_update's will see their info in userCtx
+ })
+
+ } else {
+ // don't have a sessionToken.
+ // get a username and password from the post body or something.
+ // maybe redirect to a /login page or something to ask for that.
+ var login = { name: name, password: password }
+ couch.login(login, function (er, resp, data) {
+ // again, er is an error, resp is the response obj, data is the json
+ if (er || resp.statusCode !== 200) {
+ res.statusCode = resp.statusCode || 403
+ return res.end('Invalid login or something')
+ }
+
+ // the data is something like
+ // {"ok":true,"name":"testuser","roles":[]}
+ // and couch.token is the token you'll need to save somewhere.
+
+ // at this point, you can start making authenticated requests to
+ // couchdb, or save data in their session, or do whatever it is
+ // that you need to do.
+
+ res.statusCode = 200
+ res.write("Who's got two thumbs and just logged you into couch?\n")
+ setTimeout(function () {
+ res.end("THIS GUY!")
+ }, 500)
+ })
+ }
+})
+```
+
+## Class: CouchLogin
+### new CouchLogin(couchdbUrl, token)
+
+Create a new CouchLogin object bound to the couchdb url.
+
+In addition to these, the `get`, `post`, `put`, and `del` methods all
+proxy to the associated method on [request](https://github.com/mikeal/request).
+
+However, as you'll note in the example above, only the pathname portion
+of the url is required. Urls will be appended to the couchdb url passed
+into the constructor.
+
+If you have to talk to more than one couchdb, then you'll need more than
+one CouchLogin object, for somewhat obvious reasons.
+
+All callbacks get called with the following arguments, which are exactly
+identical to the arguments passed to a `request` callback.
+
+* `er` {Error | null} Set if a communication error happens.
+* `resp` {HTTP Response} The response from the request to couchdb
+* `data` {Object} The parsed JSON data from couch
+
+If the token is the string "anonymous", then it will not attempt to log
+in before making requests. If the token is not "anonymous", then it
+must be an object with the appropriate fields.
+
+### couch.token
+
+* {Object}
+
+An object representing the couchdb session token. (Basically just a
+cookie and a timeout.)
+
+If the token has already timed out, then setting it will have no effect.
+
+### couch.tokenSet
+
+If set, this method is called whenever the token is saved.
+
+For example, you could assign a function to this method to save the
+token into a redis session, a cookie, or in some other database.
+
+Takes a callback which should be called when the token is saved.
+
+### couch.tokenGet
+
+If set, this method is called to look up the token on demand.
+
+The inverse of couch.tokenSet. Takes a callback which is called with
+the `cb(er || null, token)`.
+
+### couch.tokenDel
+
+If set, this method is called to delete the token when it should be
+discarded.
+
+Related to tokenGet and tokenSet. Takes a callback which should be
+called when the token is deleted.
+
+### couch.anonymous()
+
+Return a new CouchLogin object that points at the same couchdb server,
+but doesn't try to log in before making requests.
+
+This is handy for situations where the user is not logged in at the
+moment, but a request needs to be made anyway, and does not require
+authorization.
+
+### couch.login(auth, callback)
+
+* `auth` {Object} The login details
+ * `name` {String}
+ * `password` {String}
+* `callback` {Function}
+
+When the callback is called, the `couch.token` will already have been
+set (assuming it worked!), so subsequent requests will be done as that
+user.
+
+### couch.get(path, callback)
+
+GET the supplied path from the couchdb using the credentials on the
+token.
+
+Fails if the token is invalid or expired.
+
+### couch.del(path, callback)
+
+DELETE the supplied path from the couchdb using the credentials on the
+token.
+
+Fails if the token is invalid or expired.
+
+### couch.post(path, data, callback)
+
+POST the data to the supplied path in the couchdb, using the credentials
+on the token.
+
+Fails if the token is invalid or expired.
+
+### couch.put(path, data, callback)
+
+PUT the data to the supplied path in the couchdb, using the credentials
+on the token.
+
+Fails if the token is invalid or expired.
+
+### couch.changePass(newAuth, callback)
+
+Must already be logged in. Updates the `_users` document with new salt
+and hash, and re-logs in with the new credentials. Callback is called
+with the same arguments as login, or the first step of the process that
+failed.
+
+### couch.signup(userData, callback)
+
+Create a new user account. The userData must contain at least a `name`
+and `password` field. Any additional data will be copied to the user
+record. The `_id`, `name`, `roles`, `type`, `password_sha`, `salt`, and
+`date` fields are generated.
+
+Also signs in as the newly created user, on successful account creation.
+
+### couch.deleteAccount(name, callback)
+
+Deletes a user account. If not logged in as the user, or a server
+admin, then the request will fail.
+
+Note that this immediately invalidates any session tokens for the
+deleted user account. If you are deleting the user's record, then you
+ought to follow this with `couch.logout(callback)` so that it won't try
+to re-use the invalid session.
+
+### couch.logout(callback)
+
+Delete the session out of couchdb. This makes the token permanently
+invalid, and deletes it.
+
+### couch.decorate(req, res)
+
+Set up `req.couch` and `res.couch` as references to this couch login
+instance.
+
+Additionall, if `req.session` or `res.session` is set, then it'll call
+`session.get('couch_token', cb)` as the tokenGet method,
+`session.set('couch_token', token, cb)` as the tokenSet method, and
+`session.del('couch_token', cb)` as the tokenDel method.
+
+This works really nice with
+[RedSess](https://github.com/isaacs/redsess).
--- /dev/null
+var request = require('request')
+, url = require('url')
+, crypto = require('crypto')
+
+module.exports = CouchLogin
+
+function CouchLogin (couch, tok) {
+ if (!(this instanceof CouchLogin)) {
+ return new CouchLogin(couch)
+ }
+
+ if (!couch) throw new Error(
+ "Need to pass a couch url to CouchLogin constructor")
+
+ // having auth completely defeats the purpose
+ couch = url.parse(couch)
+ delete couch.auth
+
+ if (tok === 'anonymous') tok = NaN
+ this.token = tok
+ this.couch = url.format(couch)
+}
+
+CouchLogin.prototype =
+{ get: makeReq('GET')
+, del: makeReq('DELETE')
+, put: makeReq('PUT', true)
+, post: makeReq('POST', true)
+, login: login
+, logout: logout
+, decorate: decorate
+, changePass: changePass
+, signup: signup
+, deleteAccount: deleteAccount
+, anon: anon
+, anonymous: anon
+, valid: valid
+}
+
+Object.defineProperty(CouchLogin.prototype, 'constructor',
+ { value: CouchLogin, enumerable: false })
+
+function decorate (req, res) {
+ req.couch = res.couch = this
+
+ // backed by some sort of set(k,v,cb), get(k,cb) session storage.
+ var session = req.session || res.session || null
+ if (session) {
+ this.tokenGet = function (cb) {
+ session.get('couch_token', cb)
+ }
+
+ // don't worry about it failing. it'll just mean a login next time.
+ this.tokenSet = function (tok, cb) {
+ session.set('couch_token', tok, cb || function () {})
+ }
+
+ this.tokenDel = function (cb) {
+ session.del('couch_token', cb || function () {})
+ }
+ }
+
+ return this
+}
+
+function anon () {
+ return new CouchLogin(this.couch, NaN)
+}
+
+function makeReq (meth, body, f) { return function madeReq (p, d, cb) {
+ f = f || (this.token !== this.token)
+ if (!f && !valid(this.token)) {
+ // lazily get the token.
+ if (this.tokenGet) return this.tokenGet(function (er, tok) {
+ if (er || !valid(tok)) {
+ if (!body) cb = d, d = null
+ return cb(new Error('auth token expired or invalid'))
+ }
+ this.token = tok
+ return madeReq.call(this, p, d, cb)
+ }.bind(this))
+
+ // no getter, no token, no business.
+ return process.nextTick(function () {
+ if (!body) cb = d, d = null
+ cb(new Error('auth token expired or invalid'))
+ })
+ }
+
+ if (!body) cb = d, d = null
+
+ var h = {}
+ , u = url.resolve(this.couch, p)
+ , req = { uri: u, headers: h, json: true, body: d, method: meth }
+
+ if (this.token) {
+ h.cookie = 'AuthSession=' + this.token.AuthSession
+ }
+
+ request(req, function (er, res, data) {
+ // update cookie.
+ if (er || res.statusCode !== 200) return cb(er, res, data)
+ addToken.call(this, res)
+ return cb(er, res, data)
+ }.bind(this))
+}}
+
+function login (auth, cb) {
+ var a = { name: auth.name, password: auth.password }
+ makeReq('post', true, true).call(this, '/_session', a, cb)
+}
+
+function changePass (auth, cb) {
+ if (!auth.name || !auth.password) return cb(new Error('invalid auth'))
+
+ var u = '/_users/org.couchdb.user:' + auth.name
+ this.get(u, function (er, res, data) {
+ if (er || res.statusCode !== 200) return cb(er, res, data)
+
+ // copy any other keys we're setting here.
+ // note that name, password_sha, salt, and date
+ // are all set explicitly below.
+ Object.keys(auth).filter(function (k) {
+ return k.charAt(0) !== '_'
+ && k !== 'password'
+ && k !== 'password_sha'
+ && k !== 'salt'
+ }).forEach(function (k) {
+ data[k] = auth[k]
+ })
+
+ var newSalt = crypto.randomBytes(30).toString('hex')
+ , newPass = auth.password
+ , newSha = sha(newPass + newSalt)
+
+ data.password_sha = newSha
+ data.salt = newSalt
+ data.date = new Date().toISOString()
+
+ this.put(u + '?rev=' + data._rev, data, function (er, res, data) {
+ if (er || res.statusCode >= 400) return cb(er, res, data)
+ this.login(auth, cb)
+ }.bind(this))
+ }.bind(this))
+}
+
+// They said that there should probably be a warning before
+// deleting the user's whole account, so here it is:
+//
+// WATCH OUT!
+function deleteAccount (name, cb) {
+ var u = '/_users/org.couchdb.user:' + name
+ this.get(u, thenPut.bind(this))
+
+ function thenPut (er, res, data) {
+ if (er || res.statusCode !== 200) {
+ return cb(er, res, data)
+ }
+
+ // user accts can't be just DELETE'd by non-admins
+ // so we take the existing doc and just slap a _deleted
+ // flag on it to fake it. Works the same either way
+ // in couch.
+ data._deleted = true
+ this.put(u + '?rev=' + data._rev, data, cb)
+ }
+}
+
+
+
+function signup (auth, cb) {
+ if (this.token) return this.logout(function (er, res, data) {
+ if (er || res.statusCode !== 200) {
+ return cb(er, res, data)
+ }
+
+ if (this.token) {
+ return cb(new Error('failed to delete token'), res, data)
+ }
+
+ this.signup(auth, cb)
+ }.bind(this))
+
+ // make a new user record.
+ var newSalt = crypto.randomBytes(30).toString('hex')
+ , newSha = sha(auth.password + newSalt)
+ , user = { _id: 'org.couchdb.user:' + auth.name
+ , name: auth.name
+ , roles: []
+ , type: 'user'
+ , password_sha: newSha
+ , salt: newSalt
+ , date: new Date().toISOString() }
+
+ Object.keys(auth).forEach(function (k) {
+ if (k === 'name' || k === 'password') return
+ user[k] = auth[k]
+ })
+
+ var u = '/_users/' + user._id
+ makeReq('put', true, true).call(this, u, user, function (er, res, data) {
+ if (er || res.statusCode >= 400) {
+ return cb(er, res, data)
+ }
+
+ // it worked! log in as that user and get their record
+ this.login(auth, function (er, res, data) {
+ if (er || (res && res.statusCode >= 400) || data && data.error) {
+ return cb(er, res, data)
+ }
+ this.get(u, cb)
+ }.bind(this))
+ }.bind(this))
+}
+
+function addToken (res) {
+ // attach the token, if a new one was provided.
+ var sc = res.headers['set-cookie']
+ if (!sc) return
+ if (!Array.isArray(sc)) sc = [sc]
+
+ sc = sc.filter(function (c) {
+ return c.match(/^AuthSession=/)
+ })[0]
+
+ if (!sc.length) return
+
+ sc = sc.split(/\s*;\s*/).map(function (p) {
+ return p.split('=')
+ }).reduce(function (set, p) {
+ var k = p[0] === 'AuthSession' ? p[0] : p[0].toLowerCase()
+ , v = k === 'expires' ? Date.parse(p[1])
+ : p[1] === '' || p[1] === undefined ? true // HttpOnly
+ : p[1]
+ set[k] = v
+ return set
+ }, {})
+
+ if (sc.hasOwnProperty('max-age')) {
+ var ma = sc['max-age']
+ sc.expires = (ma <= 0) ? 0 : Date.now() + (ma * 1000)
+ delete sc['max-age']
+ }
+
+ this.token = sc
+ if (this.tokenSet) this.tokenSet(this.token)
+}
+
+
+function logout (cb) {
+ if (!this.token && this.tokenGet) {
+ return this.tokenGet(function (er, tok) {
+ if (er || !tok) return cb()
+ this.token = tok
+ this.logout(cb)
+ }.bind(this))
+ }
+
+ if (!valid(this.token)) {
+ this.token = null
+ if (this.tokenDel) this.tokenDel()
+ return process.nextTick(cb)
+ }
+
+ var h = { cookie: 'AuthSession=' + this.token.AuthSession }
+ , u = url.resolve(this.couch, '/_session')
+ , req = { uri: u, headers: h, json: true }
+
+ request.del(req, function (er, res, data) {
+ if (er || res.statusCode !== 200) {
+ return cb(er, res, data)
+ }
+
+ this.token = null
+ if (this.tokenDel) this.tokenDel()
+ cb(er, res, data)
+ }.bind(this))
+}
+
+function valid (token) {
+ var d = token && token.expires
+ return token && token.expires > Date.now()
+}
+
+function sha (s) {
+ return crypto.createHash("sha1").update(s).digest("hex")
+}
--- /dev/null
+{
+ "author": {
+ "name": "Isaac Z. Schlueter",
+ "email": "i@izs.me",
+ "url": "http://blog.izs.me/"
+ },
+ "name": "couch-login",
+ "description": "A module for doing logged-in requests to a couchdb server",
+ "version": "0.1.6",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/isaacs/couch-login.git"
+ },
+ "main": "couch-login.js",
+ "scripts": {
+ "test": "tap test/*.js"
+ },
+ "dependencies": {
+ "request": "~2.9.202"
+ },
+ "devDependencies": {
+ "tap": "~0.2.4"
+ },
+ "readme": "# couch-login\n\nThis module lets you log into couchdb to get a session token, then make\nrequests using that session. It is basically just a thin wrapper around\n[@mikeal's request module](https://github.com/mikeal/request).\n\nThis is handy if you want a user to take actions in a couchdb database\non behalf of a user, without having to store their couchdb username and\npassword anywhere. (You do need to store the AuthSession token\nsomewhere, though.)\n\n## Usage\n\n```javascript\nvar CouchLogin = require('couch-login')\n\n// Nothing about this module is http-server specific of course.\n// You could also use it to do authenticated requests against\n// a couchdb using sessions and storing the token somewhere else.\n\nhttp.createServer(function (req, res) {\n var couch = new CouchLogin('http://my-couch.iriscouch.com:5984/')\n\n // .. look up the token in the user's session or whatever ..\n // Look at couch.decorate(req, res) for more on doing that\n // automatically, below.\n\n if (sessionToken) {\n // this user already logged in.\n couch.token = sessionToken\n\n // now we can do things on their behalf, like:\n // 1. View their session info.\n // like doing request.get({ uri: couch + '/_session', ... })\n // but with the cookie and whatnot\n\n couch.get('/_session', function (er, resp, data) {\n // er = some kind of communication error.\n // resp = response object from the couchdb request.\n // data = parsed JSON response body.\n if (er || resp.statusCode !== 200) {\n res.statusCode = resp.statusCode || 403\n return res.end('Invalid login or something')\n }\n\n // now we have the session info, we know who this user is.\n // hitting couchdb for this on every request is kinda costly,\n // so maybe you should store the username wherever you're storing\n // the sessionToken. RedSess is a good util for this, if you're\n // into redis. And if you're not into redis, you're crazy,\n // because it is awesome.\n\n // now let's get the user record.\n // note that this will 404 for anyone other than the user,\n // unless they're a server admin.\n couch.get('/_users/org.couchdb.user:' + data.userCtx.name, etc)\n\n // PUTs and DELETEs will also use their session, of course, so\n // your validate_doc_update's will see their info in userCtx\n })\n\n } else {\n // don't have a sessionToken.\n // get a username and password from the post body or something.\n // maybe redirect to a /login page or something to ask for that.\n var login = { name: name, password: password }\n couch.login(login, function (er, resp, data) {\n // again, er is an error, resp is the response obj, data is the json\n if (er || resp.statusCode !== 200) {\n res.statusCode = resp.statusCode || 403\n return res.end('Invalid login or something')\n }\n\n // the data is something like\n // {\"ok\":true,\"name\":\"testuser\",\"roles\":[]}\n // and couch.token is the token you'll need to save somewhere.\n\n // at this point, you can start making authenticated requests to\n // couchdb, or save data in their session, or do whatever it is\n // that you need to do.\n\n res.statusCode = 200\n res.write(\"Who's got two thumbs and just logged you into couch?\\n\")\n setTimeout(function () {\n res.end(\"THIS GUY!\")\n }, 500)\n })\n }\n})\n```\n\n## Class: CouchLogin\n### new CouchLogin(couchdbUrl, token)\n\nCreate a new CouchLogin object bound to the couchdb url.\n\nIn addition to these, the `get`, `post`, `put`, and `del` methods all\nproxy to the associated method on [request](https://github.com/mikeal/request).\n\nHowever, as you'll note in the example above, only the pathname portion\nof the url is required. Urls will be appended to the couchdb url passed\ninto the constructor.\n\nIf you have to talk to more than one couchdb, then you'll need more than\none CouchLogin object, for somewhat obvious reasons.\n\nAll callbacks get called with the following arguments, which are exactly\nidentical to the arguments passed to a `request` callback.\n\n* `er` {Error | null} Set if a communication error happens.\n* `resp` {HTTP Response} The response from the request to couchdb\n* `data` {Object} The parsed JSON data from couch\n\nIf the token is the string \"anonymous\", then it will not attempt to log\nin before making requests. If the token is not \"anonymous\", then it\nmust be an object with the appropriate fields.\n\n### couch.token\n\n* {Object}\n\nAn object representing the couchdb session token. (Basically just a\ncookie and a timeout.)\n\nIf the token has already timed out, then setting it will have no effect.\n\n### couch.tokenSet\n\nIf set, this method is called whenever the token is saved.\n\nFor example, you could assign a function to this method to save the\ntoken into a redis session, a cookie, or in some other database.\n\nTakes a callback which should be called when the token is saved.\n\n### couch.tokenGet\n\nIf set, this method is called to look up the token on demand.\n\nThe inverse of couch.tokenSet. Takes a callback which is called with\nthe `cb(er || null, token)`.\n\n### couch.tokenDel\n\nIf set, this method is called to delete the token when it should be\ndiscarded.\n\nRelated to tokenGet and tokenSet. Takes a callback which should be\ncalled when the token is deleted.\n\n### couch.anonymous()\n\nReturn a new CouchLogin object that points at the same couchdb server,\nbut doesn't try to log in before making requests.\n\nThis is handy for situations where the user is not logged in at the\nmoment, but a request needs to be made anyway, and does not require\nauthorization.\n\n### couch.login(auth, callback)\n\n* `auth` {Object} The login details\n * `name` {String}\n * `password` {String}\n* `callback` {Function}\n\nWhen the callback is called, the `couch.token` will already have been\nset (assuming it worked!), so subsequent requests will be done as that\nuser.\n\n### couch.get(path, callback)\n\nGET the supplied path from the couchdb using the credentials on the\ntoken.\n\nFails if the token is invalid or expired.\n\n### couch.del(path, callback)\n\nDELETE the supplied path from the couchdb using the credentials on the\ntoken.\n\nFails if the token is invalid or expired.\n\n### couch.post(path, data, callback)\n\nPOST the data to the supplied path in the couchdb, using the credentials\non the token.\n\nFails if the token is invalid or expired.\n\n### couch.put(path, data, callback)\n\nPUT the data to the supplied path in the couchdb, using the credentials\non the token.\n\nFails if the token is invalid or expired.\n\n### couch.changePass(newAuth, callback)\n\nMust already be logged in. Updates the `_users` document with new salt\nand hash, and re-logs in with the new credentials. Callback is called\nwith the same arguments as login, or the first step of the process that\nfailed.\n\n### couch.signup(userData, callback)\n\nCreate a new user account. The userData must contain at least a `name`\nand `password` field. Any additional data will be copied to the user\nrecord. The `_id`, `name`, `roles`, `type`, `password_sha`, `salt`, and\n`date` fields are generated.\n\nAlso signs in as the newly created user, on successful account creation.\n\n### couch.deleteAccount(name, callback)\n\nDeletes a user account. If not logged in as the user, or a server\nadmin, then the request will fail.\n\nNote that this immediately invalidates any session tokens for the\ndeleted user account. If you are deleting the user's record, then you\nought to follow this with `couch.logout(callback)` so that it won't try\nto re-use the invalid session.\n\n### couch.logout(callback)\n\nDelete the session out of couchdb. This makes the token permanently\ninvalid, and deletes it.\n\n### couch.decorate(req, res)\n\nSet up `req.couch` and `res.couch` as references to this couch login\ninstance.\n\nAdditionall, if `req.session` or `res.session` is set, then it'll call\n`session.get('couch_token', cb)` as the tokenGet method,\n`session.set('couch_token', token, cb)` as the tokenSet method, and\n`session.del('couch_token', cb)` as the tokenDel method.\n\nThis works really nice with\n[RedSess](https://github.com/isaacs/redsess).\n",
+ "_id": "couch-login@0.1.6",
+ "_from": "couch-login@~0.1.6"
+}
}
-// lstat on windows, missing from early 0.5 versions
-// replacing with stat isn't quite perfect, but good enough to get by.
-if (process.platform === "win32" && !process.binding("fs").lstat) {
- fs.lstat = fs.stat
- fs.lstatSync = fs.statSync
-}
-
-
// lutimes implementation, or no-op
if (!fs.lutimes) {
if (constants.hasOwnProperty("O_SYMLINK")) {
}
+// if lchmod/lchown do not exist, then make them no-ops
+if (!fs.lchmod) {
+ fs.lchmod = function (path, mode, cb) {
+ process.nextTick(cb)
+ }
+ fs.lchmodSync = function () {}
+}
+if (!fs.lchown) {
+ fs.lchown = function (path, uid, gid, cb) {
+ process.nextTick(cb)
+ }
+ fs.lchownSync = function () {}
+}
+
+
+
// on Windows, A/V software can lock the directory, causing this
// to fail with an EACCES or EPERM if the directory contains newly
},
"name": "graceful-fs",
"description": "fs monkey-patching to avoid EMFILE and other problems",
- "version": "1.1.8",
+ "version": "1.1.9",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/node-graceful-fs.git"
"node": ">=0.4.0"
},
"devDependencies": {},
- "_npmUser": {
- "name": "isaacs",
- "email": "i@izs.me"
- },
- "_id": "graceful-fs@1.1.8",
- "dependencies": {},
- "optionalDependencies": {},
- "_engineSupported": true,
- "_npmVersion": "1.1.10",
- "_nodeVersion": "v0.7.7-pre",
- "_defaultsLoaded": true,
+ "readme": "Just like node's `fs` module, but it does an incremental back-off when\nEMFILE is encountered.\n\nUseful in asynchronous situations where one needs to try to open lots\nand lots of files.\n",
+ "_id": "graceful-fs@1.1.9",
"_from": "graceful-fs@~1.1.1"
}
}
log.info('it worked if it ends with', 'ok')
+log.verbose('cli', process.argv)
log.info('using', 'node-gyp@%s', prog.version)
-log.info('using', 'node@%s', process.versions.node)
+log.info('using', 'node@%s | %s | %s', process.versions.node, process.platform, process.arch)
/**
if (prog.todo.length === 0) {
// done!
completed = true
- log.info('done', 'ok')
+ log.info('ok')
return
}
var command = prog.todo.shift()
*/
var fs = require('graceful-fs')
+ , rm = require('rimraf')
, path = require('path')
, glob = require('glob')
, log = require('npmlog')
, which = require('which')
, mkdirp = require('mkdirp')
, win = process.platform == 'win32'
- , openbsd = process.platform == 'openbsd'
exports.usage = 'Invokes `' + (win ? 'msbuild' : 'make') + '` and builds the module'
function build (gyp, argv, callback) {
- var makeCommand = openbsd ? 'gmake' : 'make'
+ var makeCommand = gyp.opts.make || process.env.MAKE
+ || (process.platform.indexOf('bsd') != -1 ? 'gmake' : 'make')
var command = win ? 'msbuild' : makeCommand
, buildDir = path.resolve('build')
, configPath = path.resolve(buildDir, 'config.gypi')
if (signal) {
return callback(new Error('`' + command + '` got signal: ' + signal))
}
+ //symlinkNodeBinding()
callback()
}
+ function symlinkNodeBinding () {
+ var buildDir = path.join('build', buildType, '*.node')
+ log.verbose('globbing for files', buildDir)
+ glob(buildDir, function (err, nodeFiles) {
+ if (err) return callback(err)
+ function link () {
+ var file = nodeFiles.shift()
+ if (!file) {
+ // no more files to link... done!
+ return callback()
+ }
+ var dest = path.join('build', path.basename(file))
+ log.info('symlink', 'creating link %j pointing to %j', file, dest)
+ var rel = path.relative('build', file)
+ log.verbose('symlink data', rel)
+ fs.symlink(rel, dest, 'file', function (err) {
+ if (err) {
+ if (err.code === 'EEXIST') {
+ log.verbose('destination already exists; deleting', dest)
+ rm(dest, function (err) {
+ if (err) return callback(err)
+ log.verbose('delete successful; trying symlink again')
+ nodeFiles.unshift(file)
+ link()
+ })
+ } else {
+ callback(err)
+ }
+ return
+ }
+ // process the next file, if any
+ link()
+ })
+ }
+ // start linking
+ link()
+ })
+ }
+
}
// We're gonna glob C:\python2*
function guessPython () {
log.verbose('could not find "' + python + '". guessing location')
- var rootDir = process.env.HOMEDRIVE || process.env.SystemDrive || 'C:\\'
+ var rootDir = process.env.SystemDrive || 'C:\\'
if (rootDir[rootDir.length - 1] !== '\\') {
rootDir += '\\'
}
if (err) {
return callback(err)
}
+ log.verbose('check python version', '`python --version` returned: %j', stderr)
var version = stderr.trim().replace(/[^\d\.]+/g, '')
- if (semver.lt(version, '3.0.0')) {
+ var numDots = 0
+ version.replace(/\./g, function () {
+ numDots++
+ })
+ while (numDots < 2) {
+ version += '.0'
+ numDots++
+ }
+ log.verbose('check python version', 'using version %j to check', version)
+ if (semver.gte(version, '2.5.0') && semver.lt(version, '3.0.0')) {
getNodeDir()
} else {
- failPython3()
+ failPythonVersion(version)
}
})
}
+ '", you can set the PYTHON env variable.'))
}
- function failPython3 () {
+ function failPythonVersion (badVersion) {
callback(new Error('Python executable "' + python
- + '" is Python 3, which is not supported.\n'
- + 'You can set the PYTHON env variable to point to a Python 2 interpreter.'))
+ + '" is v' + badVersion + ', which is not supported by gyp.\n'
+ + 'You can pass the --python switch to point to Python >= v2.5.0 & < 3.0.0.'))
}
function getNodeDir () {
*/
var fs = require('graceful-fs')
+ , osenv = require('osenv')
, tar = require('tar')
, rm = require('rimraf')
, path = require('path')
// Determine which node dev files version we are installing
var versionStr = argv[0] || gyp.opts.target || process.version
- log.verbose('install', 'input version string', versionStr)
+ log.verbose('install', 'input version string %j', versionStr)
// parse the version to normalize and ensure it's valid
var version = semver.parse(versionStr)
return callback(new Error('Minimum target version is `0.6.0` or greater. Got: ' + versionStr))
}
- // 0.x.y-pre versions are not published yet. Use previous release.
+ // 0.x.y-pre versions are not published yet and cannot be installed. Bail.
if (version[5] === '-pre') {
- version[3] = +version[3] - 1
- version[5] = null
- log.verbose('-pre version detected, adjusting patch version')
+ log.verbose('detected "pre" node version', versionStr)
+ if (gyp.opts.nodedir) {
+ log.verbose('--nodedir flag was passed; skipping install', gyp.opts.nodedir)
+ callback()
+ } else {
+ callback(new Error('"pre" versions of node cannot be installed, use the --nodedir flag instead'))
+ }
+ return
}
// flatten version into String
go()
}
- function download (url, onError) {
+ function download (url) {
log.http('GET', url)
var requestOpts = {
uri: url
- , onResponse: true
}
// basic support for a proxy server
log.verbose('proxy', proxyUrl)
requestOpts.proxy = proxyUrl
}
- var req = request(requestOpts, onError)
+ var req = request(requestOpts)
req.on('response', function (res) {
log.http(res.statusCode, url)
})
function go () {
- log.verbose('ensuring nodedir is created', devDir)
+ log.verbose('ensuring nodedir is created', devDir)
- // first create the dir for the node dev files
- mkdir(devDir, function (err, created) {
- if (err) return cb(err)
-
- if (created) {
- log.verbose('created nodedir', created)
- }
+ // first create the dir for the node dev files
+ mkdir(devDir, function (err, created) {
+ if (err) {
+ if (err.code == 'EACCES') {
+ // this EACCES fallback is a workaround for npm's `sudo` behavior, where
+ // it drops the permissions before invoking any child processes (like
+ // node-gyp). So what happens is the "nobody" user doesn't have
+ // permission to create the dev dir. As a fallback, make the tmpdir() be
+ // the dev dir for this installation. This is not ideal, but at least
+ // the compilation will succeed...
+ gyp.devDir = path.resolve(osenv.tmpdir(), '.node-gyp')
+ log.warn(err.code, 'user "%s" does not have permission to create dev dir "%s"', osenv.user(), devDir)
+ log.warn(err.code, 'attempting to reinstall using temporary dev dir "%s"', gyp.devDir)
+ gyp.commands.install(argv, cb)
+ } else {
+ cb(err)
+ }
+ return
+ }
- // now download the node tarball
- var tarballUrl = distUrl + '/v' + version + '/node-v' + version + '.tar.gz'
- , badDownload = false
- , extractCount = 0
- , gunzip = zlib.createGunzip()
- , extracter = tar.Extract({ path: devDir, strip: 1, filter: isValid })
-
- // checks if a file to be extracted from the tarball is valid.
- // only .h header files and the gyp files get extracted
- function isValid () {
- var name = this.path.substring(devDir.length + 1)
- var isValid = valid(name)
- if (name === '' && this.type === 'Directory') {
- // the first directory entry is ok
- return true
+ if (created) {
+ log.verbose('created nodedir', created)
}
- if (isValid) {
- log.verbose('extracted file from tarball', name)
- extractCount++
- } else {
- // invalid
- log.silly('ignoring from tarball', name)
+
+ // now download the node tarball
+ var tarballUrl = distUrl + '/v' + version + '/node-v' + version + '.tar.gz'
+ , badDownload = false
+ , extractCount = 0
+ , gunzip = zlib.createGunzip()
+ , extracter = tar.Extract({ path: devDir, strip: 1, filter: isValid })
+
+ // checks if a file to be extracted from the tarball is valid.
+ // only .h header files and the gyp files get extracted
+ function isValid () {
+ var name = this.path.substring(devDir.length + 1)
+ var isValid = valid(name)
+ if (name === '' && this.type === 'Directory') {
+ // the first directory entry is ok
+ return true
+ }
+ if (isValid) {
+ log.verbose('extracted file from tarball', name)
+ extractCount++
+ } else {
+ // invalid
+ log.silly('ignoring from tarball', name)
+ }
+ return isValid
}
- return isValid
- }
- gunzip.on('error', cb)
- extracter.on('error', cb)
- extracter.on('end', afterTarball)
+ gunzip.on('error', cb)
+ extracter.on('error', cb)
+ extracter.on('end', afterTarball)
- // download the tarball, gunzip and extract!
- var req = download(tarballUrl, downloadError)
- .pipe(gunzip)
- .pipe(extracter)
+ // download the tarball, gunzip and extract!
+ var req = download(tarballUrl)
- // something went wrong downloading the tarball?
- function downloadError (err, res) {
- if (err || res.statusCode != 200) {
+ // something went wrong downloading the tarball?
+ req.on('error', function (err) {
badDownload = true
- cb(err || new Error(res.statusCode + ' status code downloading tarball'))
- }
- }
+ cb(err)
+ })
- // invoked after the tarball has finished being extracted
- function afterTarball () {
- if (badDownload) return
- if (extractCount === 0) {
- return cb(new Error('There was a fatal problem while downloading/extracting the tarball'))
- }
- log.verbose('tarball', 'done parsing tarball')
- var async = 0
+ req.on('close', function () {
+ if (extractCount === 0) {
+ cb(new Error('Connection closed while downloading tarball file'))
+ }
+ })
- if (isLegacy) {
- // copy over the files from the `legacy` dir
- async++
- copyLegacy(deref)
- }
+ req.on('response', function (res) {
+ if (res.statusCode !== 200) {
+ badDownload = true
+ cb(new Error(res.statusCode + ' status code downloading tarball'))
+ return
+ }
+ // start unzipping and untaring
+ req.pipe(gunzip).pipe(extracter)
+ })
- if (win) {
- // need to download node.lib
- async++
- downloadNodeLib(deref)
- }
+ // invoked after the tarball has finished being extracted
+ function afterTarball () {
+ if (badDownload) return
+ if (extractCount === 0) {
+ return cb(new Error('There was a fatal problem while downloading/extracting the tarball'))
+ }
+ log.verbose('tarball', 'done parsing tarball')
+ var async = 0
- // write the "installVersion" file
- async++
- var installVersionPath = path.resolve(devDir, 'installVersion')
- fs.writeFile(installVersionPath, gyp.package.installVersion + '\n', deref)
+ if (isLegacy) {
+ // copy over the files from the `legacy` dir
+ async++
+ copyLegacy(deref)
+ }
- if (async === 0) {
- // no async tasks required
- cb()
- }
+ if (win) {
+ // need to download node.lib
+ async++
+ downloadNodeLib(deref)
+ }
- function deref (err) {
- if (err) return cb(err)
- --async || cb()
- }
- }
+ // write the "installVersion" file
+ async++
+ var installVersionPath = path.resolve(devDir, 'installVersion')
+ fs.writeFile(installVersionPath, gyp.package.installVersion + '\n', deref)
- function copyLegacy (done) {
- // legacy versions of node (< 0.8) require the legacy files to be copied
- // over since they contain many bugfixes from the current node build system
- log.verbose('legacy', 'copying "legacy" gyp configuration files for version', version)
+ if (async === 0) {
+ // no async tasks required
+ cb()
+ }
- var legacyDir = path.resolve(__dirname, '..', 'legacy')
- log.verbose('legacy', 'using "legacy" dir', legacyDir)
- log.verbose('legacy', 'copying to "dev" dir', devDir)
+ function deref (err) {
+ if (err) return cb(err)
+ --async || cb()
+ }
+ }
- var reader = fstream.Reader({ path: legacyDir, type: 'Directory' })
- var writer = fstream.Writer({ path: devDir, type: 'Directory' })
+ function copyLegacy (done) {
+ // legacy versions of node (< 0.8) require the legacy files to be copied
+ // over since they contain many bugfixes from the current node build system
+ log.verbose('legacy', 'copying "legacy" gyp configuration files for version', version)
- reader.on('entry', function onEntry (entry) {
- log.verbose('legacy', 'reading entry:', entry.path)
- entry.on('entry', onEntry)
- })
+ var legacyDir = path.resolve(__dirname, '..', 'legacy')
+ log.verbose('legacy', 'using "legacy" dir', legacyDir)
+ log.verbose('legacy', 'copying to "dev" dir', devDir)
- reader.on('error', done)
- writer.on('error', done)
+ var reader = fstream.Reader({ path: legacyDir, type: 'Directory' })
+ var writer = fstream.Writer({ path: devDir, type: 'Directory' })
- // Like `cp -rpf`
- reader.pipe(writer)
+ reader.on('entry', function onEntry (entry) {
+ log.verbose('legacy', 'reading entry:', entry.path)
+ entry.on('entry', onEntry)
+ })
- reader.on('end', done)
- }
+ reader.on('error', done)
+ writer.on('error', done)
- function downloadNodeLib (done) {
- log.verbose('on Windows; need to download `node.lib`...')
- var dir32 = path.resolve(devDir, 'ia32')
- , dir64 = path.resolve(devDir, 'x64')
- , nodeLibPath32 = path.resolve(dir32, 'node.lib')
- , nodeLibPath64 = path.resolve(dir64, 'node.lib')
- , nodeLibUrl32 = distUrl + '/v' + version + '/node.lib'
- , nodeLibUrl64 = distUrl + '/v' + version + '/x64/node.lib'
-
- log.verbose('32-bit node.lib dir', dir32)
- log.verbose('64-bit node.lib dir', dir64)
- log.verbose('`node.lib` 32-bit url', nodeLibUrl32)
- log.verbose('`node.lib` 64-bit url', nodeLibUrl64)
-
- var async = 2
- mkdir(dir32, function (err) {
- if (err) return done(err)
- log.verbose('streaming 32-bit node.lib to:', nodeLibPath32)
-
- var req = download(nodeLibUrl32)
- req.on('error', done)
- req.on('response', function (res) {
- if (res.statusCode !== 200) {
- done(new Error(res.statusCode + ' status code downloading 32-bit node.lib'))
- }
- })
- req.on('end', function () {
- --async || done()
- })
+ // Like `cp -rpf`
+ reader.pipe(writer)
- var ws = fs.createWriteStream(nodeLibPath32)
- ws.on('error', cb)
- req.pipe(ws)
- })
- mkdir(dir64, function (err) {
- if (err) return done(err)
- log.verbose('streaming 64-bit node.lib to:', nodeLibPath64)
-
- var req = download(nodeLibUrl64)
- req.on('error', done)
- req.on('response', function (res) {
- if (res.statusCode !== 200) {
- done(new Error(res.statusCode + ' status code downloading 64-bit node.lib'))
- }
+ reader.on('end', done)
+ }
+
+ function downloadNodeLib (done) {
+ log.verbose('on Windows; need to download `node.lib`...')
+ var dir32 = path.resolve(devDir, 'ia32')
+ , dir64 = path.resolve(devDir, 'x64')
+ , nodeLibPath32 = path.resolve(dir32, 'node.lib')
+ , nodeLibPath64 = path.resolve(dir64, 'node.lib')
+ , nodeLibUrl32 = distUrl + '/v' + version + '/node.lib'
+ , nodeLibUrl64 = distUrl + '/v' + version + '/x64/node.lib'
+
+ log.verbose('32-bit node.lib dir', dir32)
+ log.verbose('64-bit node.lib dir', dir64)
+ log.verbose('`node.lib` 32-bit url', nodeLibUrl32)
+ log.verbose('`node.lib` 64-bit url', nodeLibUrl64)
+
+ var async = 2
+ mkdir(dir32, function (err) {
+ if (err) return done(err)
+ log.verbose('streaming 32-bit node.lib to:', nodeLibPath32)
+
+ var req = download(nodeLibUrl32)
+ req.on('error', done)
+ req.on('response', function (res) {
+ if (res.statusCode !== 200) {
+ done(new Error(res.statusCode + ' status code downloading 32-bit node.lib'))
+ return
+ }
+
+ var ws = fs.createWriteStream(nodeLibPath32)
+ ws.on('error', cb)
+ req.pipe(ws)
+ })
+ req.on('end', function () {
+ --async || done()
+ })
})
- req.on('end', function () {
- --async || done()
+ mkdir(dir64, function (err) {
+ if (err) return done(err)
+ log.verbose('streaming 64-bit node.lib to:', nodeLibPath64)
+
+ var req = download(nodeLibUrl64)
+ req.on('error', done)
+ req.on('response', function (res) {
+ if (res.statusCode !== 200) {
+ done(new Error(res.statusCode + ' status code downloading 64-bit node.lib'))
+ return
+ }
+
+ var ws = fs.createWriteStream(nodeLibPath64)
+ ws.on('error', cb)
+ req.pipe(ws)
+ })
+ req.on('end', function () {
+ --async || done()
+ })
})
+ } // downloadNodeLib()
- var ws = fs.createWriteStream(nodeLibPath64)
- ws.on('error', cb)
- req.pipe(ws)
- })
- }
-
-
- })
+ }) // mkdir()
- }
+ } // go()
/**
* Checks if a given filename is "valid" for this installation.
*/
proto.configDefs = {
- help: Boolean // everywhere
- , arch: String // 'configure'
- , debug: Boolean // 'build'
+ help: Boolean // everywhere
+ , arch: String // 'configure'
+ , debug: Boolean // 'build'
, directory: String // bin
+ , make: String // 'build'
, msvs_version: String // 'configure'
- , ensure: Boolean // 'install'
- , solution: String // 'build' (windows only)
- , proxy: String // 'install'
- , nodedir: String // 'configure'
- , loglevel: String // everywhere
+ , ensure: Boolean // 'install'
+ , solution: String // 'build' (windows only)
+ , proxy: String // 'install'
+ , nodedir: String // 'configure'
+ , loglevel: String // everywhere
}
/**
{
"name": "node-gyp",
- "description": "`node-gyp` is a cross-platform command-line tool written in Node.js for compiling",
+ "description": "Node.js native addon build tool",
"keywords": [
"native",
"addon",
"bindings",
"gyp"
],
- "version": "0.5.2",
+ "version": "0.5.8",
"installVersion": 9,
"author": {
"name": "Nathan Rajlich",
"mkdirp": "0.3",
"nopt": "1",
"npmlog": "0",
+ "osenv": "0",
"request": "2.9",
"rimraf": "2",
"semver": "1",
"node": ">= 0.6.0"
},
"readme": "node-gyp\n=========\n### Node.js native addon build tool\n\n`node-gyp` is a cross-platform command-line tool written in Node.js for compiling\nnative addon modules for Node.js, which takes away the pain of dealing with the\nvarious differences in build platforms. It is the replacement to the `node-waf`\nprogram which is removed for node `v0.8`. If you have a native addon for node that\nstill has a `wscript` file, then you should definitely add a `binding.gyp` file\nto support the latest versions of node.\n\nMultiple target versions of node are supported (i.e. `0.6`, `0.7`,..., `1.0`,\netc.), regardless of what version of node is actually installed on your system\n(`node-gyp` downloads the necessary development files for the target version).\n\n#### Features:\n\n * Easy to use, consistent interface\n * Same commands to build your module on every platform\n * Supports multiple target versions of Node\n\n\nInstallation\n------------\n\nYou can install with `npm`:\n\n``` bash\n$ npm install -g node-gyp\n```\n\nYou will also need to install:\n\n * On Unix:\n * `python`\n * `make`\n * A proper C/C++ compiler toolchain, like GCC\n * On Windows:\n * [Python][windows-python] ([`v2.7.2`][windows-python-v2.7.2] recommended, `v3.x.x` not yet supported)\n * Microsoft Visual C++ ([Express][msvc] version works well)\n * For 64-bit builds of node and native modules you will _also_ need the [Windows 7 64-bit SDK][win7sdk]\n\nHow to Use\n----------\n\nTo compile your native addon, first go to its root directory:\n\n``` bash\n$ cd my_node_addon\n```\n\nThe next step is to generate the appropriate project build files for the current\nplatform. Use `configure` for that:\n\n``` bash\n$ node-gyp configure\n```\n\n__Note__: The `configure` step looks for the `binding.gyp` file in the current\ndirectory to processs. See below for instructions on creating the `binding.gyp` file.\n\nNow you will have either a `Makefile` (on Unix platforms) or a `vcxproj` file\n(on Windows) in the `build/` directory. Next invoke the `build` command:\n\n``` bash\n$ node-gyp build\n```\n\nNow you have your compiled `.node` bindings file! The compiled bindings end up\nin `build/Debug/` or `build/Release/`, depending on the build mode. At this point\nyou can require the `.node` file with Node and run your tests!\n\n__Note:__ To create a _Debug_ build of the bindings file, pass the `--debug` (or\n`-d`) switch when running the either `configure` or `build` command.\n\n\nThe \"binding.gyp\" file\n----------------------\n\nPreviously when node had `node-waf` you had to write a `wscript` file. The\nreplacement for that is the `binding.gyp` file, which describes the configuration\nto build your module in a JSON-like format. This file gets placed in the root of\nyour package, alongside the `package.json` file.\n\nA barebones `gyp` file appropriate for building a node addon looks like:\n\n``` json\n{\n \"targets\": [\n {\n \"target_name\": \"binding\",\n \"sources\": [ \"src/binding.cc\" ]\n }\n ]\n}\n```\n\nSome additional resources for writing `gyp` files:\n\n * [\"Hello World\" node addon example](https://github.com/joyent/node/tree/master/test/addons/hello-world)\n * [gyp user documentation](http://code.google.com/p/gyp/wiki/GypUserDocumentation)\n * [gyp input format reference](http://code.google.com/p/gyp/wiki/InputFormatReference)\n * ['\"binding.gyp\" files out in the wild' wiki page](https://github.com/TooTallNate/node-gyp/wiki/%22binding.gyp%22-files-out-in-the-wild)\n\n\nCommands\n--------\n\n`node-gyp` responds to the following commands:\n\n * `build` - Invokes `make`/`msbuild.exe` and builds the native addon\n * `clean` - Removes any generated project files and the `out` dir\n * `configure` - Generates project build files for the current platform\n * `rebuild` - Runs \"clean\", \"configure\" and \"build\" all at once\n * `install` - Installs node development files for the given version.\n * `list` - Lists the currently installed node development file versions\n * `remove` - Removes a node development files for a given version\n\n\nLicense\n-------\n\n(The MIT License)\n\nCopyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net>\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n[windows-python]: http://www.python.org/getit/windows\n[windows-python-v2.7.2]: http://www.python.org/download/releases/2.7.2#download\n[msvc]: http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-cpp-express\n[win7sdk]: http://www.microsoft.com/download/en/details.aspx?displayLang=en&id=8279\n",
- "_id": "node-gyp@0.5.2",
+ "_id": "node-gyp@0.5.8",
"dist": {
- "shasum": "7410e3dd9d950592ee80d09c7e5ef22286f79c0f"
+ "shasum": "2e6cdb28159c9bdd3fe484c7db81f119ee17e594"
},
- "_from": "node-gyp@0.5.2"
+ "_from": "node-gyp@~0.5"
}
var fs = require('fs')
, url = require('url')
, path = require('path')
+, CouchLogin = require('couch-login')
, npmlog
try {
npmlog = require("npmlog")
} catch (er) {
npmlog = { error: noop, warn: noop, info: noop,
- verbose: noop, silly: noop, http: silly,
+ verbose: noop, silly: noop, http: noop,
pause: noop, resume: noop }
}
function noop () {}
function RegClient (options) {
- // a registry url must be provided.
- var registry = url.parse(options.registry)
- if (!registry.protocol) throw new Error(
- 'Invalid registry: ' + registry.url)
- this.registry = registry.href
+ // if provided, then the registry needs to be a url.
+ // if it's not provided, then we're just using the cache only.
+ var registry = options.registry
+ if (registry) {
+ registry = url.parse(registry)
+ if (!registry.protocol) throw new Error(
+ 'Invalid registry: ' + registry.url)
+ this.registry = registry.href
+ if (this.registry.slice(-1) !== '/') {
+ this.registry += '/'
+ }
+ } else {
+ this.registry = null
+ }
this.retries = options.retries || 2
this.retryFactor = options.retryFactor || 10
a = a.split(":")
this.username = a.shift()
this.password = a.join(":")
+ } else {
+ this.username = options.username
+ this.password = options.password
+
+ // if username and password are set, but auth isn't, use them.
+ if (this.username && this.password) {
+ var a = this.username + ":" + this.password
+ this.auth = new Buffer(a, "utf8").toString("base64")
+ }
+ }
+
+ if (this.auth && !this.alwaysAuth && this.registry) {
+ // if we're always authing, then we just send the
+ // user/pass on every thing. otherwise, create a
+ // session, and use that.
+ this.token = options.token
+ this.couchLogin = new CouchLogin(this.registry, this.token)
}
+
this.email = options.email || null
this.defaultTag = options.tag || "latest"
timeout = Math.min(timeout, this.cacheMax)
timeout = Math.max(timeout, this.cacheMin)
+ if (!this.registry) timeout = Infinity
+
if ( process.env.COMP_CWORD !== undefined
&& process.env.COMP_LINE !== undefined
&& process.env.COMP_POINT !== undefined
if (typeof cb_ !== "function") cb_ = etag, etag = null
if (typeof cb_ !== "function") cb_ = what, what = null
+ if (!this.registry) return cb(new Error(
+ "No registry url provided: " + method + " " + where))
+
// Since there are multiple places where an error could occur,
// don't let the cb be called more than once.
var errState = null
var registry = this.registry
var adduserChange = /^\/?-\/user\/org\.couchdb\.user:([^\/]+)\/-rev/
- , adduserNew = /^\/?-\/user\/org\.couchdb\.user:([^\/]+)/
- , authRequired = (what || this.alwaysAuth)
- && !where.match(adduserNew)
- || where.match(adduserChange)
- || method === "DELETE"
+ , adduserNew = /^\/?-\/user\/org\.couchdb\.user:([^\/]+)/
+ , nu = where.match(adduserNew)
+ , uc = where.match(adduserChange)
+ , isUpload = what || this.alwaysAuth
+ , isDel = method === "DELETE"
+ , authRequired = isUpload && !nu || uc || isDel
// resolve to a full url on the registry
if (!where.match(/^https?:\/\//)) {
}
var remote = url.parse(where)
- , auth = authRequired && this.auth
-
- if (authRequired && !auth && this.username && this.password) {
- var a = this.username + ":" + this.password
- a = new Buffer(a, "utf8").toString("base64")
- auth = this.auth = a
+ , auth = this.auth
+
+ if (authRequired && !this.alwaysAuth) {
+ var couch = this.couchLogin
+ , token = couch && (this.token || couch.token)
+ , validToken = token && couch.valid(token)
+
+ if (!validToken) token = null
+ else this.token = token
+
+ if (couch && !token) {
+ // login to get a valid token
+ var a = { name: this.username, password: this.password }
+ var args = arguments
+ return this.couchLogin.login(a, function (er, cr, data) {
+ if (er || !couch.valid(couch.token)) {
+ er = er || new Error('login error')
+ return cb(er, cr, data)
+ }
+ this.token = this.couchLogin.token
+ return regRequest.call(this, method, where, what, etag, nofollow, cb_)
+ }.bind(this))
+ }
}
- if (authRequired && !auth) {
+ // now we either have a valid token, or an auth.
+
+ if (authRequired && !auth && !token) {
return cb(new Error(
"Cannot insert data into the registry without auth"))
}
- if (auth) {
+ if (auth && !token) {
remote.auth = new Buffer(auth, "base64").toString("utf8")
}
operation.attempt(function (currentAttempt) {
self.log.info("retry", "registry request attempt " + currentAttempt
+ " at " + (new Date()).toLocaleTimeString())
- makeRequest.call(self, method, remote, where, what, etag, nofollow
+ makeRequest.call(self, method, remote, where, what, etag, nofollow, token
, function (er, parsed, raw, response) {
// Only retry on 408, 5xx or no `response`.
var statusCode = response && response.statusCode
})
}
-function makeRequest (method, remote, where, what, etag, nofollow, cb_) {
+function makeRequest (method, remote, where, what, etag, nofollow, tok, cb_) {
var cbCalled = false
function cb () {
if (cbCalled) return
var opts = { url: remote
, method: method
, ca: this.ca
- , follow: false
, strictSSL: this.strictSSL }
, headers = opts.headers = {}
if (etag) {
headers[method === "GET" ? "if-none-match" : "if-match"] = etag
}
+ if (tok) {
+ headers.cookie = 'AuthSession=' + tok.AuthSession
+ }
+
headers.accept = "application/json"
headers["user-agent"] = this.userAgent
},
"name": "npm-registry-client",
"description": "Client for the npm registry",
- "version": "0.0.8",
+ "version": "0.0.10",
"repository": {
"url": "git://github.com/isaacs/npm-registry-client"
},
"mkdirp": "~0.3.3",
"rimraf": "~2.0.1",
"retry": "0.6.0",
+ "couch-login": "~0.1.6",
"npmlog": ""
},
"devDependencies": {
},
"license": "BSD",
"readme": "# npm-registry-client\n\nThe code that npm uses to talk to the registry.\n\nIt handles all the caching and HTTP calls.\n\n## Usage\n\n```javascript\nvar RegClient = require('npm-registry-client')\nvar client = new RegClient(options)\n\nclient.get(\"npm\", \"latest\", 1000, function (er, data, raw, res) {\n // error is an error if there was a problem.\n // data is the parsed data object\n // raw is the json string\n // res is the response from couch\n})\n```\n\n# Options\n\n* `registry` **Required** {String} URL to the registry\n* `cache` **Required** {String} Path to the cache folder\n* `alwaysAuth` {Boolean} Auth even for GET requests.\n* `auth` {String} A base64-encoded `username:password`\n* `email` {String} User's email address\n* `tag` {String} The default tag to use when publishing new packages.\n Default = `\"latest\"`\n* `ca` {String} Cerficate signing authority certificates to trust.\n* `strictSSL` {Boolean} Whether or not to be strict with SSL\n certificates. Default = `true`\n* `userAgent` {String} User agent header to send. Default =\n `\"node/{process.version}\"`\n* `log` {Object} The logger to use. Defaults to `require(\"npmlog\")` if\n that works, otherwise logs are disabled.\n* `retries` {Number} Number of times to retry on GET failures.\n Default=2\n* `retryFactor` {Number} `factor` setting for `node-retry`. Default=10\n* `retryMinTimeout` {Number} `minTimeout` setting for `node-retry`.\n Default=10000 (10 seconds)\n* `retryMaxTimeout` {Number} `maxTimeout` setting for `node-retry`.\n Default=60000 (60 seconds)\n\n# client.request(method, where, [what], [etag], [nofollow], cb)\n\n* `method` {String} HTTP method\n* `where` {String} Path to request on the server\n* `what` {Stream | Buffer | String | Object} The request body. Objects\n that are not Buffers or Streams are encoded as JSON.\n* `etag` {String} The cached ETag\n* `nofollow` {Boolean} Prevent following 302/301 responses\n* `cb` {Function}\n * `error` {Error | null}\n * `data` {Object} the parsed data object\n * `raw` {String} the json\n * `res` {Response Object} response from couch\n\nMake a request to the registry. All the other methods are wrappers\naround this. one.\n\n# client.adduser(username, password, email, cb)\n\n* `username` {String}\n* `password` {String}\n* `email` {String}\n* `cb` {Function}\n\nAdd a user account to the registry, or verify the credentials.\n\n# client.get(url, [timeout], [nofollow], [staleOk], cb)\n\n* `url` {String} The url path to fetch\n* `timeout` {Number} Number of seconds old that a cached copy must be\n before a new request will be made.\n* `nofollow` {Boolean} Do not follow 301/302 responses\n* `staleOk` {Boolean} If there's cached data available, then return that\n to the callback quickly, and update the cache the background.\n\nFetches data from the registry via a GET request, saving it in\nthe cache folder with the ETag.\n\n# client.publish(data, tarball, [readme], cb)\n\n* `data` {Object} Package data\n* `tarball` {String | Stream} Filename or stream of the package tarball\n* `readme` {String} Contents of the README markdown file\n* `cb` {Function}\n\nPublish a package to the registry.\n\nNote that this does not create the tarball from a folder. However, it\ncan accept a gzipped tar stream or a filename to a tarball.\n\n# client.star(package, starred, cb)\n\n* `package` {String} Name of the package to star\n* `starred` {Boolean} True to star the package, false to unstar it.\n* `cb` {Function}\n\nStar or unstar a package.\n\nNote that the user does not have to be the package owner to star or\nunstar a package, though other writes do require that the user be the\npackage owner.\n\n# client.tag(project, version, tag, cb)\n\n* `project` {String} Project name\n* `version` {String} Version to tag\n* `tag` {String} Tag name to apply\n* `cb` {Function}\n\nMark a version in the `dist-tags` hash, so that `pkg@tag`\nwill fetch the specified version.\n\n# client.unpublish(name, [ver], cb)\n\n* `name` {String} package name\n* `ver` {String} version to unpublish. Leave blank to unpublish all\n versions.\n* `cb` {Function}\n\nRemove a version of a package (or all versions) from the registry. When\nthe last version us unpublished, the entire document is removed from the\ndatabase.\n\n# client.upload(where, file, [etag], [nofollow], cb)\n\n* `where` {String} URL path to upload to\n* `file` {String | Stream} Either the filename or a readable stream\n* `etag` {String} Cache ETag\n* `nofollow` {Boolean} Do not follow 301/302 responses\n* `cb` {Function}\n\nUpload an attachment. Mostly used by `client.publish()`.\n",
- "_id": "npm-registry-client@0.0.8",
+ "_id": "npm-registry-client@0.0.10",
"_from": "npm-registry-client@0"
}
{
"name": "read-package-json",
- "version": "0.0.12",
+ "version": "0.1.1",
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"graceful-fs": "~1.1.8"
},
"readme": "# read-package-json\n\nThis is the thing that npm uses to read package.json files. It\nvalidates some stuff, and loads some default things.\n\nIt keeps a cache of the files you've read, so that you don't end\nup reading the same package.json file multiple times.\n\nNote that if you just want to see what's literally in the package.json\nfile, you can usually do `var data = require('some-module/package.json')`.\n\nThis module is basically only needed by npm, but it's handy to see what\nnpm will see when it looks at your package.\n\n## Usage\n\n```javascript\nvar readJson = require('read-package-json')\n\nreadJson('/path/to/package.json', function (er, data) {\n if (er) {\n console.error(\"There was an error reading the file\")\n return\n }\n\n console.error('the package data is', data)\n}\n```\n\n## readJson(file, cb)\n\n* `file` {String} The path to the package.json file\n* `cb` {Function}\n\nReads the JSON file and does the things.\n\n## `package.json` Fields\n\nSee `man 5 package.json` or `npm help json`.\n\n## readJson.log\n\nBy default this is a reference to the `npmlog` module. But if that\nmodule can't be found, then it'll be set to just a dummy thing that does\nnothing.\n\nReplace with your own `{log,warn,error}` object for fun loggy time.\n\n## readJson.extras(file, data, cb)\n\nRun all the extra stuff relative to the file, with the parsed data.\n\nModifies the data as it does stuff. Calls the cb when it's done.\n\n## readJson.extraSet = [fn, fn, ...]\n\nArray of functions that are called by `extras`. Each one receives the\narguments `fn(file, data, cb)` and is expected to call `cb(er, data)`\nwhen done or when an error occurs.\n\nOrder is indeterminate, so each function should be completely\nindependent.\n\nMix and match!\n\n## readJson.cache\n\nThe `lru-cache` object that readJson uses to not read the same file over\nand over again. See\n[lru-cache](https://github.com/isaacs/node-lru-cache) for details.\n\n## Other Relevant Files Besides `package.json`\n\nSome other files have an effect on the resulting data object, in the\nfollowing ways:\n\n### `README?(.*)`\n\nIf there is a `README` or `README.*` file present, then npm will attach\na `readme` field to the data with the contents of this file.\n\nOwing to the fact that roughly 100% of existing node modules have\nMarkdown README files, it will generally be assumed to be Markdown,\nregardless of the extension. Please plan accordingly.\n\n### `server.js`\n\nIf there is a `server.js` file, and there is not already a\n`scripts.start` field, then `scripts.start` will be set to `node\nserver.js`.\n\n### `AUTHORS`\n\nIf there is not already a `contributors` field, then the `contributors`\nfield will be set to the contents of the `AUTHORS` file, split by lines,\nand parsed.\n\n### `bindings.gyp`\n\nIf a bindings.gyp file exists, and there is not already a\n`scripts.install` field, then the `scripts.install` field will be set to\n`node-gyp rebuild`.\n\n### `wscript`\n\nIf a wscript file exists, and there is not already a `scripts.install`\nfield, then the `scripts.install` field will be set to `node-waf clean ;\nnode-waf configure build`.\n\nNote that the `bindings.gyp` file supercedes this, since node-waf has\nbeen deprecated in favor of node-gyp.\n\n### `index.js`\n\nIf the json file does not exist, but there is a `index.js` file\npresent instead, and that file has a package comment, then it will try\nto parse the package comment, and use that as the data instead.\n\nA package comment looks like this:\n\n```javascript\n/**package\n * { \"name\": \"my-bare-module\"\n * , \"version\": \"1.2.3\"\n * , \"description\": \"etc....\" }\n **/\n\n// or...\n\n/**package\n{ \"name\": \"my-bare-module\"\n, \"version\": \"1.2.3\"\n, \"description\": \"etc....\" }\n**/\n```\n\nThe important thing is that it starts with `/**package`, and ends with\n`**/`. If the package.json file exists, then the index.js is not\nparsed.\n\n### `{directories.man}/*.[0-9]`\n\nIf there is not already a `man` field defined as an array of files or a\nsingle file, and\nthere is a `directories.man` field defined, then that directory will\nbe searched for manpages.\n\nAny valid manpages found in that directory will be assigned to the `man`\narray, and installed in the appropriate man directory at package install\ntime, when installed globally on a Unix system.\n\n### `{directories.bin}/*`\n\nIf there is not already a `bin` field defined as a string filename or a\nhash of `<name> : <filename>` pairs, then the `directories.bin`\ndirectory will be searched and all the files within it will be linked as\nexecutables at install time.\n\nWhen installing locally, npm links bins into `node_modules/.bin`, which\nis in the `PATH` environ when npm runs scripts. When\ninstalling globally, they are linked into `{prefix}/bin`, which is\npresumably in the `PATH` environment variable.\n",
- "_id": "read-package-json@0.0.12",
- "_from": "read-package-json@0"
+ "_id": "read-package-json@0.1.1",
+ "_from": "read-package-json@~0.1.0"
}
function readme (file, data, cb) {
if (data.readme) return cb(null, data);
var dir = path.dirname(file)
- glob("README?(.*)", { cwd: dir }, function (er, files) {
+ var globOpts = { cwd: dir, nocase: true }
+ glob("README?(.*)", globOpts, function (er, files) {
if (er) return cb(er);
if (!files.length) return cb()
var rm = path.resolve(dir, files[0])
})
bugsTypoWarn(file, data)
scriptTypoWarn(file, data)
+ noreadmeWarn(file, data)
+}
+
+function noreadmeWarn (file, data) {
+ if (data.readme) return;
+ warn(file, data, "No README.md file found!")
+ data.readme = "ERROR: No README.md file found!"
}
function checkTypo (file, data, d) {
{
- "version": "1.1.36",
+ "version": "1.1.39",
"name": "npm",
"publishConfig": {
"proprietary-attribs": false
"npmlog": "0",
"ansi": "~0.1.2",
"npm-registry-client": "0",
- "read-package-json": "0",
+ "read-package-json": "~0.1.1",
"read-installed": "0",
"glob": "~3.1.9",
"init-package-json": "0",
"osenv": "0",
"lockfile": ">=0.2",
- "retry": "~0.6.0"
+ "retry": "~0.6.0",
+ "couch-login": "~0.1.6"
},
"bundleDependencies": [
"semver",
"init-package-json",
"osenv",
"lockfile",
- "retry"
+ "retry",
+ "couch-login"
],
"devDependencies": {
"ronn": "https://github.com/isaacs/ronnjs/tarball/master",
"tap": "~0.2.5"
},
"engines": {
- "node": "0.6 || 0.7 || 0.8",
+ "node": ">0.6",
"npm": "1"
},
"scripts": {