node_modules updated, buefy add to lib folder
This commit is contained in:
Generated
+2
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
deps
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2014, Joyent, Inc. All rights reserved.
|
||||
Compatibility tests copyright (c) 2010-2014 Caolan McMahon.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
#
|
||||
# Makefile: top-level Makefile
|
||||
#
|
||||
# This Makefile contains only repo-specific logic and uses included makefiles
|
||||
# to supply common targets (javascriptlint, jsstyle, restdown, etc.), which are
|
||||
# used by other repos as well.
|
||||
#
|
||||
|
||||
#
|
||||
# Tools must be installed on the path
|
||||
#
|
||||
JSL = jsl
|
||||
JSSTYLE = jsstyle
|
||||
|
||||
#
|
||||
# Files
|
||||
#
|
||||
JS_FILES := $(shell find lib tests -name '*.js' -not -name compat.js)
|
||||
JSL_FILES_NODE = $(JS_FILES)
|
||||
JSSTYLE_FILES = $(JS_FILES)
|
||||
JSL_CONF_NODE = jsl.node.conf
|
||||
|
||||
all:
|
||||
npm install
|
||||
|
||||
test:
|
||||
npm test
|
||||
|
||||
include ./Makefile.targ
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
# -*- mode: makefile -*-
|
||||
#
|
||||
# Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
#
|
||||
# Makefile.targ: common targets.
|
||||
#
|
||||
# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
|
||||
# into other repos as-is without requiring any modifications. If you find
|
||||
# yourself changing this file, you should instead update the original copy in
|
||||
# eng.git and then update your repo to use the new version.
|
||||
#
|
||||
# This Makefile defines several useful targets and rules. You can use it by
|
||||
# including it from a Makefile that specifies some of the variables below.
|
||||
#
|
||||
# Targets defined in this Makefile:
|
||||
#
|
||||
# check Checks JavaScript files for lint and style
|
||||
# Checks bash scripts for syntax
|
||||
# Checks SMF manifests for validity against the SMF DTD
|
||||
#
|
||||
# clean Removes built files
|
||||
#
|
||||
# docs Builds restdown documentation in docs/
|
||||
#
|
||||
# prepush Depends on "check" and "test"
|
||||
#
|
||||
# test Does nothing (you should override this)
|
||||
#
|
||||
# xref Generates cscope (source cross-reference index)
|
||||
#
|
||||
# For details on what these targets are supposed to do, see the Joyent
|
||||
# Engineering Guide.
|
||||
#
|
||||
# To make use of these targets, you'll need to set some of these variables. Any
|
||||
# variables left unset will simply not be used.
|
||||
#
|
||||
# BASH_FILES Bash scripts to check for syntax
|
||||
# (paths relative to top-level Makefile)
|
||||
#
|
||||
# CLEAN_FILES Files to remove as part of the "clean" target. Note
|
||||
# that files generated by targets in this Makefile are
|
||||
# automatically included in CLEAN_FILES. These include
|
||||
# restdown-generated HTML and JSON files.
|
||||
#
|
||||
# DOC_FILES Restdown (documentation source) files. These are
|
||||
# assumed to be contained in "docs/", and must NOT
|
||||
# contain the "docs/" prefix.
|
||||
#
|
||||
# JSL_CONF_NODE Specify JavaScriptLint configuration files
|
||||
# JSL_CONF_WEB (paths relative to top-level Makefile)
|
||||
#
|
||||
# Node.js and Web configuration files are separate
|
||||
# because you'll usually want different global variable
|
||||
# configurations. If no file is specified, none is given
|
||||
# to jsl, which causes it to use a default configuration,
|
||||
# which probably isn't what you want.
|
||||
#
|
||||
# JSL_FILES_NODE JavaScript files to check with Node config file.
|
||||
# JSL_FILES_WEB JavaScript files to check with Web config file.
|
||||
#
|
||||
# You can also override these variables:
|
||||
#
|
||||
# BASH Path to bash (default: bash)
|
||||
#
|
||||
# CSCOPE_DIRS Directories to search for source files for the cscope
|
||||
# index. (default: ".")
|
||||
#
|
||||
# JSL Path to JavaScriptLint (default: "jsl")
|
||||
#
|
||||
# JSL_FLAGS_NODE Additional flags to pass through to JSL
|
||||
# JSL_FLAGS_WEB
|
||||
# JSL_FLAGS
|
||||
#
|
||||
# JSSTYLE Path to jsstyle (default: jsstyle)
|
||||
#
|
||||
# JSSTYLE_FLAGS Additional flags to pass through to jsstyle
|
||||
#
|
||||
|
||||
#
|
||||
# Defaults for the various tools we use.
|
||||
#
|
||||
BASH ?= bash
|
||||
BASHSTYLE ?= tools/bashstyle
|
||||
CP ?= cp
|
||||
CSCOPE ?= cscope
|
||||
CSCOPE_DIRS ?= .
|
||||
JSL ?= jsl
|
||||
JSSTYLE ?= jsstyle
|
||||
MKDIR ?= mkdir -p
|
||||
MV ?= mv
|
||||
RESTDOWN_FLAGS ?=
|
||||
RMTREE ?= rm -rf
|
||||
JSL_FLAGS ?= --nologo --nosummary
|
||||
|
||||
ifeq ($(shell uname -s),SunOS)
|
||||
TAR ?= gtar
|
||||
else
|
||||
TAR ?= tar
|
||||
endif
|
||||
|
||||
|
||||
#
|
||||
# Defaults for other fixed values.
|
||||
#
|
||||
BUILD = build
|
||||
DISTCLEAN_FILES += $(BUILD)
|
||||
DOC_BUILD = $(BUILD)/docs/public
|
||||
|
||||
#
|
||||
# Configure JSL_FLAGS_{NODE,WEB} based on JSL_CONF_{NODE,WEB}.
|
||||
#
|
||||
ifneq ($(origin JSL_CONF_NODE), undefined)
|
||||
JSL_FLAGS_NODE += --conf=$(JSL_CONF_NODE)
|
||||
endif
|
||||
|
||||
ifneq ($(origin JSL_CONF_WEB), undefined)
|
||||
JSL_FLAGS_WEB += --conf=$(JSL_CONF_WEB)
|
||||
endif
|
||||
|
||||
#
|
||||
# Targets. For descriptions on what these are supposed to do, see the
|
||||
# Joyent Engineering Guide.
|
||||
#
|
||||
|
||||
#
|
||||
# Instruct make to keep around temporary files. We have rules below that
|
||||
# automatically update git submodules as needed, but they employ a deps/*/.git
|
||||
# temporary file. Without this directive, make tries to remove these .git
|
||||
# directories after the build has completed.
|
||||
#
|
||||
.SECONDARY: $($(wildcard deps/*):%=%/.git)
|
||||
|
||||
#
|
||||
# This rule enables other rules that use files from a git submodule to have
|
||||
# those files depend on deps/module/.git and have "make" automatically check
|
||||
# out the submodule as needed.
|
||||
#
|
||||
deps/%/.git:
|
||||
git submodule update --init deps/$*
|
||||
|
||||
#
|
||||
# These recipes make heavy use of dynamically-created phony targets. The parent
|
||||
# Makefile defines a list of input files like BASH_FILES. We then say that each
|
||||
# of these files depends on a fake target called filename.bashchk, and then we
|
||||
# define a pattern rule for those targets that runs bash in check-syntax-only
|
||||
# mode. This mechanism has the nice properties that if you specify zero files,
|
||||
# the rule becomes a noop (unlike a single rule to check all bash files, which
|
||||
# would invoke bash with zero files), and you can check individual files from
|
||||
# the command line with "make filename.bashchk".
|
||||
#
|
||||
.PHONY: check-bash
|
||||
check-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle)
|
||||
|
||||
%.bashchk: %
|
||||
$(BASH) -n $^
|
||||
|
||||
%.bashstyle: %
|
||||
$(BASHSTYLE) $^
|
||||
|
||||
.PHONY: check-jsl check-jsl-node check-jsl-web
|
||||
check-jsl: check-jsl-node check-jsl-web
|
||||
|
||||
check-jsl-node: $(JSL_FILES_NODE:%=%.jslnodechk)
|
||||
|
||||
check-jsl-web: $(JSL_FILES_WEB:%=%.jslwebchk)
|
||||
|
||||
%.jslnodechk: % $(JSL_EXEC)
|
||||
$(JSL) $(JSL_FLAGS) $(JSL_FLAGS_NODE) $<
|
||||
|
||||
%.jslwebchk: % $(JSL_EXEC)
|
||||
$(JSL) $(JSL_FLAGS) $(JSL_FLAGS_WEB) $<
|
||||
|
||||
.PHONY: check-jsstyle
|
||||
check-jsstyle: $(JSSTYLE_FILES:%=%.jsstylechk)
|
||||
|
||||
%.jsstylechk: % $(JSSTYLE_EXEC)
|
||||
$(JSSTYLE) $(JSSTYLE_FLAGS) $<
|
||||
|
||||
.PHONY: check
|
||||
check: check-jsl check-jsstyle check-bash
|
||||
@echo check ok
|
||||
|
||||
.PHONY: clean
|
||||
clean::
|
||||
-$(RMTREE) $(CLEAN_FILES)
|
||||
|
||||
.PHONY: distclean
|
||||
distclean:: clean
|
||||
-$(RMTREE) $(DISTCLEAN_FILES)
|
||||
|
||||
CSCOPE_FILES = cscope.in.out cscope.out cscope.po.out
|
||||
CLEAN_FILES += $(CSCOPE_FILES)
|
||||
|
||||
.PHONY: xref
|
||||
xref: cscope.files
|
||||
$(CSCOPE) -bqR
|
||||
|
||||
.PHONY: cscope.files
|
||||
cscope.files:
|
||||
find $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \
|
||||
-o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@
|
||||
|
||||
#
|
||||
# The "docs" target is complicated because we do several things here:
|
||||
#
|
||||
# (1) Use restdown to build HTML and JSON files from each of DOC_FILES.
|
||||
#
|
||||
# (2) Copy these files into $(DOC_BUILD) (build/docs/public), which
|
||||
# functions as a complete copy of the documentation that could be
|
||||
# mirrored or served over HTTP.
|
||||
#
|
||||
# (3) Then copy any directories and media from docs/media into
|
||||
# $(DOC_BUILD)/media. This allows projects to include their own media,
|
||||
# including files that will override same-named files provided by
|
||||
# restdown.
|
||||
#
|
||||
# Step (3) is the surprisingly complex part: in order to do this, we need to
|
||||
# identify the subdirectories in docs/media, recreate them in
|
||||
# $(DOC_BUILD)/media, then do the same with the files.
|
||||
#
|
||||
DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$")
|
||||
DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%)
|
||||
DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%)
|
||||
|
||||
DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null)
|
||||
DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%)
|
||||
DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%)
|
||||
|
||||
#
|
||||
# Like the other targets, "docs" just depends on the final files we want to
|
||||
# create in $(DOC_BUILD), leveraging other targets and recipes to define how
|
||||
# to get there.
|
||||
#
|
||||
.PHONY: docs
|
||||
docs: \
|
||||
$(DOC_FILES:%.restdown=$(DOC_BUILD)/%.html) \
|
||||
$(DOC_FILES:%.restdown=$(DOC_BUILD)/%.json) \
|
||||
$(DOC_MEDIA_FILES_BUILD)
|
||||
|
||||
#
|
||||
# We keep the intermediate files so that the next build can see whether the
|
||||
# files in DOC_BUILD are up to date.
|
||||
#
|
||||
.PRECIOUS: \
|
||||
$(DOC_FILES:%.restdown=docs/%.html) \
|
||||
$(DOC_FILES:%.restdown=docs/%json)
|
||||
|
||||
#
|
||||
# We do clean those intermediate files, as well as all of DOC_BUILD.
|
||||
#
|
||||
CLEAN_FILES += \
|
||||
$(DOC_BUILD) \
|
||||
$(DOC_FILES:%.restdown=docs/%.html) \
|
||||
$(DOC_FILES:%.restdown=docs/%.json)
|
||||
|
||||
#
|
||||
# Before installing the files, we must make sure the directories exist. The |
|
||||
# syntax tells make that the dependency need only exist, not be up to date.
|
||||
# Otherwise, it might try to rebuild spuriously because the directory itself
|
||||
# appears out of date.
|
||||
#
|
||||
$(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD)
|
||||
|
||||
$(DOC_BUILD)/%: docs/% | $(DOC_BUILD)
|
||||
$(CP) $< $@
|
||||
|
||||
docs/%.json docs/%.html: docs/%.restdown | $(DOC_BUILD) $(RESTDOWN_EXEC)
|
||||
$(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $<
|
||||
|
||||
$(DOC_BUILD):
|
||||
$(MKDIR) $@
|
||||
|
||||
$(DOC_MEDIA_DIRS_BUILD):
|
||||
$(MKDIR) $@
|
||||
|
||||
#
|
||||
# The default "test" target does nothing. This should usually be overridden by
|
||||
# the parent Makefile. It's included here so we can define "prepush" without
|
||||
# requiring the repo to define "test".
|
||||
#
|
||||
.PHONY: test
|
||||
test:
|
||||
|
||||
.PHONY: prepush
|
||||
prepush: check test
|
||||
+594
@@ -0,0 +1,594 @@
|
||||
# vasync: observable asynchronous control flow
|
||||
|
||||
This module provides several functions for asynchronous control flow. There are
|
||||
many modules that do this already (notably async.js). This one's claim to fame
|
||||
is improved debuggability.
|
||||
|
||||
|
||||
## Observability is important
|
||||
|
||||
Working with Node's asynchronous, callback-based model is much easier with a
|
||||
handful of simple control-flow abstractions, like:
|
||||
|
||||
* waterfalls and pipelines (which invoke a list of asynchronous callbacks
|
||||
sequentially)
|
||||
* parallel pipelines (which invoke a list of asynchronous callbacks in parallel
|
||||
and invoke a top-level callback when the last one completes).
|
||||
* queues
|
||||
* barriers
|
||||
|
||||
But these structures also introduce new types of programming errors: failing to
|
||||
invoke the callback can cause the program to hang, and inadvertently invoking it
|
||||
twice can cause all kinds of mayhem that's very difficult to debug.
|
||||
|
||||
The functions in this module keep track of what's going on so that you can
|
||||
figure out what happened when your program goes wrong. They generally return an
|
||||
object describing details of the current state. If your program goes wrong, you
|
||||
have several ways of getting at this state:
|
||||
|
||||
* On illumos-based systems, use MDB to [find the status object](http://dtrace.org/blogs/bmc/2012/05/05/debugging-node-js-memory-leaks/)
|
||||
and then [print it out](http://dtrace.org/blogs/dap/2012/01/13/playing-with-nodev8-postmortem-debugging/).
|
||||
* Provide an HTTP API (or AMQP, or whatever) that returns these pending status
|
||||
objects as JSON (see [kang](https://github.com/davepacheco/kang)).
|
||||
* Incorporate a REPL into your program and print out the status object.
|
||||
* Use the Node debugger to print out the status object.
|
||||
|
||||
## Functions
|
||||
|
||||
* [parallel](#parallel-invoke-n-functions-in-parallel): invoke N functions in
|
||||
parallel (and merge the results)
|
||||
* [forEachParallel](#foreachparallel-invoke-the-same-function-on-n-inputs-in-parallel):
|
||||
invoke the same function on N inputs in parallel
|
||||
* [pipeline](#pipeline-invoke-n-functions-in-series-and-stop-on-failure): invoke
|
||||
N functions in series (and stop on failure)
|
||||
* [forEachPipeline](#foreachpipeline-invoke-the-same-function-on-n-inputs-in-series-and-stop-on-failure):
|
||||
invoke the same function on N inputs in series (and stop on failure)
|
||||
* [waterfall](#waterfall-invoke-n-functions-in-series-stop-on-failure-and-propagate-results):
|
||||
like pipeline, but propagating results between stages
|
||||
* [barrier](#barrier-coordinate-multiple-concurrent-operations): coordinate
|
||||
multiple concurrent operations
|
||||
* [queue/queuev](#queuequeuev-fixed-size-worker-queue): fixed-size worker queue
|
||||
|
||||
### parallel: invoke N functions in parallel
|
||||
|
||||
Synopsis: `parallel(args, callback)`
|
||||
|
||||
This function takes a list of input functions (specified by the "funcs" property
|
||||
of "args") and runs them all. These input functions are expected to be
|
||||
asynchronous: they get a "callback" argument and should invoke it as
|
||||
`callback(err, result)`. The error and result will be saved and made available
|
||||
to the original caller when all of these functions complete.
|
||||
|
||||
This function returns the same "result" object it passes to the callback, and
|
||||
you can use the fields in this object to debug or observe progress:
|
||||
|
||||
* `operations`: array corresponding to the input functions, with
|
||||
* `func`: input function,
|
||||
* `status`: "pending", "ok", or "fail",
|
||||
* `err`: returned "err" value, if any, and
|
||||
* `result`: returned "result" value, if any
|
||||
* `successes`: "result" field for each of "operations" where
|
||||
"status" == "ok" (in no particular order)
|
||||
* `ndone`: number of input operations that have completed
|
||||
* `nerrors`: number of input operations that have failed
|
||||
|
||||
This status object lets you see in a debugger exactly which functions have
|
||||
completed, what they returned, and which ones are outstanding.
|
||||
|
||||
All errors are combined into a single "err" parameter to the final callback (see
|
||||
below).
|
||||
|
||||
Example usage:
|
||||
|
||||
```js
|
||||
console.log(mod_vasync.parallel({
|
||||
'funcs': [
|
||||
function f1 (callback) { mod_dns.resolve('joyent.com', callback); },
|
||||
function f2 (callback) { mod_dns.resolve('github.com', callback); },
|
||||
function f3 (callback) { mod_dns.resolve('asdfaqsdfj.com', callback); }
|
||||
]
|
||||
}, function (err, results) {
|
||||
console.log('error: %s', err.message);
|
||||
console.log('results: %s', mod_util.inspect(results, null, 3));
|
||||
}));
|
||||
```
|
||||
|
||||
In the first tick, this outputs:
|
||||
|
||||
```js
|
||||
status: { operations:
|
||||
[ { func: [Function: f1], status: 'pending' },
|
||||
{ func: [Function: f2], status: 'pending' },
|
||||
{ func: [Function: f3], status: 'pending' } ],
|
||||
successes: [],
|
||||
ndone: 0,
|
||||
nerrors: 0 }
|
||||
```
|
||||
|
||||
showing that there are three operations pending and none has yet been started.
|
||||
When the program finishes, it outputs this error:
|
||||
|
||||
error: first of 1 error: queryA ENOTFOUND
|
||||
|
||||
which encapsulates all of the intermediate failures. This model allows you to
|
||||
write the final callback like you normally would:
|
||||
|
||||
```js
|
||||
if (err)
|
||||
return (callback(err));
|
||||
```
|
||||
|
||||
and still propagate useful information to callers that don't deal with multiple
|
||||
errors (i.e. most callers).
|
||||
|
||||
The example also prints out the detailed final status, including all of the
|
||||
errors and return values:
|
||||
|
||||
```js
|
||||
results: { operations:
|
||||
[ { func: [Function: f1],
|
||||
funcname: 'f1',
|
||||
status: 'ok',
|
||||
err: null,
|
||||
result: [ '165.225.132.33' ] },
|
||||
{ func: [Function: f2],
|
||||
funcname: 'f2',
|
||||
status: 'ok',
|
||||
err: null,
|
||||
result: [ '207.97.227.239' ] },
|
||||
{ func: [Function: f3],
|
||||
funcname: 'f3',
|
||||
status: 'fail',
|
||||
err: { [Error: queryA ENOTFOUND] code: 'ENOTFOUND',
|
||||
errno: 'ENOTFOUND', syscall: 'queryA' },
|
||||
result: undefined } ],
|
||||
successes: [ [ '165.225.132.33' ], [ '207.97.227.239' ] ],
|
||||
ndone: 3,
|
||||
nerrors: 1 }
|
||||
```
|
||||
|
||||
You can use this if you want to handle all of the errors individually or to get
|
||||
at all of the individual return values.
|
||||
|
||||
Note that "successes" is provided as a convenience and the order of items in
|
||||
that array may not correspond to the order of the inputs. To consume output in
|
||||
an ordered manner, you should iterate over "operations" and pick out the result
|
||||
from each item.
|
||||
|
||||
|
||||
### forEachParallel: invoke the same function on N inputs in parallel
|
||||
|
||||
Synopsis: `forEachParallel(args, callback)`
|
||||
|
||||
This function is exactly like `parallel`, except that the input is specified as
|
||||
a *single* function ("func") and a list of inputs ("inputs"). The function is
|
||||
invoked on each input in parallel.
|
||||
|
||||
This example is exactly equivalent to the one above:
|
||||
|
||||
```js
|
||||
console.log(mod_vasync.forEachParallel({
|
||||
'func': mod_dns.resolve,
|
||||
'inputs': [ 'joyent.com', 'github.com', 'asdfaqsdfj.com' ]
|
||||
}, function (err, results) {
|
||||
console.log('error: %s', err.message);
|
||||
console.log('results: %s', mod_util.inspect(results, null, 3));
|
||||
}));
|
||||
```
|
||||
|
||||
### pipeline: invoke N functions in series (and stop on failure)
|
||||
|
||||
Synopsis: `pipeline(args, callback)`
|
||||
|
||||
The named arguments (that go inside `args`) are:
|
||||
|
||||
* `funcs`: input functions, to be invoked in series
|
||||
* `arg`: arbitrary argument that will be passed to each function
|
||||
|
||||
The functions are invoked in order as `func(arg, callback)`, where "arg" is the
|
||||
user-supplied argument from "args" and "callback" should be invoked in the usual
|
||||
way. If any function emits an error, the whole pipeline stops.
|
||||
|
||||
The return value and the arguments to the final callback are exactly the same as
|
||||
for `parallel`. The error object for the final callback is just the error
|
||||
returned by whatever pipeline function failed (if any).
|
||||
|
||||
This example is similar to the one above, except that it runs the steps in
|
||||
sequence and stops early because `pipeline` stops on the first error:
|
||||
|
||||
```js
|
||||
console.log(mod_vasync.pipeline({
|
||||
'funcs': [
|
||||
function f1 (_, callback) { mod_fs.stat('/tmp', callback); },
|
||||
function f2 (_, callback) { mod_fs.stat('/noexist', callback); },
|
||||
function f3 (_, callback) { mod_fs.stat('/var', callback); }
|
||||
]
|
||||
}, function (err, results) {
|
||||
console.log('error: %s', err.message);
|
||||
console.log('results: %s', mod_util.inspect(results, null, 3));
|
||||
}));
|
||||
```
|
||||
|
||||
As a result, the status after the first tick looks like this:
|
||||
|
||||
```js
|
||||
{ operations:
|
||||
[ { func: [Function: f1], status: 'pending' },
|
||||
{ func: [Function: f2], status: 'waiting' },
|
||||
{ func: [Function: f3], status: 'waiting' } ],
|
||||
successes: [],
|
||||
ndone: 0,
|
||||
nerrors: 0 }
|
||||
```
|
||||
|
||||
Note that the second and third stages are now "waiting", rather than "pending"
|
||||
in the `parallel` case. The error and complete result look just like the
|
||||
parallel case.
|
||||
|
||||
|
||||
### forEachPipeline: invoke the same function on N inputs in series (and stop on failure)
|
||||
|
||||
Synopsis: `forEachPipeline(args, callback)`
|
||||
|
||||
This function is exactly like `pipeline`, except that the input is specified as
|
||||
a *single* function ("func") and a list of inputs ("inputs"). The function is
|
||||
invoked on each input in series.
|
||||
|
||||
This example is exactly equivalent to the one above:
|
||||
|
||||
```js
|
||||
console.log(mod_vasync.forEachPipeline({
|
||||
'func': mod_dns.resolve,
|
||||
'inputs': [ 'joyent.com', 'github.com', 'asdfaqsdfj.com' ]
|
||||
}, function (err, results) {
|
||||
console.log('error: %s', err.message);
|
||||
console.log('results: %s', mod_util.inspect(results, null, 3));
|
||||
}));
|
||||
```
|
||||
|
||||
### waterfall: invoke N functions in series, stop on failure, and propagate results
|
||||
|
||||
Synopsis: `waterfall(funcs, callback)`
|
||||
|
||||
This function works like `pipeline` except for argument passing.
|
||||
|
||||
Each function is passed any values emitted by the previous function (none for
|
||||
the first function), followed by the callback to invoke upon completion. This
|
||||
callback must be invoked exactly once, regardless of success or failure. As
|
||||
conventional in Node, the first argument to the callback indicates an error (if
|
||||
non-null). Subsequent arguments are passed to the next function in the "funcs"
|
||||
chain.
|
||||
|
||||
If any function fails (i.e., calls its callback with an Error), then the
|
||||
remaining functions are not invoked and "callback" is invoked with the error.
|
||||
|
||||
The only difference between waterfall() and pipeline() are the arguments passed
|
||||
to each function in the chain. pipeline() always passes the same argument
|
||||
followed by the callback, while waterfall() passes whatever values were emitted
|
||||
by the previous function followed by the callback.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```js
|
||||
mod_vasync.waterfall([
|
||||
function func1(callback) {
|
||||
setImmediate(function () {
|
||||
callback(null, 37);
|
||||
});
|
||||
},
|
||||
function func2(extra, callback) {
|
||||
console.log('func2 got "%s" from func1', extra);
|
||||
callback();
|
||||
}
|
||||
], function () {
|
||||
console.log('done');
|
||||
});
|
||||
```
|
||||
|
||||
This prints:
|
||||
|
||||
```
|
||||
func2 got "37" from func1
|
||||
better stop early
|
||||
```
|
||||
|
||||
### barrier: coordinate multiple concurrent operations
|
||||
|
||||
Synopsis: `barrier([args])`
|
||||
|
||||
Returns a new barrier object. Like `parallel`, barriers are useful for
|
||||
coordinating several concurrent operations, but instead of specifying a list of
|
||||
functions to invoke, you just say how many (and optionally which ones) are
|
||||
outstanding, and this object emits `'drain'` when they've all completed. This
|
||||
is syntactically lighter-weight, and more flexible.
|
||||
|
||||
* Methods:
|
||||
|
||||
* start(name): Indicates that the named operation began. The name must not
|
||||
match an operation which is already ongoing.
|
||||
* done(name): Indicates that the named operation ended.
|
||||
|
||||
|
||||
* Read-only public properties (for debugging):
|
||||
|
||||
* pending: Set of pending operations. Keys are names passed to "start", and
|
||||
values are timestamps when the operation began.
|
||||
* recent: Array of recent completed operations. Each element is an object
|
||||
with a "name", "start", and "done" field. By default, 10 operations are
|
||||
remembered.
|
||||
|
||||
|
||||
* Options:
|
||||
|
||||
* nrecent: number of recent operations to remember (for debugging)
|
||||
|
||||
Example: printing sizes of files in a directory
|
||||
|
||||
```js
|
||||
var mod_fs = require('fs');
|
||||
var mod_path = require('path');
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
|
||||
var barrier = mod_vasync.barrier();
|
||||
|
||||
barrier.on('drain', function () {
|
||||
console.log('all files checked');
|
||||
});
|
||||
|
||||
barrier.start('readdir');
|
||||
|
||||
mod_fs.readdir(__dirname, function (err, files) {
|
||||
barrier.done('readdir');
|
||||
|
||||
if (err)
|
||||
throw (err);
|
||||
|
||||
files.forEach(function (file) {
|
||||
barrier.start('stat ' + file);
|
||||
|
||||
var path = mod_path.join(__dirname, file);
|
||||
|
||||
mod_fs.stat(path, function (err2, stat) {
|
||||
barrier.done('stat ' + file);
|
||||
console.log('%s: %d bytes', file, stat['size']);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
This emits:
|
||||
|
||||
barrier-readdir.js: 602 bytes
|
||||
foreach-parallel.js: 358 bytes
|
||||
barrier-basic.js: 552 bytes
|
||||
nofail.js: 384 bytes
|
||||
pipeline.js: 490 bytes
|
||||
parallel.js: 481 bytes
|
||||
queue-serializer.js: 441 bytes
|
||||
queue-stat.js: 529 bytes
|
||||
all files checked
|
||||
|
||||
|
||||
### queue/queuev: fixed-size worker queue
|
||||
|
||||
Synopsis: `queue(worker, concurrency)`
|
||||
|
||||
Synopsis: `queuev(args)`
|
||||
|
||||
This function returns an object that allows up to a fixed number of tasks to be
|
||||
dispatched at any given time. The interface is compatible with that provided
|
||||
by the "async" Node library, except that the returned object's fields represent
|
||||
a public interface you can use to introspect what's going on.
|
||||
|
||||
* Arguments
|
||||
|
||||
* worker: a function invoked as `worker(task, callback)`, where `task` is a
|
||||
task dispatched to this queue and `callback` should be invoked when the
|
||||
task completes.
|
||||
* concurrency: a positive integer indicating the maximum number of tasks
|
||||
that may be dispatched at any time. With concurrency = 1, the queue
|
||||
serializes all operations.
|
||||
|
||||
|
||||
* Methods
|
||||
|
||||
* push(task, [callback]): add a task (or array of tasks) to the queue, with
|
||||
an optional callback to be invoked when each task completes. If a list of
|
||||
tasks are added, the callback is invoked for each one.
|
||||
* length(): for compatibility with node-async.
|
||||
* close(): signal that no more tasks will be enqueued. Further attempts to
|
||||
enqueue tasks to this queue will throw. Once all pending and queued
|
||||
tasks are completed the object will emit the "end" event. The "end"
|
||||
event is the last event the queue will emit, and it will be emitted even
|
||||
if no tasks were ever enqueued.
|
||||
* kill(): clear enqueued tasks and implicitly close the queue. Several
|
||||
caveats apply when kill() is called:
|
||||
* The completion callback will _not_ be called for items purged from
|
||||
the queue.
|
||||
* The drain handler is cleared (for node-async compatibility)
|
||||
* Subsequent calls to kill() or close() are no-ops.
|
||||
* As with close(), it is not legal to call push() after kill().
|
||||
|
||||
|
||||
* Read-only public properties (for debugging):
|
||||
|
||||
* concurrency: for compatibility with node-async
|
||||
* worker: worker function, as passed into "queue"/"queuev"
|
||||
* worker\_name: worker function's "name" field
|
||||
* npending: the number of tasks currently being processed
|
||||
* pending: an object (*not* an array) describing the tasks currently being
|
||||
processed
|
||||
* queued: array of tasks currently queued for processing
|
||||
* closed: true when close() has been called on the queue
|
||||
* ended: true when all tasks have completed processing, and no more
|
||||
processing will occur
|
||||
* killed: true when kill() has been called on the queue
|
||||
|
||||
|
||||
* Hooks (for compatibility with node-async):
|
||||
|
||||
* saturated
|
||||
* empty
|
||||
* drain
|
||||
|
||||
* Events
|
||||
|
||||
* 'end': see close()
|
||||
|
||||
If the tasks are themselves simple objects, then the entire queue may be
|
||||
serialized (as via JSON.stringify) for debugging and monitoring tools. Using
|
||||
the above fields, you can see what this queue is doing (worker\_name), which
|
||||
tasks are queued, which tasks are being processed, and so on.
|
||||
|
||||
### Example 1: Stat several files
|
||||
|
||||
Here's an example demonstrating the queue:
|
||||
|
||||
```js
|
||||
var mod_fs = require('fs');
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
|
||||
var queue;
|
||||
|
||||
function doneOne()
|
||||
{
|
||||
console.log('task completed; queue state:\n%s\n',
|
||||
JSON.stringify(queue, null, 4));
|
||||
}
|
||||
|
||||
queue = mod_vasync.queue(mod_fs.stat, 2);
|
||||
|
||||
console.log('initial queue state:\n%s\n', JSON.stringify(queue, null, 4));
|
||||
|
||||
queue.push('/tmp/file1', doneOne);
|
||||
queue.push('/tmp/file2', doneOne);
|
||||
queue.push('/tmp/file3', doneOne);
|
||||
queue.push('/tmp/file4', doneOne);
|
||||
|
||||
console.log('all tasks dispatched:\n%s\n', JSON.stringify(queue, null, 4));
|
||||
```
|
||||
|
||||
The initial queue state looks like this:
|
||||
|
||||
```js
|
||||
initial queue state:
|
||||
{
|
||||
"nextid": 0,
|
||||
"worker_name": "anon",
|
||||
"npending": 0,
|
||||
"pending": {},
|
||||
"queued": [],
|
||||
"concurrency": 2
|
||||
}
|
||||
```
|
||||
After four tasks have been pushed, we see that two of them have been dispatched
|
||||
and the remaining two are queued up:
|
||||
|
||||
```js
|
||||
all tasks pushed:
|
||||
{
|
||||
"nextid": 4,
|
||||
"worker_name": "anon",
|
||||
"npending": 2,
|
||||
"pending": {
|
||||
"1": {
|
||||
"id": 1,
|
||||
"task": "/tmp/file1"
|
||||
},
|
||||
"2": {
|
||||
"id": 2,
|
||||
"task": "/tmp/file2"
|
||||
}
|
||||
},
|
||||
"queued": [
|
||||
{
|
||||
"id": 3,
|
||||
"task": "/tmp/file3"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"task": "/tmp/file4"
|
||||
}
|
||||
],
|
||||
"concurrency": 2
|
||||
}
|
||||
```
|
||||
|
||||
As they complete, we see tasks moving from "queued" to "pending", and completed
|
||||
tasks disappear:
|
||||
|
||||
```js
|
||||
task completed; queue state:
|
||||
{
|
||||
"nextid": 4,
|
||||
"worker_name": "anon",
|
||||
"npending": 1,
|
||||
"pending": {
|
||||
"3": {
|
||||
"id": 3,
|
||||
"task": "/tmp/file3"
|
||||
}
|
||||
},
|
||||
"queued": [
|
||||
{
|
||||
"id": 4,
|
||||
"task": "/tmp/file4"
|
||||
}
|
||||
],
|
||||
"concurrency": 2
|
||||
}
|
||||
```
|
||||
|
||||
When all tasks have completed, the queue state looks like it started:
|
||||
|
||||
```js
|
||||
task completed; queue state:
|
||||
{
|
||||
"nextid": 4,
|
||||
"worker_name": "anon",
|
||||
"npending": 0,
|
||||
"pending": {},
|
||||
"queued": [],
|
||||
"concurrency": 2
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Example 2: A simple serializer
|
||||
|
||||
You can use a queue with concurrency 1 and where the tasks are themselves
|
||||
functions to ensure that an arbitrary asynchronous function never runs
|
||||
concurrently with another one, no matter what each one does. Since the tasks
|
||||
are the actual functions to be invoked, the worker function just invokes each
|
||||
one:
|
||||
|
||||
```js
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
|
||||
var queue = mod_vasync.queue(
|
||||
function (task, callback) { task(callback); }, 1);
|
||||
|
||||
queue.push(function (callback) {
|
||||
console.log('first task begins');
|
||||
setTimeout(function () {
|
||||
console.log('first task ends');
|
||||
callback();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
queue.push(function (callback) {
|
||||
console.log('second task begins');
|
||||
process.nextTick(function () {
|
||||
console.log('second task ends');
|
||||
callback();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
This example outputs:
|
||||
|
||||
$ node examples/queue-serializer.js
|
||||
first task begins
|
||||
first task ends
|
||||
second task begins
|
||||
second task ends
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
|
||||
var barrier = mod_vasync.barrier();
|
||||
|
||||
barrier.on('drain', function () {
|
||||
console.log('barrier drained!');
|
||||
});
|
||||
|
||||
console.log('barrier', barrier);
|
||||
|
||||
barrier.start('op1');
|
||||
console.log('op1 started', barrier);
|
||||
|
||||
barrier.start('op2');
|
||||
console.log('op2 started', barrier);
|
||||
|
||||
barrier.done('op2');
|
||||
console.log('op2 done', barrier);
|
||||
|
||||
barrier.done('op1');
|
||||
console.log('op1 done', barrier);
|
||||
|
||||
barrier.start('op3');
|
||||
console.log('op3 started');
|
||||
|
||||
setTimeout(function () {
|
||||
barrier.done('op3');
|
||||
console.log('op3 done');
|
||||
}, 10);
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
var mod_fs = require('fs');
|
||||
var mod_path = require('path');
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
|
||||
var barrier = mod_vasync.barrier();
|
||||
|
||||
barrier.on('drain', function () {
|
||||
console.log('all files checked');
|
||||
});
|
||||
|
||||
barrier.start('readdir');
|
||||
|
||||
mod_fs.readdir(__dirname, function (err, files) {
|
||||
barrier.done('readdir');
|
||||
|
||||
if (err)
|
||||
throw (err);
|
||||
|
||||
files.forEach(function (file) {
|
||||
barrier.start('stat ' + file);
|
||||
|
||||
var path = mod_path.join(__dirname, file);
|
||||
|
||||
mod_fs.stat(path, function (err2, stat) {
|
||||
barrier.done('stat ' + file);
|
||||
console.log('%s: %d bytes', file, stat['size']);
|
||||
});
|
||||
});
|
||||
});
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
var mod_dns = require('dns');
|
||||
var mod_util = require('util');
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
|
||||
console.log(mod_vasync.forEachParallel({
|
||||
'func': mod_dns.resolve,
|
||||
'inputs': [ 'joyent.com', 'github.com', 'asdfaqsdfj.com' ]
|
||||
}, function (err, results) {
|
||||
console.log('error: %s', err.message);
|
||||
console.log('results: %s', mod_util.inspect(results, null, 3));
|
||||
}));
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
var mod_dns = require('dns');
|
||||
var mod_util = require('util');
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
|
||||
console.log(mod_vasync.forEachPipeline({
|
||||
'func': mod_dns.resolve,
|
||||
'inputs': [ 'joyent.com', 'github.com', 'asdfaqsdfj.com' ]
|
||||
}, function (err, results) {
|
||||
console.log('error: %s', err.message);
|
||||
console.log('results: %s', mod_util.inspect(results, null, 3));
|
||||
}));
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
var mod_util = require('util');
|
||||
var mod_fs = require('fs');
|
||||
|
||||
var status = mod_vasync.parallel({
|
||||
funcs: [
|
||||
function f1 (callback) { mod_fs.stat('/tmp', callback); },
|
||||
function f2 (callback) { mod_fs.stat('/var', callback); }
|
||||
]
|
||||
}, function (err, results) {
|
||||
console.log(err);
|
||||
console.log(mod_util.inspect(results, false, 8));
|
||||
});
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
var mod_dns = require('dns');
|
||||
var mod_util = require('util');
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
|
||||
console.log(mod_vasync.parallel({
|
||||
'funcs': [
|
||||
function f1 (callback) { mod_dns.resolve('joyent.com', callback); },
|
||||
function f2 (callback) { mod_dns.resolve('github.com', callback); },
|
||||
function f3 (callback) { mod_dns.resolve('asdfaqsdfj.com', callback); }
|
||||
]
|
||||
}, function (err, results) {
|
||||
console.log('error: %s', err.message);
|
||||
console.log('results: %s', mod_util.inspect(results, null, 3));
|
||||
}));
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
var mod_dns = require('dns');
|
||||
var mod_util = require('util');
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
|
||||
console.log(mod_vasync.pipeline({
|
||||
'funcs': [
|
||||
function f1 (_, callback) { mod_dns.resolve('joyent.com', callback); },
|
||||
function f2 (_, callback) { mod_dns.resolve('github.com', callback); },
|
||||
function f3 (_, callback) { mod_dns.resolve('asdfaqsdfj.com', callback); }
|
||||
]
|
||||
}, function (err, results) {
|
||||
console.log('error: %s', err.message);
|
||||
console.log('results: %s', mod_util.inspect(results, null, 3));
|
||||
}));
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
|
||||
var queue = mod_vasync.queue(function (task, callback) { task(callback); }, 1);
|
||||
|
||||
queue.push(function (callback) {
|
||||
console.log('first task begins');
|
||||
setTimeout(function () {
|
||||
console.log('first task ends');
|
||||
callback();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
queue.push(function (callback) {
|
||||
console.log('second task begins');
|
||||
process.nextTick(function () {
|
||||
console.log('second task ends');
|
||||
callback();
|
||||
});
|
||||
});
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
var mod_fs = require('fs');
|
||||
var mod_vasync = require('../lib/vasync');
|
||||
|
||||
var queue;
|
||||
|
||||
function doneOne()
|
||||
{
|
||||
console.log('task completed; queue state:\n%s\n',
|
||||
JSON.stringify(queue, null, 4));
|
||||
}
|
||||
|
||||
queue = mod_vasync.queue(mod_fs.stat, 2);
|
||||
|
||||
console.log('initial queue state:\n%s\n', JSON.stringify(queue, null, 4));
|
||||
|
||||
queue.push('/tmp/file1', doneOne);
|
||||
queue.push('/tmp/file2', doneOne);
|
||||
queue.push('/tmp/file3', doneOne);
|
||||
queue.push('/tmp/file4', doneOne);
|
||||
|
||||
console.log('all tasks pushed:\n%s\n', JSON.stringify(queue, null, 4));
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* examples/waterfall.js: simple waterfall example
|
||||
*/
|
||||
var mod_vasync = require('..');
|
||||
mod_vasync.waterfall([
|
||||
function func1(callback) {
|
||||
setImmediate(function () {
|
||||
callback(null, 37);
|
||||
});
|
||||
},
|
||||
function func2(extra, callback) {
|
||||
console.log('func2 got "%s" from func1', extra);
|
||||
callback();
|
||||
}
|
||||
], function () {
|
||||
console.log('done');
|
||||
});
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
#
|
||||
# Configuration File for JavaScript Lint
|
||||
#
|
||||
# This configuration file can be used to lint a collection of scripts, or to enable
|
||||
# or disable warnings for scripts that are linted via the command line.
|
||||
#
|
||||
|
||||
### Warnings
|
||||
# Enable or disable warnings based on requirements.
|
||||
# Use "+WarningName" to display or "-WarningName" to suppress.
|
||||
#
|
||||
+ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent
|
||||
+ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity
|
||||
+ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement
|
||||
+anon_no_return_value # anonymous function does not always return value
|
||||
+assign_to_function_call # assignment to a function call
|
||||
-block_without_braces # block statement without curly braces
|
||||
+comma_separated_stmts # multiple statements separated by commas (use semicolons?)
|
||||
+comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)
|
||||
+default_not_at_end # the default case is not at the end of the switch statement
|
||||
+dup_option_explicit # duplicate "option explicit" control comment
|
||||
+duplicate_case_in_switch # duplicate case in switch statement
|
||||
+duplicate_formal # duplicate formal argument {name}
|
||||
+empty_statement # empty statement or extra semicolon
|
||||
+identifier_hides_another # identifer {name} hides an identifier in a parent scope
|
||||
-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement
|
||||
+incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.
|
||||
+invalid_fallthru # unexpected "fallthru" control comment
|
||||
+invalid_pass # unexpected "pass" control comment
|
||||
+jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax
|
||||
+leading_decimal_point # leading decimal point may indicate a number or an object member
|
||||
+legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax
|
||||
+meaningless_block # meaningless block; curly braces have no impact
|
||||
+mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence
|
||||
+misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma
|
||||
+missing_break # missing break statement
|
||||
+missing_break_for_last_case # missing break statement for last case in switch
|
||||
+missing_default_case # missing default case in switch statement
|
||||
+missing_option_explicit # the "option explicit" control comment is missing
|
||||
+missing_semicolon # missing semicolon
|
||||
+missing_semicolon_for_lambda # missing semicolon for lambda assignment
|
||||
+multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs
|
||||
+nested_comment # nested comment
|
||||
+no_return_value # function {name} does not always return a value
|
||||
+octal_number # leading zeros make an octal number
|
||||
+parseint_missing_radix # parseInt missing radix parameter
|
||||
+partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag
|
||||
+redeclared_var # redeclaration of {name}
|
||||
+trailing_comma_in_array # extra comma is not recommended in array initializers
|
||||
+trailing_decimal_point # trailing decimal point may indicate a number or an object member
|
||||
+undeclared_identifier # undeclared identifier: {name}
|
||||
+unreachable_code # unreachable code
|
||||
-unreferenced_argument # argument declared but never referenced: {name}
|
||||
-unreferenced_function # function is declared but never referenced: {name}
|
||||
+unreferenced_variable # variable is declared but never referenced: {name}
|
||||
+unsupported_version # JavaScript {version} is not supported
|
||||
+use_of_label # use of label
|
||||
+useless_assign # useless assignment
|
||||
+useless_comparison # useless comparison; comparing identical expressions
|
||||
-useless_quotes # the quotation marks are unnecessary
|
||||
+useless_void # use of the void type may be unnecessary (void is always undefined)
|
||||
+var_hides_arg # variable {name} hides argument
|
||||
+want_assign_or_call # expected an assignment or function call
|
||||
+with_statement # with statement hides undeclared variables; use temporary variable instead
|
||||
|
||||
|
||||
### Output format
|
||||
# Customize the format of the error message.
|
||||
# __FILE__ indicates current file path
|
||||
# __FILENAME__ indicates current file name
|
||||
# __LINE__ indicates current line
|
||||
# __COL__ indicates current column
|
||||
# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__)
|
||||
# __ERROR_NAME__ indicates error name (used in configuration file)
|
||||
# __ERROR_PREFIX__ indicates error prefix
|
||||
# __ERROR_MSG__ indicates error message
|
||||
#
|
||||
# For machine-friendly output, the output format can be prefixed with
|
||||
# "encode:". If specified, all items will be encoded with C-slashes.
|
||||
#
|
||||
# Visual Studio syntax (default):
|
||||
+output-format __FILE__(__LINE__): __ERROR__
|
||||
# Alternative syntax:
|
||||
#+output-format __FILE__:__LINE__: __ERROR__
|
||||
|
||||
|
||||
### Context
|
||||
# Show the in-line position of the error.
|
||||
# Use "+context" to display or "-context" to suppress.
|
||||
#
|
||||
+context
|
||||
|
||||
|
||||
### Control Comments
|
||||
# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for
|
||||
# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is
|
||||
# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason,
|
||||
# although legacy control comments are enabled by default for backward compatibility.
|
||||
#
|
||||
-legacy_control_comments
|
||||
|
||||
|
||||
### Defining identifiers
|
||||
# By default, "option explicit" is enabled on a per-file basis.
|
||||
# To enable this for all files, use "+always_use_option_explicit"
|
||||
-always_use_option_explicit
|
||||
|
||||
# Define certain identifiers of which the lint is not aware.
|
||||
# (Use this in conjunction with the "undeclared identifier" warning.)
|
||||
#
|
||||
# Common uses for webpages might be:
|
||||
+define __dirname
|
||||
+define clearInterval
|
||||
+define clearTimeout
|
||||
+define console
|
||||
+define exports
|
||||
+define global
|
||||
+define process
|
||||
+define require
|
||||
+define setImmediate
|
||||
+define setInterval
|
||||
+define setTimeout
|
||||
+define Buffer
|
||||
+define JSON
|
||||
+define Math
|
||||
|
||||
### JavaScript Version
|
||||
# To change the default JavaScript version:
|
||||
#+default-type text/javascript;version=1.5
|
||||
#+default-type text/javascript;e4x=1
|
||||
|
||||
### Files
|
||||
# Specify which files to lint
|
||||
# Use "+recurse" to enable recursion (disabled by default).
|
||||
# To add a set of files, use "+process FileName", "+process Folder\Path\*.js",
|
||||
# or "+process Folder\Path\*.htm".
|
||||
#
|
||||
|
||||
+610
@@ -0,0 +1,610 @@
|
||||
/*
|
||||
* vasync.js: utilities for observable asynchronous control flow
|
||||
*/
|
||||
|
||||
var mod_assert = require('assert');
|
||||
var mod_events = require('events');
|
||||
var mod_util = require('util');
|
||||
var mod_verror = require('verror');
|
||||
|
||||
/*
|
||||
* Public interface
|
||||
*/
|
||||
exports.parallel = parallel;
|
||||
exports.forEachParallel = forEachParallel;
|
||||
exports.pipeline = pipeline;
|
||||
exports.forEachPipeline = forEachPipeline;
|
||||
exports.queue = queue;
|
||||
exports.queuev = queuev;
|
||||
exports.barrier = barrier;
|
||||
exports.waterfall = waterfall;
|
||||
|
||||
if (!global.setImmediate) {
|
||||
global.setImmediate = function (func) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
args.unshift(0);
|
||||
args.unshift(func);
|
||||
setTimeout.apply(this, args);
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* This is incorporated here from jsprim because jsprim ends up pulling in a lot
|
||||
* of dependencies. If we end up needing more from jsprim, though, we should
|
||||
* add it back and rip out this function.
|
||||
*/
|
||||
function isEmpty(obj)
|
||||
{
|
||||
var key;
|
||||
for (key in obj)
|
||||
return (false);
|
||||
return (true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a set of functions that complete asynchronously using the standard
|
||||
* callback(err, result) pattern, invoke them all and merge the results. See
|
||||
* README.md for details.
|
||||
*/
|
||||
function parallel(args, callback)
|
||||
{
|
||||
var funcs, rv, doneOne, i;
|
||||
|
||||
mod_assert.equal(typeof (args), 'object', '"args" must be an object');
|
||||
mod_assert.ok(Array.isArray(args['funcs']),
|
||||
'"args.funcs" must be specified and must be an array');
|
||||
mod_assert.equal(typeof (callback), 'function',
|
||||
'callback argument must be specified and must be a function');
|
||||
|
||||
funcs = args['funcs'].slice(0);
|
||||
|
||||
rv = {
|
||||
'operations': new Array(funcs.length),
|
||||
'successes': [],
|
||||
'ndone': 0,
|
||||
'nerrors': 0
|
||||
};
|
||||
|
||||
if (funcs.length === 0) {
|
||||
setImmediate(function () { callback(null, rv); });
|
||||
return (rv);
|
||||
}
|
||||
|
||||
doneOne = function (entry) {
|
||||
return (function (err, result) {
|
||||
mod_assert.equal(entry['status'], 'pending');
|
||||
|
||||
entry['err'] = err;
|
||||
entry['result'] = result;
|
||||
entry['status'] = err ? 'fail' : 'ok';
|
||||
|
||||
if (err)
|
||||
rv['nerrors']++;
|
||||
else
|
||||
rv['successes'].push(result);
|
||||
|
||||
if (++rv['ndone'] < funcs.length)
|
||||
return;
|
||||
|
||||
var errors = rv['operations'].filter(function (ent) {
|
||||
return (ent['status'] == 'fail');
|
||||
}).map(function (ent) { return (ent['err']); });
|
||||
|
||||
if (errors.length > 0)
|
||||
callback(new mod_verror.MultiError(errors), rv);
|
||||
else
|
||||
callback(null, rv);
|
||||
});
|
||||
};
|
||||
|
||||
for (i = 0; i < funcs.length; i++) {
|
||||
rv['operations'][i] = {
|
||||
'func': funcs[i],
|
||||
'funcname': funcs[i].name || '(anon)',
|
||||
'status': 'pending'
|
||||
};
|
||||
|
||||
funcs[i](doneOne(rv['operations'][i]));
|
||||
}
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exactly like parallel, except that the input is specified as a single
|
||||
* function to invoke on N different inputs (rather than N functions). "args"
|
||||
* must have the following fields:
|
||||
*
|
||||
* func asynchronous function to invoke on each input value
|
||||
*
|
||||
* inputs array of input values
|
||||
*/
|
||||
function forEachParallel(args, callback)
|
||||
{
|
||||
var func, funcs;
|
||||
|
||||
mod_assert.equal(typeof (args), 'object', '"args" must be an object');
|
||||
mod_assert.equal(typeof (args['func']), 'function',
|
||||
'"args.func" must be specified and must be a function');
|
||||
mod_assert.ok(Array.isArray(args['inputs']),
|
||||
'"args.inputs" must be specified and must be an array');
|
||||
|
||||
func = args['func'];
|
||||
funcs = args['inputs'].map(function (input) {
|
||||
return (function (subcallback) {
|
||||
return (func(input, subcallback));
|
||||
});
|
||||
});
|
||||
|
||||
return (parallel({ 'funcs': funcs }, callback));
|
||||
}
|
||||
|
||||
/*
|
||||
* Like parallel, but invokes functions in sequence rather than in parallel
|
||||
* and aborts if any function exits with failure. Arguments include:
|
||||
*
|
||||
* funcs invoke the functions in parallel
|
||||
*
|
||||
* arg first argument to each pipeline function
|
||||
*/
|
||||
function pipeline(args, callback)
|
||||
{
|
||||
var funcs, uarg, rv, next;
|
||||
|
||||
mod_assert.equal(typeof (args), 'object', '"args" must be an object');
|
||||
mod_assert.ok(Array.isArray(args['funcs']),
|
||||
'"args.funcs" must be specified and must be an array');
|
||||
|
||||
funcs = args['funcs'].slice(0);
|
||||
uarg = args['arg'];
|
||||
|
||||
rv = {
|
||||
'operations': funcs.map(function (func) {
|
||||
return ({
|
||||
'func': func,
|
||||
'funcname': func.name || '(anon)',
|
||||
'status': 'waiting'
|
||||
});
|
||||
}),
|
||||
'successes': [],
|
||||
'ndone': 0,
|
||||
'nerrors': 0
|
||||
};
|
||||
|
||||
if (funcs.length === 0) {
|
||||
setImmediate(function () { callback(null, rv); });
|
||||
return (rv);
|
||||
}
|
||||
|
||||
next = function (err, result) {
|
||||
if (rv['nerrors'] > 0 ||
|
||||
rv['ndone'] >= rv['operations'].length) {
|
||||
throw new mod_verror.VError('pipeline callback ' +
|
||||
'invoked after the pipeline has already ' +
|
||||
'completed (%j)', rv);
|
||||
}
|
||||
|
||||
var entry = rv['operations'][rv['ndone']++];
|
||||
|
||||
mod_assert.equal(entry['status'], 'pending');
|
||||
|
||||
entry['status'] = err ? 'fail' : 'ok';
|
||||
entry['err'] = err;
|
||||
entry['result'] = result;
|
||||
|
||||
if (err)
|
||||
rv['nerrors']++;
|
||||
else
|
||||
rv['successes'].push(result);
|
||||
|
||||
if (err || rv['ndone'] == funcs.length) {
|
||||
callback(err, rv);
|
||||
} else {
|
||||
var nextent = rv['operations'][rv['ndone']];
|
||||
nextent['status'] = 'pending';
|
||||
|
||||
/*
|
||||
* We invoke the next function on the next tick so that
|
||||
* the caller (stage N) need not worry about the case
|
||||
* that the next stage (stage N + 1) runs in its own
|
||||
* context.
|
||||
*/
|
||||
setImmediate(function () {
|
||||
nextent['func'](uarg, next);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
rv['operations'][0]['status'] = 'pending';
|
||||
funcs[0](uarg, next);
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exactly like pipeline, except that the input is specified as a single
|
||||
* function to invoke on N different inputs (rather than N functions). "args"
|
||||
* must have the following fields:
|
||||
*
|
||||
* func asynchronous function to invoke on each input value
|
||||
*
|
||||
* inputs array of input values
|
||||
*/
|
||||
function forEachPipeline(args, callback) {
|
||||
mod_assert.equal(typeof (args), 'object', '"args" must be an object');
|
||||
mod_assert.equal(typeof (args['func']), 'function',
|
||||
'"args.func" must be specified and must be a function');
|
||||
mod_assert.ok(Array.isArray(args['inputs']),
|
||||
'"args.inputs" must be specified and must be an array');
|
||||
mod_assert.equal(typeof (callback), 'function',
|
||||
'callback argument must be specified and must be a function');
|
||||
|
||||
var func = args['func'];
|
||||
|
||||
var funcs = args['inputs'].map(function (input) {
|
||||
return (function (_, subcallback) {
|
||||
return (func(input, subcallback));
|
||||
});
|
||||
});
|
||||
|
||||
return (pipeline({'funcs': funcs}, callback));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* async-compatible "queue" function.
|
||||
*/
|
||||
function queue(worker, concurrency)
|
||||
{
|
||||
return (new WorkQueue({
|
||||
'worker': worker,
|
||||
'concurrency': concurrency
|
||||
}));
|
||||
}
|
||||
|
||||
function queuev(args)
|
||||
{
|
||||
return (new WorkQueue(args));
|
||||
}
|
||||
|
||||
function WorkQueue(args)
|
||||
{
|
||||
mod_assert.ok(args.hasOwnProperty('worker'));
|
||||
mod_assert.equal(typeof (args['worker']), 'function');
|
||||
mod_assert.ok(args.hasOwnProperty('concurrency'));
|
||||
mod_assert.equal(typeof (args['concurrency']), 'number');
|
||||
mod_assert.equal(Math.floor(args['concurrency']), args['concurrency']);
|
||||
mod_assert.ok(args['concurrency'] > 0);
|
||||
|
||||
mod_events.EventEmitter.call(this);
|
||||
|
||||
this.nextid = 0;
|
||||
this.worker = args['worker'];
|
||||
this.worker_name = args['worker'].name || 'anon';
|
||||
this.npending = 0;
|
||||
this.pending = {};
|
||||
this.queued = [];
|
||||
this.closed = false;
|
||||
this.ended = false;
|
||||
|
||||
/* user-settable fields inherited from "async" interface */
|
||||
this.concurrency = args['concurrency'];
|
||||
this.saturated = undefined;
|
||||
this.empty = undefined;
|
||||
this.drain = undefined;
|
||||
}
|
||||
|
||||
mod_util.inherits(WorkQueue, mod_events.EventEmitter);
|
||||
|
||||
WorkQueue.prototype.push = function (tasks, callback)
|
||||
{
|
||||
if (!Array.isArray(tasks))
|
||||
return (this.pushOne(tasks, callback));
|
||||
|
||||
var wq = this;
|
||||
return (tasks.map(function (task) {
|
||||
return (wq.pushOne(task, callback));
|
||||
}));
|
||||
};
|
||||
|
||||
WorkQueue.prototype.updateConcurrency = function (concurrency)
|
||||
{
|
||||
if (this.closed)
|
||||
throw new mod_verror.VError(
|
||||
'update concurrency invoked after queue closed');
|
||||
this.concurrency = concurrency;
|
||||
this.dispatchNext();
|
||||
};
|
||||
|
||||
WorkQueue.prototype.close = function ()
|
||||
{
|
||||
var wq = this;
|
||||
|
||||
if (wq.closed)
|
||||
return;
|
||||
wq.closed = true;
|
||||
|
||||
/*
|
||||
* If the queue is already empty, just fire the "end" event on the
|
||||
* next tick.
|
||||
*/
|
||||
if (wq.npending === 0 && wq.queued.length === 0) {
|
||||
setImmediate(function () {
|
||||
if (!wq.ended) {
|
||||
wq.ended = true;
|
||||
wq.emit('end');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/* private */
|
||||
WorkQueue.prototype.pushOne = function (task, callback)
|
||||
{
|
||||
if (this.closed)
|
||||
throw new mod_verror.VError('push invoked after queue closed');
|
||||
|
||||
var id = ++this.nextid;
|
||||
var entry = { 'id': id, 'task': task, 'callback': callback };
|
||||
|
||||
this.queued.push(entry);
|
||||
this.dispatchNext();
|
||||
|
||||
return (id);
|
||||
};
|
||||
|
||||
/* private */
|
||||
WorkQueue.prototype.dispatchNext = function ()
|
||||
{
|
||||
var wq = this;
|
||||
if (wq.npending === 0 && wq.queued.length === 0) {
|
||||
if (wq.drain)
|
||||
wq.drain();
|
||||
wq.emit('drain');
|
||||
/*
|
||||
* The queue is closed; emit the final "end"
|
||||
* event before we come to rest:
|
||||
*/
|
||||
if (wq.closed) {
|
||||
wq.ended = true;
|
||||
wq.emit('end');
|
||||
}
|
||||
} else if (wq.queued.length > 0) {
|
||||
while (wq.queued.length > 0 && wq.npending < wq.concurrency) {
|
||||
var next = wq.queued.shift();
|
||||
wq.dispatch(next);
|
||||
|
||||
if (wq.queued.length === 0) {
|
||||
if (wq.empty)
|
||||
wq.empty();
|
||||
wq.emit('empty');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WorkQueue.prototype.dispatch = function (entry)
|
||||
{
|
||||
var wq = this;
|
||||
|
||||
mod_assert.ok(!this.pending.hasOwnProperty(entry['id']));
|
||||
mod_assert.ok(this.npending < this.concurrency);
|
||||
mod_assert.ok(!this.ended);
|
||||
|
||||
this.npending++;
|
||||
this.pending[entry['id']] = entry;
|
||||
|
||||
if (this.npending === this.concurrency) {
|
||||
if (this.saturated)
|
||||
this.saturated();
|
||||
this.emit('saturated');
|
||||
}
|
||||
|
||||
/*
|
||||
* We invoke the worker function on the next tick so that callers can
|
||||
* always assume that the callback is NOT invoked during the call to
|
||||
* push() even if the queue is not at capacity. It also avoids O(n)
|
||||
* stack usage when used with synchronous worker functions.
|
||||
*/
|
||||
setImmediate(function () {
|
||||
wq.worker(entry['task'], function (err) {
|
||||
--wq.npending;
|
||||
delete (wq.pending[entry['id']]);
|
||||
|
||||
if (entry['callback'])
|
||||
entry['callback'].apply(null, arguments);
|
||||
|
||||
wq.dispatchNext();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
WorkQueue.prototype.length = function ()
|
||||
{
|
||||
return (this.queued.length);
|
||||
};
|
||||
|
||||
WorkQueue.prototype.kill = function ()
|
||||
{
|
||||
this.killed = true;
|
||||
this.queued = [];
|
||||
this.drain = undefined;
|
||||
this.close();
|
||||
};
|
||||
|
||||
/*
|
||||
* Barriers coordinate multiple concurrent operations.
|
||||
*/
|
||||
function barrier(args)
|
||||
{
|
||||
return (new Barrier(args));
|
||||
}
|
||||
|
||||
function Barrier(args)
|
||||
{
|
||||
mod_assert.ok(!args || !args['nrecent'] ||
|
||||
typeof (args['nrecent']) == 'number',
|
||||
'"nrecent" must have type "number"');
|
||||
|
||||
mod_events.EventEmitter.call(this);
|
||||
|
||||
var nrecent = args && args['nrecent'] ? args['nrecent'] : 10;
|
||||
|
||||
if (nrecent > 0) {
|
||||
this.nrecent = nrecent;
|
||||
this.recent = [];
|
||||
}
|
||||
|
||||
this.pending = {};
|
||||
this.scheduled = false;
|
||||
}
|
||||
|
||||
mod_util.inherits(Barrier, mod_events.EventEmitter);
|
||||
|
||||
Barrier.prototype.start = function (name)
|
||||
{
|
||||
mod_assert.ok(!this.pending.hasOwnProperty(name),
|
||||
'operation "' + name + '" is already pending');
|
||||
this.pending[name] = Date.now();
|
||||
};
|
||||
|
||||
Barrier.prototype.done = function (name)
|
||||
{
|
||||
mod_assert.ok(this.pending.hasOwnProperty(name),
|
||||
'operation "' + name + '" is not pending');
|
||||
|
||||
if (this.recent) {
|
||||
this.recent.push({
|
||||
'name': name,
|
||||
'start': this.pending[name],
|
||||
'done': Date.now()
|
||||
});
|
||||
|
||||
if (this.recent.length > this.nrecent)
|
||||
this.recent.shift();
|
||||
}
|
||||
|
||||
delete (this.pending[name]);
|
||||
|
||||
/*
|
||||
* If we executed at least one operation and we're now empty, we should
|
||||
* emit "drain". But most code doesn't deal well with events being
|
||||
* processed while they're executing, so we actually schedule this event
|
||||
* for the next tick.
|
||||
*
|
||||
* We use the "scheduled" flag to avoid emitting multiple "drain" events
|
||||
* on consecutive ticks if the user starts and ends another task during
|
||||
* this tick.
|
||||
*/
|
||||
if (!isEmpty(this.pending) || this.scheduled)
|
||||
return;
|
||||
|
||||
this.scheduled = true;
|
||||
|
||||
var self = this;
|
||||
|
||||
setImmediate(function () {
|
||||
self.scheduled = false;
|
||||
|
||||
/*
|
||||
* It's also possible that the user has started another task on
|
||||
* the previous tick, in which case we really shouldn't emit
|
||||
* "drain".
|
||||
*/
|
||||
if (isEmpty(self.pending))
|
||||
self.emit('drain');
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* waterfall([ funcs ], callback): invoke each of the asynchronous functions
|
||||
* "funcs" in series. Each function is passed any values emitted by the
|
||||
* previous function (none for the first function), followed by the callback to
|
||||
* invoke upon completion. This callback must be invoked exactly once,
|
||||
* regardless of success or failure. As conventional in Node, the first
|
||||
* argument to the callback indicates an error (if non-null). Subsequent
|
||||
* arguments are passed to the next function in the "funcs" chain.
|
||||
*
|
||||
* If any function fails (i.e., calls its callback with an Error), then the
|
||||
* remaining functions are not invoked and "callback" is invoked with the error.
|
||||
*
|
||||
* The only difference between waterfall() and pipeline() are the arguments
|
||||
* passed to each function in the chain. pipeline() always passes the same
|
||||
* argument followed by the callback, while waterfall() passes whatever values
|
||||
* were emitted by the previous function followed by the callback.
|
||||
*/
|
||||
function waterfall(funcs, callback)
|
||||
{
|
||||
var rv, current, next;
|
||||
|
||||
mod_assert.ok(Array.isArray(funcs));
|
||||
mod_assert.ok(arguments.length == 1 || typeof (callback) == 'function');
|
||||
funcs = funcs.slice(0);
|
||||
|
||||
rv = {
|
||||
'operations': funcs.map(function (func) {
|
||||
return ({
|
||||
'func': func,
|
||||
'funcname': func.name || '(anon)',
|
||||
'status': 'waiting'
|
||||
});
|
||||
}),
|
||||
'successes': [],
|
||||
'ndone': 0,
|
||||
'nerrors': 0
|
||||
};
|
||||
|
||||
if (funcs.length === 0) {
|
||||
if (callback)
|
||||
setImmediate(function () { callback(null, rv); });
|
||||
return (rv);
|
||||
}
|
||||
|
||||
next = function (idx, err) {
|
||||
var args, entry, nextentry;
|
||||
|
||||
if (err === undefined)
|
||||
err = null;
|
||||
|
||||
if (idx != current) {
|
||||
throw (new mod_verror.VError(
|
||||
'vasync.waterfall: function %d ("%s") invoked ' +
|
||||
'its callback twice', idx,
|
||||
rv['operations'][idx].funcname));
|
||||
}
|
||||
|
||||
mod_assert.equal(idx, rv['ndone']);
|
||||
entry = rv['operations'][rv['ndone']++];
|
||||
args = Array.prototype.slice.call(arguments, 2);
|
||||
|
||||
mod_assert.equal(entry['status'], 'pending');
|
||||
entry['status'] = err ? 'fail' : 'ok';
|
||||
entry['err'] = err;
|
||||
entry['results'] = args;
|
||||
|
||||
if (err)
|
||||
rv['nerrors']++;
|
||||
else
|
||||
rv['successes'].push(args);
|
||||
|
||||
if (err || rv['ndone'] == funcs.length) {
|
||||
if (callback) {
|
||||
args.unshift(err);
|
||||
callback.apply(null, args);
|
||||
}
|
||||
} else {
|
||||
nextentry = rv['operations'][rv['ndone']];
|
||||
nextentry['status'] = 'pending';
|
||||
current++;
|
||||
args.push(next.bind(null, current));
|
||||
setImmediate(function () {
|
||||
nextentry['func'].apply(null, args);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
rv['operations'][0]['status'] = 'pending';
|
||||
current = 0;
|
||||
funcs[0](next.bind(null, current));
|
||||
return (rv);
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#
|
||||
# Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
#
|
||||
# Makefile: top-level Makefile
|
||||
#
|
||||
# This Makefile contains only repo-specific logic and uses included makefiles
|
||||
# to supply common targets (javascriptlint, jsstyle, restdown, etc.), which are
|
||||
# used by other repos as well.
|
||||
#
|
||||
|
||||
#
|
||||
# Tools
|
||||
#
|
||||
NPM = npm
|
||||
|
||||
#
|
||||
# Files
|
||||
#
|
||||
JS_FILES := $(shell find lib examples tests -name '*.js')
|
||||
JSL_FILES_NODE = $(JS_FILES)
|
||||
JSSTYLE_FILES = $(JS_FILES)
|
||||
JSL_CONF_NODE = jsl.node.conf
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
$(NPM) install
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
node tests/tst.inherit.js
|
||||
node tests/tst.verror.js
|
||||
node tests/tst.werror.js
|
||||
node tests/tst.serror.js
|
||||
@echo all tests passed
|
||||
|
||||
include ./Makefile.targ
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
# -*- mode: makefile -*-
|
||||
#
|
||||
# Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
#
|
||||
# Makefile.targ: common targets.
|
||||
#
|
||||
# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped
|
||||
# into other repos as-is without requiring any modifications. If you find
|
||||
# yourself changing this file, you should instead update the original copy in
|
||||
# eng.git and then update your repo to use the new version.
|
||||
#
|
||||
# This Makefile defines several useful targets and rules. You can use it by
|
||||
# including it from a Makefile that specifies some of the variables below.
|
||||
#
|
||||
# Targets defined in this Makefile:
|
||||
#
|
||||
# check Checks JavaScript files for lint and style
|
||||
# Checks bash scripts for syntax
|
||||
# Checks SMF manifests for validity against the SMF DTD
|
||||
#
|
||||
# clean Removes built files
|
||||
#
|
||||
# docs Builds restdown documentation in docs/
|
||||
#
|
||||
# prepush Depends on "check" and "test"
|
||||
#
|
||||
# test Does nothing (you should override this)
|
||||
#
|
||||
# xref Generates cscope (source cross-reference index)
|
||||
#
|
||||
# For details on what these targets are supposed to do, see the Joyent
|
||||
# Engineering Guide.
|
||||
#
|
||||
# To make use of these targets, you'll need to set some of these variables. Any
|
||||
# variables left unset will simply not be used.
|
||||
#
|
||||
# BASH_FILES Bash scripts to check for syntax
|
||||
# (paths relative to top-level Makefile)
|
||||
#
|
||||
# CLEAN_FILES Files to remove as part of the "clean" target. Note
|
||||
# that files generated by targets in this Makefile are
|
||||
# automatically included in CLEAN_FILES. These include
|
||||
# restdown-generated HTML and JSON files.
|
||||
#
|
||||
# DOC_FILES Restdown (documentation source) files. These are
|
||||
# assumed to be contained in "docs/", and must NOT
|
||||
# contain the "docs/" prefix.
|
||||
#
|
||||
# JSL_CONF_NODE Specify JavaScriptLint configuration files
|
||||
# JSL_CONF_WEB (paths relative to top-level Makefile)
|
||||
#
|
||||
# Node.js and Web configuration files are separate
|
||||
# because you'll usually want different global variable
|
||||
# configurations. If no file is specified, none is given
|
||||
# to jsl, which causes it to use a default configuration,
|
||||
# which probably isn't what you want.
|
||||
#
|
||||
# JSL_FILES_NODE JavaScript files to check with Node config file.
|
||||
# JSL_FILES_WEB JavaScript files to check with Web config file.
|
||||
#
|
||||
# You can also override these variables:
|
||||
#
|
||||
# BASH Path to bash (default: bash)
|
||||
#
|
||||
# CSCOPE_DIRS Directories to search for source files for the cscope
|
||||
# index. (default: ".")
|
||||
#
|
||||
# JSL Path to JavaScriptLint (default: "jsl")
|
||||
#
|
||||
# JSL_FLAGS_NODE Additional flags to pass through to JSL
|
||||
# JSL_FLAGS_WEB
|
||||
# JSL_FLAGS
|
||||
#
|
||||
# JSSTYLE Path to jsstyle (default: jsstyle)
|
||||
#
|
||||
# JSSTYLE_FLAGS Additional flags to pass through to jsstyle
|
||||
#
|
||||
|
||||
#
|
||||
# Defaults for the various tools we use.
|
||||
#
|
||||
BASH ?= bash
|
||||
BASHSTYLE ?= tools/bashstyle
|
||||
CP ?= cp
|
||||
CSCOPE ?= cscope
|
||||
CSCOPE_DIRS ?= .
|
||||
JSL ?= jsl
|
||||
JSSTYLE ?= jsstyle
|
||||
MKDIR ?= mkdir -p
|
||||
MV ?= mv
|
||||
RESTDOWN_FLAGS ?=
|
||||
RMTREE ?= rm -rf
|
||||
JSL_FLAGS ?= --nologo --nosummary
|
||||
|
||||
ifeq ($(shell uname -s),SunOS)
|
||||
TAR ?= gtar
|
||||
else
|
||||
TAR ?= tar
|
||||
endif
|
||||
|
||||
|
||||
#
|
||||
# Defaults for other fixed values.
|
||||
#
|
||||
BUILD = build
|
||||
DISTCLEAN_FILES += $(BUILD)
|
||||
DOC_BUILD = $(BUILD)/docs/public
|
||||
|
||||
#
|
||||
# Configure JSL_FLAGS_{NODE,WEB} based on JSL_CONF_{NODE,WEB}.
|
||||
#
|
||||
ifneq ($(origin JSL_CONF_NODE), undefined)
|
||||
JSL_FLAGS_NODE += --conf=$(JSL_CONF_NODE)
|
||||
endif
|
||||
|
||||
ifneq ($(origin JSL_CONF_WEB), undefined)
|
||||
JSL_FLAGS_WEB += --conf=$(JSL_CONF_WEB)
|
||||
endif
|
||||
|
||||
#
|
||||
# Targets. For descriptions on what these are supposed to do, see the
|
||||
# Joyent Engineering Guide.
|
||||
#
|
||||
|
||||
#
|
||||
# Instruct make to keep around temporary files. We have rules below that
|
||||
# automatically update git submodules as needed, but they employ a deps/*/.git
|
||||
# temporary file. Without this directive, make tries to remove these .git
|
||||
# directories after the build has completed.
|
||||
#
|
||||
.SECONDARY: $($(wildcard deps/*):%=%/.git)
|
||||
|
||||
#
|
||||
# This rule enables other rules that use files from a git submodule to have
|
||||
# those files depend on deps/module/.git and have "make" automatically check
|
||||
# out the submodule as needed.
|
||||
#
|
||||
deps/%/.git:
|
||||
git submodule update --init deps/$*
|
||||
|
||||
#
|
||||
# These recipes make heavy use of dynamically-created phony targets. The parent
|
||||
# Makefile defines a list of input files like BASH_FILES. We then say that each
|
||||
# of these files depends on a fake target called filename.bashchk, and then we
|
||||
# define a pattern rule for those targets that runs bash in check-syntax-only
|
||||
# mode. This mechanism has the nice properties that if you specify zero files,
|
||||
# the rule becomes a noop (unlike a single rule to check all bash files, which
|
||||
# would invoke bash with zero files), and you can check individual files from
|
||||
# the command line with "make filename.bashchk".
|
||||
#
|
||||
.PHONY: check-bash
|
||||
check-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle)
|
||||
|
||||
%.bashchk: %
|
||||
$(BASH) -n $^
|
||||
|
||||
%.bashstyle: %
|
||||
$(BASHSTYLE) $^
|
||||
|
||||
.PHONY: check-jsl check-jsl-node check-jsl-web
|
||||
check-jsl: check-jsl-node check-jsl-web
|
||||
|
||||
check-jsl-node: $(JSL_FILES_NODE:%=%.jslnodechk)
|
||||
|
||||
check-jsl-web: $(JSL_FILES_WEB:%=%.jslwebchk)
|
||||
|
||||
%.jslnodechk: % $(JSL_EXEC)
|
||||
$(JSL) $(JSL_FLAGS) $(JSL_FLAGS_NODE) $<
|
||||
|
||||
%.jslwebchk: % $(JSL_EXEC)
|
||||
$(JSL) $(JSL_FLAGS) $(JSL_FLAGS_WEB) $<
|
||||
|
||||
.PHONY: check-jsstyle
|
||||
check-jsstyle: $(JSSTYLE_FILES:%=%.jsstylechk)
|
||||
|
||||
%.jsstylechk: % $(JSSTYLE_EXEC)
|
||||
$(JSSTYLE) $(JSSTYLE_FLAGS) $<
|
||||
|
||||
.PHONY: check
|
||||
check: check-jsl check-jsstyle check-bash
|
||||
@echo check ok
|
||||
|
||||
.PHONY: clean
|
||||
clean::
|
||||
-$(RMTREE) $(CLEAN_FILES)
|
||||
|
||||
.PHONY: distclean
|
||||
distclean:: clean
|
||||
-$(RMTREE) $(DISTCLEAN_FILES)
|
||||
|
||||
CSCOPE_FILES = cscope.in.out cscope.out cscope.po.out
|
||||
CLEAN_FILES += $(CSCOPE_FILES)
|
||||
|
||||
.PHONY: xref
|
||||
xref: cscope.files
|
||||
$(CSCOPE) -bqR
|
||||
|
||||
.PHONY: cscope.files
|
||||
cscope.files:
|
||||
find $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \
|
||||
-o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@
|
||||
|
||||
#
|
||||
# The "docs" target is complicated because we do several things here:
|
||||
#
|
||||
# (1) Use restdown to build HTML and JSON files from each of DOC_FILES.
|
||||
#
|
||||
# (2) Copy these files into $(DOC_BUILD) (build/docs/public), which
|
||||
# functions as a complete copy of the documentation that could be
|
||||
# mirrored or served over HTTP.
|
||||
#
|
||||
# (3) Then copy any directories and media from docs/media into
|
||||
# $(DOC_BUILD)/media. This allows projects to include their own media,
|
||||
# including files that will override same-named files provided by
|
||||
# restdown.
|
||||
#
|
||||
# Step (3) is the surprisingly complex part: in order to do this, we need to
|
||||
# identify the subdirectories in docs/media, recreate them in
|
||||
# $(DOC_BUILD)/media, then do the same with the files.
|
||||
#
|
||||
DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$")
|
||||
DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%)
|
||||
DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%)
|
||||
|
||||
DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null)
|
||||
DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%)
|
||||
DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%)
|
||||
|
||||
#
|
||||
# Like the other targets, "docs" just depends on the final files we want to
|
||||
# create in $(DOC_BUILD), leveraging other targets and recipes to define how
|
||||
# to get there.
|
||||
#
|
||||
.PHONY: docs
|
||||
docs: \
|
||||
$(DOC_FILES:%.restdown=$(DOC_BUILD)/%.html) \
|
||||
$(DOC_FILES:%.restdown=$(DOC_BUILD)/%.json) \
|
||||
$(DOC_MEDIA_FILES_BUILD)
|
||||
|
||||
#
|
||||
# We keep the intermediate files so that the next build can see whether the
|
||||
# files in DOC_BUILD are up to date.
|
||||
#
|
||||
.PRECIOUS: \
|
||||
$(DOC_FILES:%.restdown=docs/%.html) \
|
||||
$(DOC_FILES:%.restdown=docs/%json)
|
||||
|
||||
#
|
||||
# We do clean those intermediate files, as well as all of DOC_BUILD.
|
||||
#
|
||||
CLEAN_FILES += \
|
||||
$(DOC_BUILD) \
|
||||
$(DOC_FILES:%.restdown=docs/%.html) \
|
||||
$(DOC_FILES:%.restdown=docs/%.json)
|
||||
|
||||
#
|
||||
# Before installing the files, we must make sure the directories exist. The |
|
||||
# syntax tells make that the dependency need only exist, not be up to date.
|
||||
# Otherwise, it might try to rebuild spuriously because the directory itself
|
||||
# appears out of date.
|
||||
#
|
||||
$(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD)
|
||||
|
||||
$(DOC_BUILD)/%: docs/% | $(DOC_BUILD)
|
||||
$(CP) $< $@
|
||||
|
||||
docs/%.json docs/%.html: docs/%.restdown | $(DOC_BUILD) $(RESTDOWN_EXEC)
|
||||
$(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $<
|
||||
|
||||
$(DOC_BUILD):
|
||||
$(MKDIR) $@
|
||||
|
||||
$(DOC_MEDIA_DIRS_BUILD):
|
||||
$(MKDIR) $@
|
||||
|
||||
#
|
||||
# The default "test" target does nothing. This should usually be overridden by
|
||||
# the parent Makefile. It's included here so we can define "prepush" without
|
||||
# requiring the repo to define "test".
|
||||
#
|
||||
.PHONY: test
|
||||
test:
|
||||
|
||||
.PHONY: prepush
|
||||
prepush: check test
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
# verror: richer JavaScript errors
|
||||
|
||||
This module provides two classes:
|
||||
|
||||
* VError, for combining errors while preserving each one's error message, and
|
||||
* WError, for wrapping errors.
|
||||
|
||||
Both support printf-style error messages using
|
||||
[extsprintf](https://github.com/davepacheco/node-extsprintf).
|
||||
|
||||
## printf-style Error constructor
|
||||
|
||||
At the most basic level, VError is just like JavaScript's Error class, but with
|
||||
printf-style arguments:
|
||||
|
||||
```javascript
|
||||
var VError = require('verror');
|
||||
|
||||
var filename = '/etc/passwd';
|
||||
var err = new VError('missing file: "%s"', filename);
|
||||
console.log(err.message);
|
||||
```
|
||||
|
||||
This prints:
|
||||
|
||||
missing file: "/etc/passwd"
|
||||
|
||||
`err.stack` works the same as for built-in errors:
|
||||
|
||||
```javascript
|
||||
console.log(err.stack);
|
||||
```
|
||||
|
||||
This prints:
|
||||
|
||||
missing file: "/etc/passwd"
|
||||
at Object.<anonymous> (/Users/dap/node-verror/examples/varargs.js:4:11)
|
||||
at Module._compile (module.js:449:26)
|
||||
at Object.Module._extensions..js (module.js:467:10)
|
||||
at Module.load (module.js:356:32)
|
||||
at Function.Module._load (module.js:312:12)
|
||||
at Module.runMain (module.js:492:10)
|
||||
at process.startup.processNextTick.process._tickCallback (node.js:244:9)
|
||||
|
||||
|
||||
## Causes
|
||||
|
||||
You can also pass a `cause` argument, which is another Error. For example:
|
||||
|
||||
```javascript
|
||||
var fs = require('fs');
|
||||
var VError = require('verror');
|
||||
|
||||
var filename = '/nonexistent';
|
||||
fs.stat(filename, function (err1) {
|
||||
var err2 = new VError(err1, 'stat "%s" failed', filename);
|
||||
console.error(err2.message);
|
||||
});
|
||||
```
|
||||
|
||||
This prints out:
|
||||
|
||||
stat "/nonexistent" failed: ENOENT, stat '/nonexistent'
|
||||
|
||||
which resembles how Unix programs typically report errors:
|
||||
|
||||
$ sort /nonexistent
|
||||
sort: open failed: /nonexistent: No such file or directory
|
||||
|
||||
To match the Unixy feel, just prepend the program's name to the VError's
|
||||
`message`.
|
||||
|
||||
You can also get the next-level Error using `err.cause()`:
|
||||
|
||||
```javascript
|
||||
console.error(err2.cause().message);
|
||||
```
|
||||
|
||||
prints:
|
||||
|
||||
ENOENT, stat '/nonexistent'
|
||||
|
||||
Of course, you can nest these as many times as you want:
|
||||
|
||||
```javascript
|
||||
var VError = require('verror');
|
||||
var err1 = new Error('No such file or directory');
|
||||
var err2 = new VError(err1, 'failed to stat "%s"', '/junk');
|
||||
var err3 = new VError(err2, 'request failed');
|
||||
console.error(err3.message);
|
||||
```
|
||||
|
||||
This prints:
|
||||
|
||||
request failed: failed to stat "/junk": No such file or directory
|
||||
|
||||
The idea is that each layer in the stack annotates the error with a description
|
||||
of what it was doing (with a printf-like format string) and the result is a
|
||||
message that explains what happened at every level.
|
||||
|
||||
|
||||
## WError: wrap layered errors
|
||||
|
||||
Sometimes you don't want an Error's "message" field to include the details of
|
||||
all of the low-level errors, but you still want to be able to get at them
|
||||
programmatically. For example, in an HTTP server, you probably don't want to
|
||||
spew all of the low-level errors back to the client, but you do want to include
|
||||
them in the audit log entry for the request. In that case, you can use a
|
||||
WError, which is created exactly like VError (and also supports both
|
||||
printf-style arguments and an optional cause), but the resulting "message" only
|
||||
contains the top-level error. It's also more verbose, including the class
|
||||
associated with each error in the cause chain. Using the same example above,
|
||||
but replacing `err3`'s VError with WError, we get this output:
|
||||
|
||||
request failed
|
||||
|
||||
That's what we wanted -- just a high-level summary for the client. But we can
|
||||
get the object's toString() for the full details:
|
||||
|
||||
WError: request failed; caused by WError: failed to stat "/nonexistent";
|
||||
caused by Error: No such file or directory
|
||||
|
||||
# Contributing
|
||||
|
||||
Contributions welcome. Code should be "make check" clean. To run "make check",
|
||||
you'll need these tools:
|
||||
|
||||
* https://github.com/davepacheco/jsstyle
|
||||
* https://github.com/davepacheco/javascriptlint
|
||||
|
||||
If you're changing something non-trivial or user-facing, you may want to submit
|
||||
an issue first.
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
var extsprintf = require('extsprintf');
|
||||
var fs = require('fs');
|
||||
var VError = require('../lib/verror');
|
||||
|
||||
function checkFile(filename, callback) {
|
||||
fs.stat(filename, function (err) {
|
||||
if (err)
|
||||
/* Annotate the "stat" error with what we were doing. */
|
||||
return (callback(new VError(err,
|
||||
'failed to check "%s"', filename)));
|
||||
|
||||
/* ... */
|
||||
return (callback());
|
||||
});
|
||||
}
|
||||
|
||||
function handleRequest(filename, callback) {
|
||||
checkFile('/nonexistent', function (err) {
|
||||
if (err)
|
||||
/* Annotate the "checkFile" error. */
|
||||
return (callback(new VError(
|
||||
err, 'request failed')));
|
||||
|
||||
/* ... */
|
||||
return (callback());
|
||||
});
|
||||
}
|
||||
|
||||
handleRequest('/nonexistent', function (err) {
|
||||
if (err) {
|
||||
console.log(err.message);
|
||||
console.log(extsprintf.sprintf('%r', err));
|
||||
}
|
||||
|
||||
/* ... */
|
||||
});
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
var extsprintf = require('extsprintf');
|
||||
var fs = require('fs');
|
||||
var VError = require('../lib/verror');
|
||||
|
||||
function checkFile(filename, callback) {
|
||||
fs.stat(filename, function (err) {
|
||||
if (err)
|
||||
/* Annotate the "stat" error with what we were doing. */
|
||||
return (callback(new VError(err,
|
||||
'failed to check "%s"', filename)));
|
||||
|
||||
/* ... */
|
||||
return (callback());
|
||||
});
|
||||
}
|
||||
|
||||
function handleRequest(filename, callback) {
|
||||
checkFile('/nonexistent', function (err) {
|
||||
if (err)
|
||||
/* Wrap the "checkFile" error. */
|
||||
return (callback(new VError.WError(
|
||||
err, 'request failed')));
|
||||
|
||||
/* ... */
|
||||
return (callback());
|
||||
});
|
||||
}
|
||||
|
||||
handleRequest('/nonexistent', function (err) {
|
||||
if (err) {
|
||||
console.log(err.message);
|
||||
console.log(err.toString());
|
||||
}
|
||||
});
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
var VError = require('../lib/verror');
|
||||
var err1 = new Error('No such file or directory');
|
||||
var err2 = new VError(err1, 'failed to stat "%s"', '/junk');
|
||||
var err3 = new VError(err2, 'request failed');
|
||||
console.error(err3.message);
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
var VError = require('../lib/verror');
|
||||
|
||||
var opname = 'read';
|
||||
var err = new VError('"%s" operation failed', opname);
|
||||
console.log(err.message);
|
||||
console.log(err.stack);
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
var fs = require('fs');
|
||||
var VError = require('../lib/verror');
|
||||
|
||||
var filename = '/nonexistent';
|
||||
fs.stat(filename, function (err1) {
|
||||
var err2 = new VError(err1, 'stat "%s" failed', filename);
|
||||
console.error(err2.message);
|
||||
console.error(err2.cause().message);
|
||||
});
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
var mod_fs = require('fs');
|
||||
var mod_verror = require('../lib/verror');
|
||||
|
||||
var filename = '/nonexistent';
|
||||
|
||||
mod_fs.stat(filename, function (err1) {
|
||||
var err2 = new mod_verror.WError(err1, 'failed to stat "%s"', filename);
|
||||
|
||||
/* The following would normally be higher up the stack. */
|
||||
var err3 = new mod_verror.WError(err2, 'failed to handle request');
|
||||
console.log(err3.message);
|
||||
console.log(err3.toString());
|
||||
console.log(err3.stack);
|
||||
});
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This program attempts to determine what's possible with Node.js Error
|
||||
* objects, particularly around the properties we'd like to have:
|
||||
*
|
||||
* - "name" property can be set and read
|
||||
* - "name" property appears correct in toString()
|
||||
* - "message" property can be set and read
|
||||
* - "message" property appears correct in toString()
|
||||
* - instanceof Error is true
|
||||
* - ... even for second- and third-level subclasses
|
||||
* - instanceof is true for each parent class
|
||||
* - "stack" exists
|
||||
* - "stack" is a lazy property
|
||||
* - "stack" is correct (shows the right stack trace)
|
||||
* - other properties can be added
|
||||
*
|
||||
* In both v0.10.28 and v0.11.14-pre, the "err.name" and "err.message"
|
||||
* properties can be set both before and after fetching "err.stack", and their
|
||||
* values are always correct in err.toString().
|
||||
*
|
||||
* In v0.10.28, the values of "err.name" and "err.message" as of the *first*
|
||||
* time "err.stack" is accessed are immortalized in the "err.stack" value. As a
|
||||
* result, if you set "err.name" and "err.message" in subclass constructors, we
|
||||
* should be fine.
|
||||
*
|
||||
* In v0.11.14-pre, the initial values of "err.name" and "err.message" are
|
||||
* immortalized in the "err.stack" value, so if these are changed after
|
||||
* construction, "err.stack" will always refer to the constructor name and
|
||||
* initial message. This is unfortunate, but probably also not a big deal.
|
||||
*/
|
||||
var VError = require('../lib/verror');
|
||||
|
||||
var errorname = 'OtherName';
|
||||
var message = 'my sample error';
|
||||
var omessage = 'other error message';
|
||||
|
||||
function main()
|
||||
{
|
||||
console.log('node %s', process.version);
|
||||
runBattery(Error);
|
||||
console.log('===============================');
|
||||
runBattery(VError);
|
||||
}
|
||||
|
||||
function runBattery(klass)
|
||||
{
|
||||
var name, error;
|
||||
|
||||
name = klass.name;
|
||||
|
||||
error = new klass(message);
|
||||
printErrorInfo(name, 'simple', error);
|
||||
|
||||
error.name = errorname;
|
||||
printErrorInfo(name, 'changed "name" after fetching stack', error);
|
||||
|
||||
error = new klass(message);
|
||||
error.name = errorname;
|
||||
printErrorInfo(name, 'changed "name" before fetching stack', error);
|
||||
|
||||
error = new klass(message);
|
||||
error.message = omessage;
|
||||
printErrorInfo(name, 'changed "message" before fetching stack', error);
|
||||
|
||||
error.message = message;
|
||||
printErrorInfo(name, 'changed "message" back after fetching stack',
|
||||
error);
|
||||
|
||||
error = new klass(message);
|
||||
error.otherprop = 'otherpropvalue';
|
||||
printErrorInfo(name, 'with "otherprop" property', error);
|
||||
}
|
||||
|
||||
function printErrorInfo(classname, label, err)
|
||||
{
|
||||
console.log('------------------');
|
||||
console.log('test: %s, %s', classname, label);
|
||||
console.log('instanceof Error: %s', err instanceof Error);
|
||||
console.log('constructor: %s', err.constructor.name);
|
||||
console.log('error name: %s', err.name);
|
||||
console.log('error message: %s', err.message);
|
||||
console.log('error toString: %s', err.toString());
|
||||
console.log('has "otherprop": %s', err.hasOwnProperty('otherprop'));
|
||||
console.log('error stack: %s', err.stack);
|
||||
console.log('inspect: ', err);
|
||||
}
|
||||
|
||||
main();
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
#
|
||||
# Configuration File for JavaScript Lint
|
||||
#
|
||||
# This configuration file can be used to lint a collection of scripts, or to enable
|
||||
# or disable warnings for scripts that are linted via the command line.
|
||||
#
|
||||
|
||||
### Warnings
|
||||
# Enable or disable warnings based on requirements.
|
||||
# Use "+WarningName" to display or "-WarningName" to suppress.
|
||||
#
|
||||
+ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent
|
||||
+ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity
|
||||
+ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement
|
||||
+anon_no_return_value # anonymous function does not always return value
|
||||
+assign_to_function_call # assignment to a function call
|
||||
-block_without_braces # block statement without curly braces
|
||||
+comma_separated_stmts # multiple statements separated by commas (use semicolons?)
|
||||
+comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)
|
||||
+default_not_at_end # the default case is not at the end of the switch statement
|
||||
+dup_option_explicit # duplicate "option explicit" control comment
|
||||
+duplicate_case_in_switch # duplicate case in switch statement
|
||||
+duplicate_formal # duplicate formal argument {name}
|
||||
+empty_statement # empty statement or extra semicolon
|
||||
+identifier_hides_another # identifer {name} hides an identifier in a parent scope
|
||||
-inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement
|
||||
+incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.
|
||||
+invalid_fallthru # unexpected "fallthru" control comment
|
||||
+invalid_pass # unexpected "pass" control comment
|
||||
+jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax
|
||||
+leading_decimal_point # leading decimal point may indicate a number or an object member
|
||||
+legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax
|
||||
+meaningless_block # meaningless block; curly braces have no impact
|
||||
+mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence
|
||||
+misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma
|
||||
+missing_break # missing break statement
|
||||
+missing_break_for_last_case # missing break statement for last case in switch
|
||||
+missing_default_case # missing default case in switch statement
|
||||
+missing_option_explicit # the "option explicit" control comment is missing
|
||||
+missing_semicolon # missing semicolon
|
||||
+missing_semicolon_for_lambda # missing semicolon for lambda assignment
|
||||
+multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs
|
||||
+nested_comment # nested comment
|
||||
+no_return_value # function {name} does not always return a value
|
||||
+octal_number # leading zeros make an octal number
|
||||
+parseint_missing_radix # parseInt missing radix parameter
|
||||
+partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag
|
||||
+redeclared_var # redeclaration of {name}
|
||||
+trailing_comma_in_array # extra comma is not recommended in array initializers
|
||||
+trailing_decimal_point # trailing decimal point may indicate a number or an object member
|
||||
+undeclared_identifier # undeclared identifier: {name}
|
||||
+unreachable_code # unreachable code
|
||||
-unreferenced_argument # argument declared but never referenced: {name}
|
||||
-unreferenced_function # function is declared but never referenced: {name}
|
||||
+unreferenced_variable # variable is declared but never referenced: {name}
|
||||
+unsupported_version # JavaScript {version} is not supported
|
||||
+use_of_label # use of label
|
||||
+useless_assign # useless assignment
|
||||
+useless_comparison # useless comparison; comparing identical expressions
|
||||
-useless_quotes # the quotation marks are unnecessary
|
||||
+useless_void # use of the void type may be unnecessary (void is always undefined)
|
||||
+var_hides_arg # variable {name} hides argument
|
||||
+want_assign_or_call # expected an assignment or function call
|
||||
+with_statement # with statement hides undeclared variables; use temporary variable instead
|
||||
|
||||
|
||||
### Output format
|
||||
# Customize the format of the error message.
|
||||
# __FILE__ indicates current file path
|
||||
# __FILENAME__ indicates current file name
|
||||
# __LINE__ indicates current line
|
||||
# __COL__ indicates current column
|
||||
# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__)
|
||||
# __ERROR_NAME__ indicates error name (used in configuration file)
|
||||
# __ERROR_PREFIX__ indicates error prefix
|
||||
# __ERROR_MSG__ indicates error message
|
||||
#
|
||||
# For machine-friendly output, the output format can be prefixed with
|
||||
# "encode:". If specified, all items will be encoded with C-slashes.
|
||||
#
|
||||
# Visual Studio syntax (default):
|
||||
+output-format __FILE__(__LINE__): __ERROR__
|
||||
# Alternative syntax:
|
||||
#+output-format __FILE__:__LINE__: __ERROR__
|
||||
|
||||
|
||||
### Context
|
||||
# Show the in-line position of the error.
|
||||
# Use "+context" to display or "-context" to suppress.
|
||||
#
|
||||
+context
|
||||
|
||||
|
||||
### Control Comments
|
||||
# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for
|
||||
# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is
|
||||
# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason,
|
||||
# although legacy control comments are enabled by default for backward compatibility.
|
||||
#
|
||||
-legacy_control_comments
|
||||
|
||||
|
||||
### Defining identifiers
|
||||
# By default, "option explicit" is enabled on a per-file basis.
|
||||
# To enable this for all files, use "+always_use_option_explicit"
|
||||
-always_use_option_explicit
|
||||
|
||||
# Define certain identifiers of which the lint is not aware.
|
||||
# (Use this in conjunction with the "undeclared identifier" warning.)
|
||||
#
|
||||
# Common uses for webpages might be:
|
||||
+define __dirname
|
||||
+define clearInterval
|
||||
+define clearTimeout
|
||||
+define console
|
||||
+define exports
|
||||
+define global
|
||||
+define module
|
||||
+define process
|
||||
+define require
|
||||
+define setInterval
|
||||
+define setTimeout
|
||||
+define Buffer
|
||||
+define JSON
|
||||
+define Math
|
||||
+define __dirname
|
||||
+define __filename
|
||||
|
||||
### JavaScript Version
|
||||
# To change the default JavaScript version:
|
||||
#+default-type text/javascript;version=1.5
|
||||
#+default-type text/javascript;e4x=1
|
||||
|
||||
### Files
|
||||
# Specify which files to lint
|
||||
# Use "+recurse" to enable recursion (disabled by default).
|
||||
# To add a set of files, use "+process FileName", "+process Folder\Path\*.js",
|
||||
# or "+process Folder\Path\*.htm".
|
||||
#
|
||||
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* verror.js: richer JavaScript errors
|
||||
*/
|
||||
|
||||
var mod_assert = require('assert');
|
||||
var mod_util = require('util');
|
||||
|
||||
var mod_extsprintf = require('extsprintf');
|
||||
|
||||
/*
|
||||
* Public interface
|
||||
*/
|
||||
|
||||
/* So you can 'var VError = require('verror')' */
|
||||
module.exports = VError;
|
||||
/* For compatibility */
|
||||
VError.VError = VError;
|
||||
/* Other exported classes */
|
||||
VError.SError = SError;
|
||||
VError.WError = WError;
|
||||
VError.MultiError = MultiError;
|
||||
|
||||
/*
|
||||
* VError([cause], fmt[, arg...]): Like JavaScript's built-in Error class, but
|
||||
* supports a "cause" argument (another error) and a printf-style message. The
|
||||
* cause argument can be null or omitted entirely.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* CODE MESSAGE
|
||||
* new VError('something bad happened') "something bad happened"
|
||||
* new VError('missing file: "%s"', file) "missing file: "/etc/passwd"
|
||||
* with file = '/etc/passwd'
|
||||
* new VError(err, 'open failed') "open failed: file not found"
|
||||
* with err.message = 'file not found'
|
||||
*/
|
||||
function VError(options)
|
||||
{
|
||||
var args, obj, causedBy, ctor, tailmsg;
|
||||
|
||||
/*
|
||||
* This is a regrettable pattern, but JavaScript's built-in Error class
|
||||
* is defined to work this way, so we allow the constructor to be called
|
||||
* without "new".
|
||||
*/
|
||||
if (!(this instanceof VError)) {
|
||||
args = Array.prototype.slice.call(arguments, 0);
|
||||
obj = Object.create(VError.prototype);
|
||||
VError.apply(obj, arguments);
|
||||
return (obj);
|
||||
}
|
||||
|
||||
if (options instanceof Error || typeof (options) === 'object') {
|
||||
args = Array.prototype.slice.call(arguments, 1);
|
||||
} else {
|
||||
args = Array.prototype.slice.call(arguments, 0);
|
||||
options = undefined;
|
||||
}
|
||||
|
||||
/*
|
||||
* extsprintf (which we invoke here with our caller's arguments in order
|
||||
* to construct this Error's message) is strict in its interpretation of
|
||||
* values to be processed by the "%s" specifier. The value passed to
|
||||
* extsprintf must actually be a string or something convertible to a
|
||||
* String using .toString(). Passing other values (notably "null" and
|
||||
* "undefined") is considered a programmer error. The assumption is
|
||||
* that if you actually want to print the string "null" or "undefined",
|
||||
* then that's easy to do that when you're calling extsprintf; on the
|
||||
* other hand, if you did NOT want that (i.e., there's actually a bug
|
||||
* where the program assumes some variable is non-null and tries to
|
||||
* print it, which might happen when constructing a packet or file in
|
||||
* some specific format), then it's better to stop immediately than
|
||||
* produce bogus output.
|
||||
*
|
||||
* However, sometimes the bug is only in the code calling VError, and a
|
||||
* programmer might prefer to have the error message contain "null" or
|
||||
* "undefined" rather than have the bug in the error path crash the
|
||||
* program (making the first bug harder to identify). For that reason,
|
||||
* by default VError converts "null" or "undefined" arguments to their
|
||||
* string representations and passes those to extsprintf. Programmers
|
||||
* desiring the strict behavior can use the SError class or pass the
|
||||
* "strict" option to the VError constructor.
|
||||
*/
|
||||
if (!options || !options.strict) {
|
||||
args = args.map(function (a) {
|
||||
return (a === null ? 'null' :
|
||||
a === undefined ? 'undefined' : a);
|
||||
});
|
||||
}
|
||||
|
||||
tailmsg = args.length > 0 ?
|
||||
mod_extsprintf.sprintf.apply(null, args) : '';
|
||||
this.jse_shortmsg = tailmsg;
|
||||
this.jse_summary = tailmsg;
|
||||
|
||||
if (options) {
|
||||
causedBy = options.cause;
|
||||
|
||||
if (!causedBy || !(options.cause instanceof Error))
|
||||
causedBy = options;
|
||||
|
||||
if (causedBy && (causedBy instanceof Error)) {
|
||||
this.jse_cause = causedBy;
|
||||
this.jse_summary += ': ' + causedBy.message;
|
||||
}
|
||||
}
|
||||
|
||||
this.message = this.jse_summary;
|
||||
Error.call(this, this.jse_summary);
|
||||
|
||||
if (Error.captureStackTrace) {
|
||||
ctor = options ? options.constructorOpt : undefined;
|
||||
ctor = ctor || arguments.callee;
|
||||
Error.captureStackTrace(this, ctor);
|
||||
}
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
mod_util.inherits(VError, Error);
|
||||
VError.prototype.name = 'VError';
|
||||
|
||||
VError.prototype.toString = function ve_toString()
|
||||
{
|
||||
var str = (this.hasOwnProperty('name') && this.name ||
|
||||
this.constructor.name || this.constructor.prototype.name);
|
||||
if (this.message)
|
||||
str += ': ' + this.message;
|
||||
|
||||
return (str);
|
||||
};
|
||||
|
||||
VError.prototype.cause = function ve_cause()
|
||||
{
|
||||
return (this.jse_cause);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* SError is like VError, but stricter about types. You cannot pass "null" or
|
||||
* "undefined" as string arguments to the formatter. Since SError is only a
|
||||
* different function, not really a different class, we don't set
|
||||
* SError.prototype.name.
|
||||
*/
|
||||
function SError()
|
||||
{
|
||||
var fmtargs, opts, key, args;
|
||||
|
||||
opts = {};
|
||||
opts.constructorOpt = SError;
|
||||
|
||||
if (arguments[0] instanceof Error) {
|
||||
opts.cause = arguments[0];
|
||||
fmtargs = Array.prototype.slice.call(arguments, 1);
|
||||
} else if (typeof (arguments[0]) == 'object') {
|
||||
for (key in arguments[0])
|
||||
opts[key] = arguments[0][key];
|
||||
fmtargs = Array.prototype.slice.call(arguments, 1);
|
||||
} else {
|
||||
fmtargs = Array.prototype.slice.call(arguments, 0);
|
||||
}
|
||||
|
||||
opts.strict = true;
|
||||
args = [ opts ].concat(fmtargs);
|
||||
VError.apply(this, args);
|
||||
}
|
||||
|
||||
mod_util.inherits(SError, VError);
|
||||
|
||||
|
||||
/*
|
||||
* Represents a collection of errors for the purpose of consumers that generally
|
||||
* only deal with one error. Callers can extract the individual errors
|
||||
* contained in this object, but may also just treat it as a normal single
|
||||
* error, in which case a summary message will be printed.
|
||||
*/
|
||||
function MultiError(errors)
|
||||
{
|
||||
mod_assert.ok(errors.length > 0);
|
||||
this.ase_errors = errors;
|
||||
|
||||
VError.call(this, errors[0], 'first of %d error%s',
|
||||
errors.length, errors.length == 1 ? '' : 's');
|
||||
}
|
||||
|
||||
mod_util.inherits(MultiError, VError);
|
||||
|
||||
|
||||
/*
|
||||
* Like JavaScript's built-in Error class, but supports a "cause" argument which
|
||||
* is wrapped, not "folded in" as with VError. Accepts a printf-style message.
|
||||
* The cause argument can be null.
|
||||
*/
|
||||
function WError(options)
|
||||
{
|
||||
Error.call(this);
|
||||
|
||||
var args, cause, ctor;
|
||||
if (typeof (options) === 'object') {
|
||||
args = Array.prototype.slice.call(arguments, 1);
|
||||
} else {
|
||||
args = Array.prototype.slice.call(arguments, 0);
|
||||
options = undefined;
|
||||
}
|
||||
|
||||
if (args.length > 0) {
|
||||
this.message = mod_extsprintf.sprintf.apply(null, args);
|
||||
} else {
|
||||
this.message = '';
|
||||
}
|
||||
|
||||
if (options) {
|
||||
if (options instanceof Error) {
|
||||
cause = options;
|
||||
} else {
|
||||
cause = options.cause;
|
||||
ctor = options.constructorOpt;
|
||||
}
|
||||
}
|
||||
|
||||
Error.captureStackTrace(this, ctor || this.constructor);
|
||||
if (cause)
|
||||
this.cause(cause);
|
||||
|
||||
}
|
||||
|
||||
mod_util.inherits(WError, Error);
|
||||
WError.prototype.name = 'WError';
|
||||
|
||||
|
||||
WError.prototype.toString = function we_toString()
|
||||
{
|
||||
var str = (this.hasOwnProperty('name') && this.name ||
|
||||
this.constructor.name || this.constructor.prototype.name);
|
||||
if (this.message)
|
||||
str += ': ' + this.message;
|
||||
if (this.we_cause && this.we_cause.message)
|
||||
str += '; caused by ' + this.we_cause.toString();
|
||||
|
||||
return (str);
|
||||
};
|
||||
|
||||
WError.prototype.cause = function we_cause(c)
|
||||
{
|
||||
if (c instanceof Error)
|
||||
this.we_cause = c;
|
||||
|
||||
return (this.we_cause);
|
||||
};
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"_from": "verror@1.6.0",
|
||||
"_id": "verror@1.6.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-fROyex+swuLakEBetepuW90lLqU=",
|
||||
"_location": "/vasync/verror",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "verror@1.6.0",
|
||||
"name": "verror",
|
||||
"escapedName": "verror",
|
||||
"rawSpec": "1.6.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "1.6.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/vasync"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz",
|
||||
"_shasum": "7d13b27b1facc2e2da90405eb5ea6e5bdd252ea5",
|
||||
"_spec": "verror@1.6.0",
|
||||
"_where": "/home/erik/Documents/workspace_brackets/a1_BME_Project_Ohm/om/node_modules/vasync",
|
||||
"bugs": {
|
||||
"url": "https://github.com/davepacheco/node-verror/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"extsprintf": "1.2.0"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "richer JavaScript errors",
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"homepage": "https://github.com/davepacheco/node-verror#readme",
|
||||
"license": "MIT",
|
||||
"main": "./lib/verror.js",
|
||||
"name": "verror",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/davepacheco/node-verror.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "make test"
|
||||
},
|
||||
"version": "1.6.0"
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* tst.inherit.js: test that inheriting from VError and WError work as expected.
|
||||
*/
|
||||
|
||||
var mod_assert = require('assert');
|
||||
var mod_util = require('util');
|
||||
|
||||
var VError = require('../lib/verror');
|
||||
var WError = VError.WError;
|
||||
var err, suberr;
|
||||
|
||||
function VErrorChild()
|
||||
{
|
||||
VError.apply(this, Array.prototype.slice.call(arguments));
|
||||
}
|
||||
|
||||
mod_util.inherits(VErrorChild, VError);
|
||||
VErrorChild.prototype.name = 'VErrorChild';
|
||||
|
||||
|
||||
function WErrorChild()
|
||||
{
|
||||
WError.apply(this, Array.prototype.slice.call(arguments));
|
||||
}
|
||||
|
||||
mod_util.inherits(WErrorChild, WError);
|
||||
WErrorChild.prototype.name = 'WErrorChild';
|
||||
|
||||
|
||||
suberr = new Error('root cause');
|
||||
err = new VErrorChild(suberr, 'top');
|
||||
mod_assert.ok(err instanceof Error);
|
||||
mod_assert.ok(err instanceof VError);
|
||||
mod_assert.ok(err instanceof VErrorChild);
|
||||
mod_assert.equal(err.cause(), suberr);
|
||||
mod_assert.equal(err.message, 'top: root cause');
|
||||
mod_assert.equal(err.toString(), 'VErrorChild: top: root cause');
|
||||
mod_assert.equal(err.stack.split('\n')[0], 'VErrorChild: top: root cause');
|
||||
|
||||
suberr = new Error('root cause');
|
||||
err = new WErrorChild(suberr, 'top');
|
||||
mod_assert.ok(err instanceof Error);
|
||||
mod_assert.ok(err instanceof WError);
|
||||
mod_assert.ok(err instanceof WErrorChild);
|
||||
mod_assert.equal(err.cause(), suberr);
|
||||
mod_assert.equal(err.message, 'top');
|
||||
mod_assert.equal(err.toString(),
|
||||
'WErrorChild: top; caused by Error: root cause');
|
||||
mod_assert.equal(err.stack.split('\n')[0],
|
||||
'WErrorChild: top; caused by Error: root cause');
|
||||
|
||||
|
||||
// Test that `<Ctor>.toString()` uses the ctor name. I.e. setting
|
||||
// `<Ctor>.prototype.name` isn't necessary.
|
||||
function VErrorChildNoName() {
|
||||
VError.apply(this, Array.prototype.slice.call(arguments));
|
||||
}
|
||||
mod_util.inherits(VErrorChildNoName, VError);
|
||||
err = new VErrorChildNoName('top');
|
||||
mod_assert.equal(err.toString(), 'VErrorChildNoName: top');
|
||||
|
||||
function WErrorChildNoName() {
|
||||
WError.apply(this, Array.prototype.slice.call(arguments));
|
||||
}
|
||||
mod_util.inherits(WErrorChildNoName, WError);
|
||||
err = new WErrorChildNoName('top');
|
||||
mod_assert.equal(err.toString(), 'WErrorChildNoName: top');
|
||||
|
||||
|
||||
// Test that `<Ctor>.prototype.name` can be used for the `.toString()`
|
||||
// when the ctor is anonymous.
|
||||
var VErrorChildAnon = function () {
|
||||
VError.apply(this, Array.prototype.slice.call(arguments));
|
||||
};
|
||||
mod_util.inherits(VErrorChildAnon, VError);
|
||||
VErrorChildAnon.prototype.name = 'VErrorChildAnon';
|
||||
err = new VErrorChildAnon('top');
|
||||
mod_assert.equal(err.toString(), 'VErrorChildAnon: top');
|
||||
|
||||
var WErrorChildAnon = function () {
|
||||
WError.apply(this, Array.prototype.slice.call(arguments));
|
||||
};
|
||||
mod_util.inherits(WErrorChildAnon, WError);
|
||||
WErrorChildAnon.prototype.name = 'WErrorChildAnon';
|
||||
err = new WErrorChildAnon('top');
|
||||
mod_assert.equal(err.toString(), 'WErrorChildAnon: top');
|
||||
|
||||
|
||||
// Test get appropriate exception name in `.toString()` when reconstituting
|
||||
// an error instance a la:
|
||||
// https://github.com/mcavage/node-fast/blob/master/lib/client.js#L215
|
||||
err = new VError('top');
|
||||
err.name = 'CustomNameError';
|
||||
mod_assert.equal(err.toString(), 'CustomNameError: top');
|
||||
|
||||
err = new WError('top');
|
||||
err.name = 'CustomNameError';
|
||||
mod_assert.equal(err.toString(), 'CustomNameError: top');
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* tst.serror.js: tests basic functionality of the SError class.
|
||||
*/
|
||||
|
||||
var mod_assert = require('assert');
|
||||
var mod_verror = require('../lib/verror');
|
||||
|
||||
var VError = mod_verror.VError;
|
||||
var SError = mod_verror.SError;
|
||||
|
||||
var err, suberr, stack, nodestack;
|
||||
|
||||
/*
|
||||
* See tst.verror.js
|
||||
*/
|
||||
function cleanStack(stacktxt)
|
||||
{
|
||||
var re = new RegExp(__filename + ':\\d+:\\d+', 'gm');
|
||||
stacktxt = stacktxt.replace(re, 'tst.serror.js');
|
||||
return (stacktxt);
|
||||
}
|
||||
|
||||
nodestack = new Error().stack.split('\n').slice(2).join('\n');
|
||||
|
||||
/* basic no-args case */
|
||||
err = new SError();
|
||||
mod_assert.equal(err.name, 'VError');
|
||||
mod_assert.ok(err instanceof Error);
|
||||
mod_assert.ok(err instanceof VError);
|
||||
mod_assert.ok(err instanceof SError);
|
||||
mod_assert.equal(err.message, '');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'SError',
|
||||
' at Object.<anonymous> (tst.serror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
/* simple case */
|
||||
err = new SError('hello %d %ss', 3, 'world');
|
||||
mod_assert.equal(err.message, 'hello 3 worlds');
|
||||
|
||||
/* include a cause */
|
||||
suberr = err;
|
||||
err = new SError(suberr, 'something wrong%s', '?');
|
||||
mod_assert.equal(err.message, 'something wrong?: hello 3 worlds');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
|
||||
/* include options */
|
||||
err = new SError({
|
||||
'cause': suberr,
|
||||
'constructorOpt': arguments.callee
|
||||
}, 'something wrong?');
|
||||
mod_assert.equal(err.message, 'something wrong?: hello 3 worlds');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'SError: something wrong?: hello 3 worlds'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
/* bad arguments */
|
||||
mod_assert.throws(function () { err = new SError('foo%sbar', null); },
|
||||
/attempted to print undefined or null as a string/);
|
||||
mod_assert.throws(function () { err = new SError('foo%sbar', null); },
|
||||
/attempted to print undefined or null as a string/);
|
||||
console.log('tests passed');
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* tst.verror.js: tests basic functionality of the VError class.
|
||||
*/
|
||||
|
||||
var mod_assert = require('assert');
|
||||
var mod_verror = require('../lib/verror');
|
||||
|
||||
var VError = mod_verror.VError;
|
||||
var WError = mod_verror.WError;
|
||||
|
||||
var err, suberr, stack, substack;
|
||||
|
||||
/*
|
||||
* Remove full paths and relative line numbers from stack traces so that we can
|
||||
* compare against "known-good" output.
|
||||
*/
|
||||
function cleanStack(stacktxt)
|
||||
{
|
||||
var re = new RegExp(__filename + ':\\d+:\\d+', 'gm');
|
||||
stacktxt = stacktxt.replace(re, 'tst.verror.js');
|
||||
return (stacktxt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the generic parts of all stack traces so we can avoid hardcoding
|
||||
* Node-specific implementation details in our testing of stack traces.
|
||||
*/
|
||||
var nodestack = new Error().stack.split('\n').slice(2).join('\n');
|
||||
|
||||
/* no arguments */
|
||||
err = new VError();
|
||||
mod_assert.equal(err.name, 'VError');
|
||||
mod_assert.ok(err instanceof Error);
|
||||
mod_assert.ok(err instanceof VError);
|
||||
mod_assert.equal(err.message, '');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'VError',
|
||||
' at Object.<anonymous> (tst.verror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
/* options-argument form */
|
||||
err = new VError({});
|
||||
mod_assert.equal(err.message, '');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
|
||||
/* simple message */
|
||||
err = new VError('my error');
|
||||
mod_assert.equal(err.message, 'my error');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'VError: my error',
|
||||
' at Object.<anonymous> (tst.verror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
err = new VError({}, 'my error');
|
||||
mod_assert.equal(err.message, 'my error');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
|
||||
/* printf-style message */
|
||||
err = new VError('%s error: %3d problems', 'very bad', 15);
|
||||
mod_assert.equal(err.message, 'very bad error: 15 problems');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
|
||||
err = new VError({}, '%s error: %3d problems', 'very bad', 15);
|
||||
mod_assert.equal(err.message, 'very bad error: 15 problems');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
|
||||
/* caused by another error, with no additional message */
|
||||
suberr = new Error('root cause');
|
||||
err = new VError(suberr);
|
||||
mod_assert.equal(err.message, ': root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
|
||||
err = new VError({ 'cause': suberr });
|
||||
mod_assert.equal(err.message, ': root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
|
||||
/* caused by another error, with annotation */
|
||||
err = new VError(suberr, 'proximate cause: %d issues', 3);
|
||||
mod_assert.equal(err.message, 'proximate cause: 3 issues: root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'VError: proximate cause: 3 issues: root cause',
|
||||
' at Object.<anonymous> (tst.verror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
err = new VError({ 'cause': suberr }, 'proximate cause: %d issues', 3);
|
||||
mod_assert.equal(err.message, 'proximate cause: 3 issues: root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'VError: proximate cause: 3 issues: root cause',
|
||||
' at Object.<anonymous> (tst.verror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
/* caused by another VError, with annotation. */
|
||||
suberr = err;
|
||||
err = new VError(suberr, 'top');
|
||||
mod_assert.equal(err.message, 'top: proximate cause: 3 issues: root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
|
||||
err = new VError({ 'cause': suberr }, 'top');
|
||||
mod_assert.equal(err.message, 'top: proximate cause: 3 issues: root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
|
||||
/* caused by a WError */
|
||||
suberr = new WError(new Error('root cause'), 'mid');
|
||||
err = new VError(suberr, 'top');
|
||||
mod_assert.equal(err.message, 'top: mid');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
|
||||
/* null cause (for backwards compatibility with older versions) */
|
||||
err = new VError(null, 'my error');
|
||||
mod_assert.equal(err.message, 'my error');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'VError: my error',
|
||||
' at Object.<anonymous> (tst.verror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
err = new VError({ 'cause': null }, 'my error');
|
||||
mod_assert.equal(err.message, 'my error');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
|
||||
err = new VError(null);
|
||||
mod_assert.equal(err.message, '');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'VError',
|
||||
' at Object.<anonymous> (tst.verror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
/* constructorOpt */
|
||||
function makeErr(options) {
|
||||
return (new VError(options, 'test error'));
|
||||
}
|
||||
err = makeErr({});
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'VError: test error',
|
||||
' at makeErr (tst.verror.js)',
|
||||
' at Object.<anonymous> (tst.verror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
err = makeErr({ 'constructorOpt': makeErr });
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'VError: test error',
|
||||
' at Object.<anonymous> (tst.verror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
/* "null" or "undefined" as string for extsprintf */
|
||||
err = new VError('my %s string', null);
|
||||
mod_assert.equal('my null string', err.message);
|
||||
err = new VError('my %s string', undefined);
|
||||
mod_assert.equal('my undefined string', err.message);
|
||||
|
||||
/* invoked without "new" */
|
||||
err = VError('my %s string', 'testing!');
|
||||
mod_assert.equal(err.name, 'VError');
|
||||
mod_assert.ok(err instanceof VError);
|
||||
mod_assert.ok(err instanceof Error);
|
||||
mod_assert.equal(err.message, 'my testing! string');
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* tst.werror.js: tests basic functionality of the WError class.
|
||||
*/
|
||||
|
||||
var mod_assert = require('assert');
|
||||
var mod_verror = require('../lib/verror');
|
||||
|
||||
var VError = mod_verror.VError;
|
||||
var WError = mod_verror.WError;
|
||||
|
||||
var err, suberr, stack, substack;
|
||||
|
||||
/*
|
||||
* Remove full paths and relative line numbers from stack traces so that we can
|
||||
* compare against "known-good" output.
|
||||
*/
|
||||
function cleanStack(stacktxt)
|
||||
{
|
||||
var re = new RegExp(__filename + ':\\d+:\\d+', 'gm');
|
||||
stacktxt = stacktxt.replace(re, 'tst.werror.js');
|
||||
return (stacktxt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the generic parts of all stack traces so we can avoid hardcoding
|
||||
* Node-specific implementation details in our testing of stack traces.
|
||||
*/
|
||||
var nodestack = new Error().stack.split('\n').slice(2).join('\n');
|
||||
|
||||
/* no arguments */
|
||||
err = new WError();
|
||||
mod_assert.equal(err.name, 'WError');
|
||||
mod_assert.ok(err instanceof Error);
|
||||
mod_assert.ok(err instanceof WError);
|
||||
mod_assert.equal(err.message, '');
|
||||
mod_assert.equal(err.toString(), 'WError');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'WError',
|
||||
' at Object.<anonymous> (tst.werror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
/* options-argument form */
|
||||
err = new WError({});
|
||||
mod_assert.equal(err.message, '');
|
||||
mod_assert.equal(err.toString(), 'WError');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
|
||||
/* simple message */
|
||||
err = new WError('my error');
|
||||
mod_assert.equal(err.message, 'my error');
|
||||
mod_assert.equal(err.toString(), 'WError: my error');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'WError: my error',
|
||||
' at Object.<anonymous> (tst.werror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
err = new WError({}, 'my error');
|
||||
mod_assert.equal(err.message, 'my error');
|
||||
mod_assert.equal(err.toString(), 'WError: my error');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
|
||||
/* printf-style message */
|
||||
err = new WError('%s error: %3d problems', 'very bad', 15);
|
||||
mod_assert.equal(err.message, 'very bad error: 15 problems');
|
||||
mod_assert.equal(err.toString(), 'WError: very bad error: 15 problems');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
|
||||
err = new WError({}, '%s error: %3d problems', 'very bad', 15);
|
||||
mod_assert.equal(err.message, 'very bad error: 15 problems');
|
||||
mod_assert.equal(err.toString(), 'WError: very bad error: 15 problems');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
|
||||
/* caused by another error, with no additional message */
|
||||
suberr = new Error('root cause');
|
||||
err = new WError(suberr);
|
||||
mod_assert.equal(err.message, '');
|
||||
mod_assert.equal(err.toString(), 'WError; caused by Error: root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
|
||||
err = new WError({ 'cause': suberr });
|
||||
mod_assert.equal(err.message, '');
|
||||
mod_assert.equal(err.toString(), 'WError; caused by Error: root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
|
||||
/* caused by another error, with annotation */
|
||||
err = new WError(suberr, 'proximate cause: %d issues', 3);
|
||||
mod_assert.equal(err.message, 'proximate cause: 3 issues');
|
||||
mod_assert.equal(err.toString(), 'WError: proximate cause: 3 issues; ' +
|
||||
'caused by Error: root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'WError: proximate cause: 3 issues; caused by Error: root cause',
|
||||
' at Object.<anonymous> (tst.werror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
err = new WError({ 'cause': suberr }, 'proximate cause: %d issues', 3);
|
||||
mod_assert.equal(err.message, 'proximate cause: 3 issues');
|
||||
mod_assert.equal(err.toString(), 'WError: proximate cause: 3 issues; ' +
|
||||
'caused by Error: root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'WError: proximate cause: 3 issues; caused by Error: root cause',
|
||||
' at Object.<anonymous> (tst.werror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
/* caused by another WError, with annotation. */
|
||||
suberr = err;
|
||||
err = new WError(suberr, 'top');
|
||||
mod_assert.equal(err.message, 'top');
|
||||
mod_assert.equal(err.toString(), 'WError: top; caused by WError: ' +
|
||||
'proximate cause: 3 issues; caused by Error: root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
|
||||
err = new WError({ 'cause': suberr }, 'top');
|
||||
mod_assert.equal(err.message, 'top');
|
||||
mod_assert.equal(err.toString(), 'WError: top; caused by WError: ' +
|
||||
'proximate cause: 3 issues; caused by Error: root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
|
||||
/* caused by a VError */
|
||||
suberr = new VError(new Error('root cause'), 'mid');
|
||||
err = new WError(suberr, 'top');
|
||||
mod_assert.equal(err.message, 'top');
|
||||
mod_assert.equal(err.toString(),
|
||||
'WError: top; caused by VError: mid: root cause');
|
||||
mod_assert.ok(err.cause() === suberr);
|
||||
|
||||
/* null cause (for backwards compatibility with older versions) */
|
||||
err = new WError(null, 'my error');
|
||||
mod_assert.equal(err.message, 'my error');
|
||||
mod_assert.equal(err.toString(), 'WError: my error');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'WError: my error',
|
||||
' at Object.<anonymous> (tst.werror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
err = new WError({ 'cause': null }, 'my error');
|
||||
mod_assert.equal(err.message, 'my error');
|
||||
mod_assert.equal(err.toString(), 'WError: my error');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
|
||||
err = new WError(null);
|
||||
mod_assert.equal(err.message, '');
|
||||
mod_assert.equal(err.toString(), 'WError');
|
||||
mod_assert.ok(err.cause() === undefined);
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'WError',
|
||||
' at Object.<anonymous> (tst.werror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
/* constructorOpt */
|
||||
function makeErr(options) {
|
||||
return (new WError(options, 'test error'));
|
||||
}
|
||||
err = makeErr({});
|
||||
mod_assert.equal(err.toString(), 'WError: test error');
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'WError: test error',
|
||||
' at makeErr (tst.werror.js)',
|
||||
' at Object.<anonymous> (tst.werror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
|
||||
err = makeErr({ 'constructorOpt': makeErr });
|
||||
mod_assert.equal(err.toString(), 'WError: test error');
|
||||
stack = cleanStack(err.stack);
|
||||
mod_assert.equal(stack, [
|
||||
'WError: test error',
|
||||
' at Object.<anonymous> (tst.werror.js)'
|
||||
].join('\n') + '\n' + nodestack);
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"_from": "vasync@^1.6.4",
|
||||
"_id": "vasync@1.6.4",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8=",
|
||||
"_location": "/vasync",
|
||||
"_phantomChildren": {
|
||||
"extsprintf": "1.2.0"
|
||||
},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "vasync@^1.6.4",
|
||||
"name": "vasync",
|
||||
"escapedName": "vasync",
|
||||
"rawSpec": "^1.6.4",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "^1.6.4"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/ldapjs"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz",
|
||||
"_shasum": "dfe93616ad0e7ae801b332a9d88bfc5cdc8e1d1f",
|
||||
"_spec": "vasync@^1.6.4",
|
||||
"_where": "/home/erik/Documents/workspace_brackets/a1_BME_Project_Ohm/om/node_modules/ldapjs",
|
||||
"bugs": {
|
||||
"url": "https://github.com/davepacheco/node-vasync/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"verror": "1.6.0"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "utilities for observable asynchronous control flow",
|
||||
"devDependencies": {
|
||||
"nodeunit": "0.8.7",
|
||||
"tap": "~0.4.8"
|
||||
},
|
||||
"engines": [
|
||||
"node >=0.6.0"
|
||||
],
|
||||
"homepage": "https://github.com/davepacheco/node-vasync#readme",
|
||||
"license": "MIT",
|
||||
"main": "./lib/vasync.js",
|
||||
"name": "vasync",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/davepacheco/node-vasync.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tap tests/ && ./node_modules/.bin/nodeunit tests/compat.js"
|
||||
},
|
||||
"version": "1.6.4"
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* tests/compat.js: Some of the APIs provided by vasync are intended to be
|
||||
* API-compatible with node-async, so we incorporate the tests from node-async
|
||||
* directly here. These are copied from https://github.com/caolan/async,
|
||||
* available under the MIT license. To make it easy to update this from the
|
||||
* source, this file should remain unchanged from the source except for this
|
||||
* header comment, the change to the "require" line, and deleted lines for
|
||||
* unimplemented functions.
|
||||
*
|
||||
* The following tests are deliberately omitted:
|
||||
*
|
||||
* o "waterfall non-array": Per Joyent's Best Practices for Node.js Error
|
||||
* Handling, we're strict about argument types and throw on these programmer
|
||||
* errors rather than emitting them asynchronously.
|
||||
*
|
||||
* o "waterfall multiple callback calls": We deliberately disallow a waterfall
|
||||
* function to invoke its callback more than once, so we don't test for that
|
||||
* here. The behavior that node-async allows can potentially be used to fork
|
||||
* the waterfall, which may be useful, but it's often used instead as an
|
||||
* excuse to write code sloppily. And the downside is that it makes it really
|
||||
* hard to understand bugs where the waterfall was resumed too early. For
|
||||
* now, we're disallowing it, but if the forking behavior becomes useful, we
|
||||
* can always make our version less strict.
|
||||
*/
|
||||
var async = require('../lib/vasync');
|
||||
|
||||
exports['waterfall'] = function(test){
|
||||
test.expect(6);
|
||||
var call_order = [];
|
||||
async.waterfall([
|
||||
function(callback){
|
||||
call_order.push('fn1');
|
||||
setTimeout(function(){callback(null, 'one', 'two');}, 0);
|
||||
},
|
||||
function(arg1, arg2, callback){
|
||||
call_order.push('fn2');
|
||||
test.equals(arg1, 'one');
|
||||
test.equals(arg2, 'two');
|
||||
setTimeout(function(){callback(null, arg1, arg2, 'three');}, 25);
|
||||
},
|
||||
function(arg1, arg2, arg3, callback){
|
||||
call_order.push('fn3');
|
||||
test.equals(arg1, 'one');
|
||||
test.equals(arg2, 'two');
|
||||
test.equals(arg3, 'three');
|
||||
callback(null, 'four');
|
||||
},
|
||||
function(arg4, callback){
|
||||
call_order.push('fn4');
|
||||
test.same(call_order, ['fn1','fn2','fn3','fn4']);
|
||||
callback(null, 'test');
|
||||
}
|
||||
], function(err){
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
exports['waterfall empty array'] = function(test){
|
||||
async.waterfall([], function(err){
|
||||
test.done();
|
||||
});
|
||||
};
|
||||
|
||||
exports['waterfall no callback'] = function(test){
|
||||
async.waterfall([
|
||||
function(callback){callback();},
|
||||
function(callback){callback(); test.done();}
|
||||
]);
|
||||
};
|
||||
|
||||
exports['waterfall async'] = function(test){
|
||||
var call_order = [];
|
||||
async.waterfall([
|
||||
function(callback){
|
||||
call_order.push(1);
|
||||
callback();
|
||||
call_order.push(2);
|
||||
},
|
||||
function(callback){
|
||||
call_order.push(3);
|
||||
callback();
|
||||
},
|
||||
function(){
|
||||
test.same(call_order, [1,2,3]);
|
||||
test.done();
|
||||
}
|
||||
]);
|
||||
};
|
||||
|
||||
exports['waterfall error'] = function(test){
|
||||
test.expect(1);
|
||||
async.waterfall([
|
||||
function(callback){
|
||||
callback('error');
|
||||
},
|
||||
function(callback){
|
||||
test.ok(false, 'next function should not be called');
|
||||
callback();
|
||||
}
|
||||
], function(err){
|
||||
test.equals(err, 'error');
|
||||
});
|
||||
setTimeout(test.done, 50);
|
||||
};
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Tests that if the user modifies the list of functions passed to
|
||||
* vasync.pipeline, vasync ignores the changes and does not crash.
|
||||
*/
|
||||
var assert = require('assert');
|
||||
var vasync = require('../lib/vasync');
|
||||
var count = 0;
|
||||
var funcs;
|
||||
|
||||
function doStuff(_, callback)
|
||||
{
|
||||
count++;
|
||||
setImmediate(callback);
|
||||
}
|
||||
|
||||
funcs = [ doStuff, doStuff, doStuff ];
|
||||
|
||||
vasync.pipeline({
|
||||
'funcs': funcs
|
||||
}, function (err) {
|
||||
assert.ok(!err);
|
||||
assert.ok(count === 3);
|
||||
});
|
||||
|
||||
funcs.push(doStuff);
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
/* vim: set ts=8 sts=8 sw=8 noet: */
|
||||
|
||||
var mod_tap = require('tap');
|
||||
var mod_vasync = require('..');
|
||||
|
||||
function
|
||||
immediate_worker(task, next)
|
||||
{
|
||||
setImmediate(function () {
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
function
|
||||
sametick_worker(task, next)
|
||||
{
|
||||
next();
|
||||
}
|
||||
|
||||
function
|
||||
random_delay_worker(task, next)
|
||||
{
|
||||
setTimeout(function () {
|
||||
next();
|
||||
}, Math.floor(Math.random() * 250));
|
||||
}
|
||||
|
||||
mod_tap.test('must not push after close', function (test) {
|
||||
test.plan(3);
|
||||
|
||||
var q = mod_vasync.queuev({
|
||||
worker: immediate_worker,
|
||||
concurrency: 10
|
||||
});
|
||||
test.ok(q);
|
||||
|
||||
test.doesNotThrow(function () {
|
||||
q.push({});
|
||||
}, 'push should not throw _before_ close()');
|
||||
|
||||
q.close();
|
||||
|
||||
/*
|
||||
* If we attempt to add tasks to the queue _after_ calling close(),
|
||||
* we should get an exception:
|
||||
*/
|
||||
test.throws(function () {
|
||||
q.push({});
|
||||
}, 'push should throw _after_ close()');
|
||||
|
||||
test.end();
|
||||
});
|
||||
|
||||
mod_tap.test('get \'end\' event with close()', function (test) {
|
||||
var task_count = 45;
|
||||
var tasks_finished = 0;
|
||||
var seen_end = false;
|
||||
var seen_drain = false;
|
||||
|
||||
test.plan(14 + task_count);
|
||||
|
||||
var q = mod_vasync.queuev({
|
||||
worker: random_delay_worker,
|
||||
concurrency: 5
|
||||
});
|
||||
test.ok(q);
|
||||
|
||||
/*
|
||||
* Enqueue a bunch of tasks; more than our concurrency:
|
||||
*/
|
||||
for (var i = 0; i < 45; i++) {
|
||||
q.push({}, function () {
|
||||
tasks_finished++;
|
||||
test.ok(true);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the queue to signify that we're done now.
|
||||
*/
|
||||
test.equal(q.ended, false);
|
||||
test.equal(q.closed, false);
|
||||
q.close();
|
||||
test.equal(q.closed, true);
|
||||
test.equal(q.ended, false);
|
||||
|
||||
q.on('drain', function () {
|
||||
/*
|
||||
* 'drain' should fire before 'end':
|
||||
*/
|
||||
test.notOk(seen_drain);
|
||||
test.notOk(seen_end);
|
||||
seen_drain = true;
|
||||
});
|
||||
q.on('end', function () {
|
||||
/*
|
||||
* 'end' should fire after 'drain':
|
||||
*/
|
||||
test.ok(seen_drain);
|
||||
test.notOk(seen_end);
|
||||
seen_end = true;
|
||||
|
||||
/*
|
||||
* Check the public state:
|
||||
*/
|
||||
test.equal(q.closed, true);
|
||||
test.equal(q.ended, true);
|
||||
|
||||
/*
|
||||
* We should have fired the callbacks for _all_ enqueued
|
||||
* tasks by now:
|
||||
*/
|
||||
test.equal(task_count, tasks_finished);
|
||||
test.end();
|
||||
});
|
||||
|
||||
/*
|
||||
* Check that we see neither the 'drain', nor the 'end' event before
|
||||
* the end of this tick:
|
||||
*/
|
||||
test.notOk(seen_drain);
|
||||
test.notOk(seen_end);
|
||||
});
|
||||
|
||||
mod_tap.test('get \'end\' event with close() and no tasks', function (test) {
|
||||
var seen_drain = false;
|
||||
var seen_end = false;
|
||||
|
||||
test.plan(10);
|
||||
|
||||
var q = mod_vasync.queuev({
|
||||
worker: immediate_worker,
|
||||
concurrency: 10
|
||||
});
|
||||
|
||||
setImmediate(function () {
|
||||
test.notOk(seen_end);
|
||||
});
|
||||
|
||||
test.equal(q.ended, false);
|
||||
test.equal(q.closed, false);
|
||||
q.close();
|
||||
test.equal(q.closed, true);
|
||||
test.equal(q.ended, false);
|
||||
test.notOk(seen_end);
|
||||
|
||||
q.on('drain', function () {
|
||||
seen_drain = true;
|
||||
});
|
||||
q.on('end', function () {
|
||||
/*
|
||||
* We do not expect to see a 'drain' event, as there were no
|
||||
* tasks pushed onto the queue before we closed it.
|
||||
*/
|
||||
test.notOk(seen_drain);
|
||||
test.notOk(seen_end);
|
||||
test.equal(q.closed, true);
|
||||
test.equal(q.ended, true);
|
||||
seen_end = true;
|
||||
test.end();
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* We want to ensure that both the 'drain' event and the q.drain() hook are
|
||||
* called the same number of times:
|
||||
*/
|
||||
mod_tap.test('equivalence of on(\'drain\') and q.drain()', function (test) {
|
||||
var enqcount = 4;
|
||||
var drains = 4;
|
||||
var ee_count = 0;
|
||||
var fn_count = 0;
|
||||
|
||||
test.plan(enqcount + drains + 3);
|
||||
|
||||
var q = mod_vasync.queuev({
|
||||
worker: immediate_worker,
|
||||
concurrency: 10
|
||||
});
|
||||
|
||||
var enq = function () {
|
||||
if (--enqcount < 0)
|
||||
return;
|
||||
|
||||
q.push({}, function () {
|
||||
test.ok(true, 'task completion');
|
||||
});
|
||||
};
|
||||
|
||||
var draino = function () {
|
||||
test.ok(true, 'drain called');
|
||||
if (--drains === 0) {
|
||||
test.equal(q.closed, false, 'not closed');
|
||||
test.equal(q.ended, false, 'not ended');
|
||||
test.equal(fn_count, ee_count, 'same number of calls');
|
||||
test.end();
|
||||
}
|
||||
};
|
||||
|
||||
enq();
|
||||
enq();
|
||||
|
||||
q.on('drain', function () {
|
||||
ee_count++;
|
||||
enq();
|
||||
draino();
|
||||
});
|
||||
q.drain = function () {
|
||||
fn_count++;
|
||||
enq();
|
||||
draino();
|
||||
};
|
||||
});
|
||||
|
||||
/*
|
||||
* In the past, we've only handed on the _first_ argument to the task completion
|
||||
* callback. Make sure we hand on _all_ of the arguments now:
|
||||
*/
|
||||
mod_tap.test('ensure all arguments passed to push() callback', function (test) {
|
||||
test.plan(13);
|
||||
|
||||
var q = mod_vasync.queuev({
|
||||
worker: function (task, callback) {
|
||||
if (task.fail) {
|
||||
callback(new Error('guru meditation'));
|
||||
return;
|
||||
}
|
||||
callback(null, 1, 2, 3, 5, 8);
|
||||
},
|
||||
concurrency: 1
|
||||
});
|
||||
|
||||
q.push({ fail: true }, function (err, a, b, c, d, e) {
|
||||
test.ok(err, 'got the error');
|
||||
test.equal(err.message, 'guru meditation');
|
||||
test.type(a, 'undefined');
|
||||
test.type(b, 'undefined');
|
||||
test.type(c, 'undefined');
|
||||
test.type(d, 'undefined');
|
||||
test.type(e, 'undefined');
|
||||
});
|
||||
|
||||
q.push({ fail: false }, function (err, a, b, c, d, e) {
|
||||
test.notOk(err, 'got no error');
|
||||
test.equal(a, 1);
|
||||
test.equal(b, 2);
|
||||
test.equal(c, 3);
|
||||
test.equal(d, 5);
|
||||
test.equal(e, 8);
|
||||
});
|
||||
|
||||
q.drain = function () {
|
||||
test.end();
|
||||
};
|
||||
});
|
||||
|
||||
mod_tap.test('queue kill', function (test) {
|
||||
// Derived from async queue.kill test
|
||||
var count = 0;
|
||||
var q = mod_vasync.queuev({
|
||||
worker: function (task, callback) {
|
||||
setImmediate(function () {
|
||||
test.ok(++count < 2,
|
||||
'Function should be called once');
|
||||
callback();
|
||||
});
|
||||
},
|
||||
concurrency: 1
|
||||
});
|
||||
q.drain = function () {
|
||||
test.ok(false, 'Function should never be called');
|
||||
};
|
||||
|
||||
// Queue twice, the first will exec immediately
|
||||
q.push(0);
|
||||
q.push(0);
|
||||
|
||||
q.kill();
|
||||
|
||||
q.on('end', function () {
|
||||
test.ok(q.killed);
|
||||
test.end();
|
||||
});
|
||||
});
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
/* vim: set ts=8 sts=8 sw=8 noet: */
|
||||
|
||||
var mod_tap = require('tap');
|
||||
var mod_vasync = require('..');
|
||||
|
||||
function
|
||||
latched_worker(task, cb)
|
||||
{
|
||||
if (task.immediate) {
|
||||
cb();
|
||||
} else {
|
||||
task.latched = true;
|
||||
task.unlatch = function () {
|
||||
task.latched = false;
|
||||
cb();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function
|
||||
unlatchAll(tasks)
|
||||
{
|
||||
tasks.forEach(function (t) {
|
||||
if (t.latched) {
|
||||
t.unlatch();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function
|
||||
setAllImmediate(tasks)
|
||||
{
|
||||
tasks.forEach(function (t) {
|
||||
t.immediate = true;
|
||||
});
|
||||
}
|
||||
|
||||
mod_tap.test('test serial tasks', function (test) {
|
||||
test.plan(2);
|
||||
|
||||
var q = mod_vasync.queuev({
|
||||
worker: latched_worker,
|
||||
concurrency: 1
|
||||
});
|
||||
test.ok(q);
|
||||
|
||||
var tasks = [];
|
||||
for (var i = 0; i < 2; ++i) {
|
||||
tasks.push({
|
||||
'id': i,
|
||||
'latched': false,
|
||||
'immediate': false
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
var latched = 0;
|
||||
tasks.forEach(function (t) {
|
||||
if (t.latched) {
|
||||
++latched;
|
||||
}
|
||||
});
|
||||
test.ok(latched === 1);
|
||||
unlatchAll(tasks);
|
||||
setAllImmediate(tasks);
|
||||
}, 10);
|
||||
|
||||
q.on('drain', function () {
|
||||
q.close();
|
||||
});
|
||||
|
||||
q.on('end', function () {
|
||||
test.end();
|
||||
});
|
||||
|
||||
q.push(tasks);
|
||||
});
|
||||
|
||||
mod_tap.test('test parallel tasks', function (test) {
|
||||
test.plan(2);
|
||||
|
||||
var q = mod_vasync.queuev({
|
||||
worker: latched_worker,
|
||||
concurrency: 2
|
||||
});
|
||||
test.ok(q);
|
||||
|
||||
var tasks = [];
|
||||
for (var i = 0; i < 3; ++i) {
|
||||
tasks.push({
|
||||
'id': i,
|
||||
'latched': false,
|
||||
'immediate': false
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
var latched = 0;
|
||||
tasks.forEach(function (t) {
|
||||
if (t.latched) {
|
||||
++latched;
|
||||
}
|
||||
});
|
||||
test.ok(latched === 2);
|
||||
unlatchAll(tasks);
|
||||
setAllImmediate(tasks);
|
||||
}, 10);
|
||||
|
||||
q.on('drain', function () {
|
||||
q.close();
|
||||
});
|
||||
|
||||
q.on('end', function () {
|
||||
test.end();
|
||||
});
|
||||
|
||||
q.push(tasks);
|
||||
});
|
||||
|
||||
mod_tap.test('test ratchet up and down', function (test) {
|
||||
test.plan(8);
|
||||
|
||||
var q = mod_vasync.queuev({
|
||||
worker: latched_worker,
|
||||
concurrency: 2
|
||||
});
|
||||
test.ok(q);
|
||||
|
||||
var bounced = 0;
|
||||
var tasks = [];
|
||||
for (var i = 0; i < 21; ++i) {
|
||||
tasks.push({
|
||||
'id': i,
|
||||
'latched': false,
|
||||
'immediate': false
|
||||
});
|
||||
}
|
||||
|
||||
function count() {
|
||||
var latched = 0;
|
||||
tasks.forEach(function (t) {
|
||||
if (t.latched) {
|
||||
++latched;
|
||||
}
|
||||
});
|
||||
return (latched);
|
||||
}
|
||||
|
||||
function fiveLatch() {
|
||||
if (!q.closed) {
|
||||
++bounced;
|
||||
test.ok(count() === 5);
|
||||
q.updateConcurrency(2);
|
||||
unlatchAll(tasks);
|
||||
setTimeout(twoLatch, 10);
|
||||
}
|
||||
}
|
||||
|
||||
function twoLatch() {
|
||||
if (!q.closed) {
|
||||
++bounced;
|
||||
test.ok(count() === 2);
|
||||
q.updateConcurrency(5);
|
||||
unlatchAll(tasks);
|
||||
setTimeout(fiveLatch, 10);
|
||||
}
|
||||
}
|
||||
setTimeout(twoLatch, 10);
|
||||
|
||||
q.on('drain', function () {
|
||||
q.close();
|
||||
});
|
||||
|
||||
q.on('end', function () {
|
||||
// 21 tasks === 5 * 3 + 2 * 3 === 6 bounces
|
||||
test.ok(bounced === 6);
|
||||
test.end();
|
||||
});
|
||||
|
||||
q.push(tasks);
|
||||
});
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Tests the "waterfall" primitive.
|
||||
*/
|
||||
|
||||
var mod_tap = require('tap');
|
||||
var mod_vasync = require('..');
|
||||
|
||||
var count = 0;
|
||||
var st;
|
||||
|
||||
mod_tap.test('empty waterfall', function (test) {
|
||||
st = mod_vasync.waterfall([], function (err) {
|
||||
test.ok(err === null);
|
||||
test.ok(st.ndone === 0);
|
||||
test.ok(st.nerrors === 0);
|
||||
test.ok(st.operations.length === 0);
|
||||
test.ok(st.successes.length === 0);
|
||||
test.equal(count, 1);
|
||||
test.end();
|
||||
});
|
||||
count++;
|
||||
test.ok(st.ndone === 0);
|
||||
test.ok(st.nerrors === 0);
|
||||
test.ok(st.operations.length === 0);
|
||||
test.ok(st.successes.length === 0);
|
||||
});
|
||||
|
||||
mod_tap.test('normal 4-stage waterfall', function (test) {
|
||||
count = 0;
|
||||
st = mod_vasync.waterfall([
|
||||
function func1(cb) {
|
||||
test.ok(count === 0, 'func1: count === 0');
|
||||
test.ok(st.ndone === 0);
|
||||
count++;
|
||||
setTimeout(cb, 20, null, { 'hello': 'world' });
|
||||
},
|
||||
function func2(extra, cb) {
|
||||
test.equal(extra.hello, 'world', 'func2: extra arg');
|
||||
test.ok(count == 1, 'func2: count == 1');
|
||||
test.ok(st.ndone === 1);
|
||||
test.ok(st.operations[0].status == 'ok');
|
||||
test.ok(st.operations[1].status == 'pending');
|
||||
test.ok(st.operations[2].status == 'waiting');
|
||||
count++;
|
||||
setTimeout(cb, 20, null, 5, 6, 7);
|
||||
},
|
||||
function (five, six, seven, cb) {
|
||||
test.equal(five, 5, 'func3: extra arg');
|
||||
test.equal(six, 6, 'func3: extra arg');
|
||||
test.equal(seven, 7, 'func3: extra arg');
|
||||
test.ok(count == 2, 'func3: count == 2');
|
||||
test.ok(st.ndone === 2);
|
||||
count++;
|
||||
setTimeout(cb, 20);
|
||||
},
|
||||
function func4(cb) {
|
||||
test.ok(count == 3, 'func4: count == 2');
|
||||
test.ok(st.ndone === 3);
|
||||
count++;
|
||||
setTimeout(cb, 20, null, 8, 9);
|
||||
}
|
||||
], function (err, eight, nine) {
|
||||
test.ok(count == 4, 'final: count == 4');
|
||||
test.ok(err === null, 'no error');
|
||||
test.ok(eight == 8);
|
||||
test.ok(nine == 9);
|
||||
test.ok(st.ndone === 4);
|
||||
test.ok(st.nerrors === 0);
|
||||
test.ok(st.operations.length === 4);
|
||||
test.ok(st.successes.length === 4);
|
||||
test.ok(st.operations[0].status == 'ok');
|
||||
test.ok(st.operations[1].status == 'ok');
|
||||
test.ok(st.operations[2].status == 'ok');
|
||||
test.ok(st.operations[3].status == 'ok');
|
||||
test.end();
|
||||
});
|
||||
test.ok(st.ndone === 0);
|
||||
test.ok(st.nerrors === 0);
|
||||
test.ok(st.operations.length === 4);
|
||||
test.ok(st.operations[0].funcname == 'func1', 'func1 name');
|
||||
test.ok(st.operations[0].status == 'pending');
|
||||
test.ok(st.operations[1].funcname == 'func2', 'func2 name');
|
||||
test.ok(st.operations[1].status == 'waiting');
|
||||
test.ok(st.operations[2].funcname == '(anon)', 'anon name');
|
||||
test.ok(st.operations[2].status == 'waiting');
|
||||
test.ok(st.operations[3].funcname == 'func4', 'func4 name');
|
||||
test.ok(st.operations[3].status == 'waiting');
|
||||
test.ok(st.successes.length === 0);
|
||||
});
|
||||
|
||||
mod_tap.test('bailing out early', function (test) {
|
||||
count = 0;
|
||||
st = mod_vasync.waterfall([
|
||||
function func1(cb) {
|
||||
test.ok(count === 0, 'func1: count === 0');
|
||||
count++;
|
||||
setTimeout(cb, 20);
|
||||
},
|
||||
function func2(cb) {
|
||||
test.ok(count == 1, 'func2: count == 1');
|
||||
count++;
|
||||
setTimeout(cb, 20, new Error('boom!'));
|
||||
},
|
||||
function func3(cb) {
|
||||
test.ok(count == 2, 'func3: count == 2');
|
||||
count++;
|
||||
setTimeout(cb, 20);
|
||||
}
|
||||
], function (err) {
|
||||
test.ok(count == 2, 'final: count == 3');
|
||||
test.equal(err.message, 'boom!');
|
||||
test.ok(st.ndone == 2);
|
||||
test.ok(st.nerrors == 1);
|
||||
test.ok(st.operations[0].status == 'ok');
|
||||
test.ok(st.operations[1].status == 'fail');
|
||||
test.ok(st.operations[2].status == 'waiting');
|
||||
test.ok(st.successes.length == 1);
|
||||
test.end();
|
||||
});
|
||||
});
|
||||
|
||||
mod_tap.test('bad function', function (test) {
|
||||
count = 0;
|
||||
st = mod_vasync.waterfall([
|
||||
function func1(cb) {
|
||||
count++;
|
||||
cb();
|
||||
setTimeout(function () {
|
||||
test.throws(
|
||||
function () { cb(); process.abort(); },
|
||||
'vasync.waterfall: ' +
|
||||
'function 0 ("func1") invoked its ' +
|
||||
'callback twice');
|
||||
test.equal(count, 2);
|
||||
test.end();
|
||||
}, 100);
|
||||
},
|
||||
function func2(cb) {
|
||||
count++;
|
||||
/* do nothing -- we'll throw an exception first */
|
||||
}
|
||||
], function (err) {
|
||||
/* not reached */
|
||||
console.error('didn\'t expect to finish');
|
||||
process.abort();
|
||||
});
|
||||
});
|
||||
|
||||
mod_tap.test('badargs', function (test) {
|
||||
test.throws(function () { mod_vasync.waterfall(); });
|
||||
test.throws(function () { mod_vasync.waterfall([], 'foo'); });
|
||||
test.throws(function () { mod_vasync.waterfall('foo', 'bar'); });
|
||||
test.end();
|
||||
});
|
||||
|
||||
mod_tap.test('normal waterfall, no callback', function (test) {
|
||||
count = 0;
|
||||
st = mod_vasync.waterfall([
|
||||
function func1(cb) {
|
||||
test.ok(count === 0);
|
||||
count++;
|
||||
setImmediate(cb);
|
||||
},
|
||||
function func2(cb) {
|
||||
test.ok(count == 1);
|
||||
count++;
|
||||
setImmediate(cb);
|
||||
setTimeout(function () {
|
||||
test.ok(count == 2);
|
||||
test.end();
|
||||
}, 100);
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
mod_tap.test('empty waterfall, no callback', function (test) {
|
||||
st = mod_vasync.waterfall([]);
|
||||
setTimeout(function () { test.end(); }, 100);
|
||||
});
|
||||
Reference in New Issue
Block a user